View Javadoc
1   /*
2    * Prometheus: Application Framework
3    * Copyright 2012-2026. Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6    * use this file except in compliance with the License.  You may obtain a copy
7    * of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
14   * License for the specific language governing permissions and limitations under
15   * the License.
16   */
17  package io.github.tonywasher.joceanus.prometheus.data;
18  
19  import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
20  import io.github.tonywasher.joceanus.metis.data.MetisDataResource;
21  import io.github.tonywasher.joceanus.metis.field.MetisFieldSet;
22  import io.github.tonywasher.joceanus.metis.field.MetisFieldVersionedSet;
23  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataSet.PrometheusCryptographyDataType;
24  import io.github.tonywasher.joceanus.prometheus.exc.PrometheusDataException;
25  
26  /**
27   * ControlData definition and list. The Control Data represents the data version of the entire data
28   * set, allowing for migration code to be written to map between different versions. It also holds a
29   * pointer to the active ControlKey.
30   * <p>
31   * When code is loaded from a database, it is possible that more than one control key will be
32   * active. This will occur if a failure occurs when we are writing the results of a renew security
33   * request to the database and we have changed some records, but not all to the required controlKey.
34   * This record points to the active controlKey. All records that are not encrypted by the correct
35   * controlKey should be re-encrypted and written to the database.
36   *
37   * @author Tony Washer
38   */
39  public class PrometheusControlData
40          extends PrometheusDataItem {
41      /**
42       * Object name.
43       */
44      public static final String OBJECT_NAME = PrometheusCryptographyDataType.CONTROLDATA.getItemName();
45  
46      /**
47       * List name.
48       */
49      public static final String LIST_NAME = PrometheusCryptographyDataType.CONTROLDATA.getListName();
50  
51      /**
52       * Report fields.
53       */
54      private static final MetisFieldVersionedSet<PrometheusControlData> FIELD_DEFS = MetisFieldVersionedSet.newVersionedFieldSet(PrometheusControlData.class);
55  
56      /*
57       * FieldIds.
58       */
59      static {
60          FIELD_DEFS.declareIntegerField(PrometheusDataResource.CONTROLDATA_VERSION);
61          FIELD_DEFS.declareLinkField(PrometheusCryptographyDataType.CONTROLKEY);
62      }
63  
64      /**
65       * Error message for already exists.
66       */
67      public static final String ERROR_CTLEXISTS = PrometheusDataResource.CONTROLDATA_ERROR_EXISTS.getValue();
68  
69      /**
70       * Copy Constructor.
71       *
72       * @param pList   the associated list
73       * @param pSource The source
74       */
75      protected PrometheusControlData(final PrometheusControlDataList pList,
76                                      final PrometheusControlData pSource) {
77          /* Set standard values */
78          super(pList, pSource);
79      }
80  
81      /**
82       * Values constructor.
83       *
84       * @param pList   the List to add to
85       * @param pValues the values constructor
86       * @throws OceanusException on error
87       */
88      private PrometheusControlData(final PrometheusControlDataList pList,
89                                    final PrometheusDataValues pValues) throws OceanusException {
90          /* Initialise the item */
91          super(pList, pValues);
92  
93          /* Store the Version */
94          Object myValue = pValues.getValue(PrometheusDataResource.CONTROLDATA_VERSION);
95          if (myValue instanceof Integer i) {
96              setValueDataVersion(i);
97          } else if (myValue instanceof String s) {
98              setValueDataVersion(Integer.valueOf(s));
99          }
100 
101         /* Store the ControlKey */
102         myValue = pValues.getValue(PrometheusCryptographyDataType.CONTROLKEY);
103         if (myValue instanceof Integer myInt) {
104             /* Store value */
105             setValueControlKey(myInt);
106 
107             /* Resolve the ControlKey */
108             final PrometheusDataSet myData = getDataSet();
109             resolveDataLink(PrometheusCryptographyDataType.CONTROLKEY, myData.getControlKeys());
110         }
111     }
112 
113     @Override
114     public MetisFieldSetDef getDataFieldSet() {
115         return FIELD_DEFS;
116     }
117 
118     /**
119      * Get the data version.
120      *
121      * @return data version
122      */
123     public Integer getDataVersion() {
124         return getValues().getValue(PrometheusDataResource.CONTROLDATA_VERSION, Integer.class);
125     }
126 
127     /**
128      * Get the control key.
129      *
130      * @return the control key
131      */
132     public PrometheusControlKey getControlKey() {
133         return getValues().getValue(PrometheusCryptographyDataType.CONTROLKEY, PrometheusControlKey.class);
134     }
135 
136     /**
137      * Get the ControlKeyId for this item.
138      *
139      * @return the ControlKeyId
140      */
141     public Integer getControlKeyId() {
142         final PrometheusControlKey myKey = getControlKey();
143         return (myKey == null)
144                 ? null
145                 : myKey.getIndexedId();
146     }
147 
148     /**
149      * Set the data version value.
150      *
151      * @param pValue the value
152      * @throws OceanusException on error
153      */
154     private void setValueDataVersion(final Integer pValue) throws OceanusException {
155         getValues().setValue(PrometheusDataResource.CONTROLDATA_VERSION, pValue);
156     }
157 
158     /**
159      * Set the control key value.
160      *
161      * @param pValue the value
162      * @throws OceanusException on error
163      */
164     private void setValueControlKey(final PrometheusControlKey pValue) throws OceanusException {
165         getValues().setValue(PrometheusCryptographyDataType.CONTROLKEY, pValue);
166     }
167 
168     /**
169      * Set the control key value as Id.
170      *
171      * @param pId the value
172      * @throws OceanusException on error
173      */
174     private void setValueControlKey(final Integer pId) throws OceanusException {
175         getValues().setValue(PrometheusCryptographyDataType.CONTROLKEY, pId);
176     }
177 
178     @Override
179     public PrometheusControlData getBase() {
180         return (PrometheusControlData) super.getBase();
181     }
182 
183     @Override
184     public PrometheusControlDataList getList() {
185         return (PrometheusControlDataList) super.getList();
186     }
187 
188     @Override
189     public int compareValues(final PrometheusDataItem pThat) {
190         /* Check the versions */
191         final PrometheusControlData myThat = (PrometheusControlData) pThat;
192         return getDataVersion() - myThat.getDataVersion();
193     }
194 
195     @Override
196     public void resolveDataSetLinks() throws OceanusException {
197         /* Resolve the ControlKey */
198         final PrometheusDataSet myData = getDataSet();
199         resolveDataLink(PrometheusCryptographyDataType.CONTROLKEY, myData.getControlKeys());
200     }
201 
202     /**
203      * Set a new ControlKey.
204      *
205      * @param pControl the new control key
206      * @throws OceanusException on error
207      */
208     protected void setControlKey(final PrometheusControlKey pControl) throws OceanusException {
209         /* If we do not have a control Key */
210         if (getControlKey() == null) {
211             /* Store the control details and return */
212             setValueControlKey(pControl);
213             return;
214         }
215 
216         /* Store the current detail into history */
217         pushHistory();
218 
219         /* Store the control details */
220         setValueControlKey(pControl);
221 
222         /* Check for changes */
223         checkForHistory();
224     }
225 
226     /**
227      * Control Data List.
228      */
229     public static class PrometheusControlDataList
230             extends PrometheusDataList<PrometheusControlData> {
231         /**
232          * Report fields.
233          */
234         private static final MetisFieldSet<PrometheusControlDataList> FIELD_DEFS = MetisFieldSet.newFieldSet(PrometheusControlDataList.class);
235 
236         /**
237          * Construct an empty CORE Control Data list.
238          *
239          * @param pData the DataSet for the list
240          */
241         protected PrometheusControlDataList(final PrometheusDataSet pData) {
242             this(pData, PrometheusListStyle.CORE);
243         }
244 
245         /**
246          * Construct an empty generic ControlData list.
247          *
248          * @param pData  the DataSet for the list
249          * @param pStyle the style of the list
250          */
251         protected PrometheusControlDataList(final PrometheusDataSet pData,
252                                             final PrometheusListStyle pStyle) {
253             super(PrometheusControlData.class, pData, PrometheusCryptographyDataType.CONTROLDATA, pStyle);
254         }
255 
256         /**
257          * Constructor for a cloned List.
258          *
259          * @param pSource the source List
260          */
261         private PrometheusControlDataList(final PrometheusControlDataList pSource) {
262             super(pSource);
263         }
264 
265         @Override
266         public MetisFieldSet<PrometheusControlDataList> getDataFieldSet() {
267             return FIELD_DEFS;
268         }
269 
270         @Override
271         public String listName() {
272             return LIST_NAME;
273         }
274 
275         @Override
276         public MetisFieldSet<PrometheusControlData> getItemFields() {
277             return PrometheusControlData.FIELD_DEFS;
278         }
279 
280         @Override
281         public boolean includeDataXML() {
282             return false;
283         }
284 
285         /**
286          * Get the single element.
287          *
288          * @return the control data
289          */
290         public PrometheusControlData getControl() {
291             return isEmpty()
292                     ? null
293                     : get(0);
294         }
295 
296         @Override
297         protected PrometheusControlDataList getEmptyList(final PrometheusListStyle pStyle) {
298             final PrometheusControlDataList myList = new PrometheusControlDataList(this);
299             myList.setStyle(pStyle);
300             return myList;
301         }
302 
303         @Override
304         public PrometheusControlDataList deriveList(final PrometheusListStyle pStyle) throws OceanusException {
305             return (PrometheusControlDataList) super.deriveList(pStyle);
306         }
307 
308         @Override
309         public PrometheusControlDataList deriveDifferences(final PrometheusDataSet pDataSet,
310                                                            final PrometheusDataList<?> pOld) {
311             return (PrometheusControlDataList) super.deriveDifferences(pDataSet, pOld);
312         }
313 
314         @Override
315         public PrometheusControlData addCopyItem(final PrometheusDataItem pItem) {
316             /* Can only clone a ControlData */
317             if (!(pItem instanceof PrometheusControlData)) {
318                 return null;
319             }
320 
321             /* Clone the control data */
322             final PrometheusControlData myControl = new PrometheusControlData(this, (PrometheusControlData) pItem);
323             add(myControl);
324             return myControl;
325         }
326 
327         @Override
328         public PrometheusControlData addNewItem() {
329             throw new UnsupportedOperationException();
330         }
331 
332         /**
333          * Add new ControlData item for new security.
334          *
335          * @param pVersion the version
336          * @throws OceanusException on error
337          */
338         public void addNewControl(final Integer pVersion) throws OceanusException {
339             /* Create the ControlData */
340             final PrometheusDataValues myValues = new PrometheusDataValues(OBJECT_NAME);
341             myValues.addValue(PrometheusDataResource.CONTROLDATA_VERSION, pVersion);
342 
343             /* Add the item */
344             addValuesItem(myValues);
345         }
346 
347         @Override
348         public PrometheusControlData addValuesItem(final PrometheusDataValues pValues) throws OceanusException {
349             /* Create the controlData */
350             final PrometheusControlData myControl = new PrometheusControlData(this, pValues);
351 
352             /* Check that this controlId has not been previously added */
353             if (!isIdUnique(myControl.getIndexedId())) {
354                 myControl.addError(ERROR_DUPLICATE, MetisDataResource.DATA_ID);
355                 throw new PrometheusDataException(myControl, ERROR_VALIDATION);
356             }
357 
358             /* Add to the list */
359             add(myControl);
360 
361             /* Return it */
362             return myControl;
363         }
364 
365         @Override
366         public void postProcessOnLoad() throws OceanusException {
367             /* Just sort the list */
368             reSort();
369         }
370 
371         @Override
372         protected PrometheusDataMapItem allocateDataMap() {
373             /* Unused */
374             throw new UnsupportedOperationException();
375         }
376     }
377 }