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