View Javadoc

1   package org.lsst.ccs.config;
2   
3   
4   import org.lsst.gruth.jutils.ComponentNode;
5   
6   import java.util.*;
7   import java.util.concurrent.locks.ReentrantLock;
8   import java.util.logging.Level;
9   
10  /**
11   * implements complex strategies on top of the DBInterface.
12   * Dues to cache problems there should be only one instance of this class
13   * operating on the network.
14   * services are to be addressed remotely if needed.
15   *
16   * @author bamade
17   */
18  // Date: 13/04/12
19  
20  public class ConfigurationFacade {
21      /**
22       * data access object
23       */
24      private DBInterface dao;
25      /**
26       * listens to deprecation events (can be null).
27       */
28      private DeprecationListener deprecationListener;
29  
30      private static final Integer ZERO = Integer.valueOf(0);
31      /**
32       * technical value to deal with the call stack of transactions
33       */
34      //int stackTransact;
35      ThreadLocal<Integer> stackState = new ThreadLocal<Integer>() {
36          public Integer initialValue() {
37              return ZERO;
38          }
39      };
40      /**
41       * fair lock
42       */
43      ReentrantLock lock = new ReentrantLock(true);
44  
45      /**
46       * creates a facade object on top of a Data Access Object that implements the base services.
47       *
48       * @param dao
49       */
50      public ConfigurationFacade(DBInterface dao) {
51          this.dao = dao;
52      }
53  
54      public void setDeprecationListener(DeprecationListener deprecationListener) {
55          this.deprecationListener = deprecationListener;
56      }
57  
58  
59      ///////////////////////////// internal utilities
60  
61      /**
62       * internal (non public) method to handle a transaction stack
63       */
64      synchronized void push() {
65          Integer level = stackState.get();
66          if (level == ZERO) {
67              // blocks
68              //System.out.println("----> BEGIN");
69              lock.lock();
70              dao.begin();
71          }
72          stackState.set(level + 1);
73          //System.out.println("------> level -> " + (level +1) + " thread :" +Thread.currentThread());
74          /* old version
75          if (stackTransact == 0) dao.begin();
76          stackTransact++;
77          */
78      }
79  
80      /**
81       * internal (non public) method to handle a transaction stack
82       */
83      synchronized void pop() {
84          Integer level = stackState.get();
85          if (level == ZERO) {
86              throw new IllegalStateException("dao transaction stack empty");
87          }
88          level--;
89          //System.out.println("------> level -> " + (level ) + " thread :" + Thread.currentThread());
90          stackState.set(level);
91          if (level == ZERO) {
92              //System.out.println("-----> END");
93              dao.end();
94              lock.unlock();
95          }
96  
97          /*
98          if (stackTransact == 0) {
99              throw new IllegalStateException("dao transaction stack empty");
100         }
101         stackTransact--;
102         if (stackTransact == 0) dao.end();
103         */
104     }
105 
106     /**
107      * deals with exceptions
108      *
109      * @param exc
110      */
111     //TODO: check for db failures there are bugs ....
112     synchronized void failInDAO(Exception exc) {
113         dao.fail(exc);
114         stackState.set(ZERO);
115         lock.unlock();
116         //stackTransact = 0;
117     }
118 
119     //TODO: simplify this mess!
120     synchronized void failNThrow(Exception exc) throws PersistenceLayerException {
121         PackCst.CURLOG.log(Level.SEVERE, "", exc);
122         dao.end();
123         stackState.set(ZERO);
124         lock.unlock();
125         throw new PersistenceLayerException(exc);
126     }
127 
128     //////////////////////////// external utilities
129 
130 
131     ///////////////  subsystem descriptions and database access
132 
133     /**
134      * registers a new (and complete) SubsystemDescription to the persistence Layer.
135      * if a previous SubsystemDescription with same subsystemName AND tag is present
136      * then it is pushed in history and replaced by the new one as a subsystem which is "alive".
137      * <p/>
138      * To avoid side effects always write code like this:
139      * <PRE>
140      * description = facade.registerSubsystemDescription(description);
141      * </PRE>
142      * <p/>
143      * if trying to save a Subsystem description already under DB management this method does nothing.
144      *
145      * @param newDescription a new SubsystemDescription with all ParameterDescriptions (this object is managed and modified
146      *                       by the persistence layer)
147      * @return an unmodifiable version of the modified parameter
148      * @throws PersistenceLayerException
149      * @throws IllegalArgumentException  if argument not a "new" description (generated by the factories)
150      */
151 
152     public SubsystemDescription registerSubsystemDescription(SubsystemDescription newDescription) throws PersistenceLayerException {
153         if (!(newDescription instanceof ASubsystemDescription)) {
154             //TODO : transform or exception? exception preferred
155             throw new IllegalArgumentException("not a new description");
156         }
157         return registerSubsystemDescription((ASubsystemDescription) newDescription);
158     }
159 
160     /**
161      * internal method to register a new desccription.
162      * The algorithm is this:
163      * <UL>
164      * <LI/> tries to get an active description with same name and tag
165      * <LI/> if this old description is present deprecates it and its id marks the current description's <TT>PreviousDescriptionID</TT>
166      * <LI/> the new description is saved
167      * <LI/> ghost description is created and saved
168      * <p/>
169      * </UL>
170      *
171      * @param newDescription
172      * @return
173      * @throws PersistenceLayerException
174      */
175     SubsystemDescription registerSubsystemDescription(ASubsystemDescription newDescription) throws PersistenceLayerException {
176         // key is name + tag
177         String subsystemName = newDescription.getSubsystemName();
178         String tag = newDescription.getTag();
179         SubsystemDescription previous = null;
180         // if key exists deprecates the old one
181         push();
182         try {
183             ASubsystemDescription oldOne = dao.getActiveSubsystemDescription(subsystemName, tag);
184             long previousDescriptionID = 0L;
185             if (oldOne != null) {
186                 if (oldOne.getId() == newDescription.getId()) {
187                     //TODO: modifications of comments in ParameterDescriptions? check for boolean
188                     PackCst.CURLOG.warning("trying to save an existing subsystem description : doing nothing");
189                     //System.out.println("trying to save an existing subsystem description : doing nothing");
190                     pop();
191                     return newDescription;
192                 }
193                 previous = deprecateSubsystemDescription(oldOne);
194                 previousDescriptionID = previous.getId();
195                 newDescription.setPreviousDescriptionID(previousDescriptionID);
196             }
197             // creation of ghost and alive objects
198             dao.saveSubsystemDescription(newDescription);
199             //TODO: eliminate this request and have the modified newDescription instead
200             ASubsystemDescription registered = dao.getActiveSubsystemDescription(subsystemName, tag);
201             GhostSubsystemDescription ghost = new GhostSubsystemDescription(registered);
202             dao.saveGhostDescriptions(ghost);
203             pop();
204             //TODO : is clone() necessary ? check the idea
205             return newDescription.clone();
206             //return newDescription ;
207         } catch (Exception exc) {
208             failInDAO(exc);
209             throw new PersistenceLayerException(exc);
210         }
211     }
212 
213     /**
214      * Internal code that deprecates a SubsystemDecription. All ConfigProfiles
215      * pointing to it are deprecated and <TT>DeprecationListeners</TT>
216      * are warned (if present).
217      * <p/>
218      * Since it is an "internal" method all exceptions are forwarded to the calling codes.
219      * </P>
220      *
221      * @param currentDescriptionInDB a SubsystemDescription that MUST be in the database
222      * @return a ghost subsystem description
223      */
224     SubsystemDescription deprecateSubsystemDescription(ASubsystemDescription currentDescriptionInDB) {
225         push();
226         GhostSubsystemDescription ghost = dao.getGhostDescription(currentDescriptionInDB.getId());
227         // marks the Ghost's end date
228         long endDate = System.currentTimeMillis();
229         ghost.setEndTimestamp(endDate);
230         currentDescriptionInDB.setEndTimestamp(endDate);
231         // gets all ConfigProfile -> deprecates them and warn Listener
232         Collection<AConfigProfile> profiles = dao.getActiveProfilesForSubsystem(currentDescriptionInDB);
233         for (AConfigProfile profile : profiles) {
234             deprecateConfigProfile(ghost, profile);
235         }
236         // remove from alive DB
237         if (deprecationListener != null) {
238             //DONE: better register past one than current!
239             //deprecationListener.subsystemDeprecating(currentDescriptionInDB);
240             //TODO: clone ghost?
241             deprecationListener.subsystemDeprecating(ghost);
242         }
243         dao.deleteActiveSubsystemDescription(currentDescriptionInDB);
244         pop();
245         return ghost;
246     }
247 
248     /**
249      * Deprecates a Subsystem description already in the database.
250      * If it's not there nothing happens!
251      *
252      * @param subsystemName
253      * @param tag           use "" if there is no tag
254      * @return the deprecated description (or null if none was deprecated)
255      * @throws PersistenceLayerException
256      */
257 
258     public SubsystemDescription deprecateSubsystemDescription(String subsystemName, String tag) throws PersistenceLayerException {
259         //TODO : check for preconditions (if tag is null then "")
260         push();
261         try {
262             SubsystemDescription res = null;
263             ASubsystemDescription oldOne = dao.getActiveSubsystemDescription(subsystemName, tag);
264             if (oldOne != null) {
265                 res = deprecateSubsystemDescription(oldOne);
266             }
267             pop();
268             return res;
269         } catch (Exception exc) {
270             failInDAO(exc);
271             throw new PersistenceLayerException(exc);
272         }
273     }
274 
275     /**
276      * returns the active subsystem description with name and tag.
277      *
278      * @param name
279      * @param tag
280      * @return null if none is found
281      * @throws PersistenceLayerException
282      */
283     public SubsystemDescription getActiveSubsystemDescription(String name, String tag) throws PersistenceLayerException {
284         push();
285         try {
286             return dao.getActiveSubsystemDescription(name, tag);
287         } catch (Exception exc) {
288             failInDAO(exc);
289             throw new PersistenceLayerException(exc);
290         }
291     }
292     ///////////// Profiles and database access
293 
294     /**
295      * registers a ConfigProfile in the database. if one with the same name and tag
296      * exists it is pushed in history and a DeprecationListener is warned.
297      * <p/>
298      * Beware of side effect: better assign the returned object to the reference holding the argument.
299      * <p/>
300      * if trying to save a Config Profile already under DB management this method does nothing.
301      *
302      * @param newProfile
303      * @return unmodifiable copy of argument
304      * @throws PersistenceLayerException
305      */
306 
307 
308     public ConfigProfile registerConfigProfile(ConfigProfile newProfile) throws PersistenceLayerException {
309         if (!(newProfile instanceof AConfigProfile)) {
310             throw new IllegalArgumentException("deprecated Profile");
311         }
312         return registerConfigProfile((AConfigProfile) newProfile);
313     }
314 
315     /**
316      * internal method to register a profile:
317      * <UL>
318      * <LI/> if profile  with same name and tag is found  it is deprecated and a link
319      * is established between profiles . If they refer to distinct subsystem an exception is fired.
320      * </UL>
321      *
322      * @param newProfile
323      * @return
324      * @throws PersistenceLayerException
325      * @throws IllegalArgumentException  if there is a name clash between subsystem referenced by the new and old profile
326      */
327     ConfigProfile registerConfigProfile(AConfigProfile newProfile) throws PersistenceLayerException {
328         push();
329         try {
330             ConfigProfile previous = null;
331             long previousConfigID = 0L;
332             String name = newProfile.getName();
333             String tag = newProfile.getTag();
334             AConfigProfile oldProfile = (AConfigProfile) getActiveConfigProfile(name, tag);
335             if (oldProfile != null) {
336                 if (oldProfile.getId() == newProfile.getId()) {
337                     //TODO: modifications of remarks? check for modification boolean
338                     PackCst.CURLOG.warning("trying to save an existing Config profile : doing nothing");
339                     //System.out.println("trying to save an existing Config profile : doing nothing");
340                     pop();
341                     return newProfile;
342                 }
343                 // trying to detect an anomaly: the config in the database is refernecinf a different subsystem
344                 if (!newProfile.getSubsystemName().equals(oldProfile.getSubsystemName())) {
345                     throw new IllegalArgumentException("Name clash :  trying to create a configProfile with same name and different subsystem "
346                             + name + "/" + tag + " already exists for subsystem " + oldProfile.getSubsystemName());
347                 }
348                 GhostSubsystemDescription ghost = dao.getGhostDescription(oldProfile.getSubsystemId());
349                 previous = deprecateConfigProfile(ghost, oldProfile);
350                 //TODO: update differences between values
351                 previousConfigID = previous.getId();
352                 newProfile.setPreviousConfigID(previousConfigID);
353             }
354             /// what if subsystem exists no more ?
355             // NOTE TO DEVELOPERS : though this looks like a duplicate
356             // of text in AConfigProfile constructor
357             // it doesn't: because when used in remote mode there may be
358             // an object with wrong values on the client code
359             //TODO : redo the tryc/catch architecture this is wrong!
360             long idSubs = newProfile.getSubsystemId();
361             //List listSubs = dao.simpleHQLRequest("from ASubsystemDescription where id = " + idSubs);
362             ASubsystemDescription registered = dao.getActiveSubsystemDescription(idSubs) ;
363             if (registered == null) {
364                 throw new IllegalArgumentException(" No such living subsystem description in database " +
365                         newProfile.getSubsystemDescription());
366             }
367 
368             dao.saveConfigProfile(newProfile);
369             pop();
370             //DONE: return clone?
371             return newProfile.clone();
372             //return newProfile;
373         } catch (Exception exc) {
374             failInDAO(exc);
375             throw new PersistenceLayerException(exc);
376         }
377     }
378 
379     /**
380      * internal method for deprecating a ConfigProfile
381      *
382      * @param ghost
383      * @param currentProfileInDB
384      * @return deprecated ConfigProfile
385      */
386     ConfigProfile deprecateConfigProfile(GhostSubsystemDescription ghost, AConfigProfile currentProfileInDB) {
387         push();
388         PastConfigProfile past = new PastConfigProfile(ghost, currentProfileInDB);
389         dao.savePastProfile(past);
390         if (deprecationListener != null) {
391             //DONE: better register past one than current!
392             deprecationListener.configProfileDeprecating(past);
393         }
394         dao.deleteActiveConfigProfile(currentProfileInDB);
395         pop();
396         return past;
397     }
398 
399     /**
400      * Deprecates a ConfigProfile. If not in database does nothing.
401      * <p/>
402      * <B>Important note</B>: deprecating a configProfile to provide later a replacement will
403      * break the link between  ConfigProfile (previousConfigID) so it is preferable to create
404      * directly a new replacement that will deprecate the previous one and update the link.
405      *
406      * @param name
407      * @param tag
408      * @return the deprecated ConfigProfile (or null if there was none)
409      * @throws PersistenceLayerException
410      */
411 
412     public ConfigProfile deprecateConfigProfile(String name, String tag) throws PersistenceLayerException {
413         push();
414         try {
415             ConfigProfile res = null;
416             AConfigProfile currentProfile = (AConfigProfile) getActiveConfigProfile(name, tag);
417             if (currentProfile != null) {
418                 GhostSubsystemDescription ghost = dao.getGhostDescription(currentProfile.getSubsystemId());
419                 res = deprecateConfigProfile(ghost, currentProfile);
420             }
421             pop();
422             return res;
423         } catch (Exception exc) {
424             failInDAO(exc);
425             throw new PersistenceLayerException(exc);
426         }
427     }
428 
429     /**
430      * returns an active ConfigProfile with name and tag
431      * @param name
432      * @param tag
433      * @return null if none found
434      * @throws PersistenceLayerException
435      */
436     public ConfigProfile getActiveConfigProfile(String name, String tag) throws PersistenceLayerException {
437         push();
438         try {
439             AConfigProfile res = dao.getActiveConfigProfile(name, tag);
440             pop();
441             return res;
442         } catch (Exception exc) {
443             failInDAO(exc);
444             throw new PersistenceLayerException(exc);
445         }
446     }
447 
448 
449     /**
450      * registers modified  parameter during an engineering session
451      * @param parameterConfiguration
452      * @return
453      * @throws PersistenceLayerException
454      * @throws IllegalArgumentException if the parameterConfiguration is not a regsitered active parameter of if
455      * the configuration is not in engineering mode
456      */
457     public ParameterConfiguration engineerParmConfig(ParameterConfiguration parameterConfiguration) throws PersistenceLayerException {
458         if (!(parameterConfiguration instanceof AParameterConfiguration)) {
459             throw new IllegalArgumentException("not a registered active  parameterConfiguraiton");
460         }
461         AParameterConfiguration config = (AParameterConfiguration) parameterConfiguration;
462         if (config.getId() == 0L) {
463             throw new IllegalArgumentException("not a registered parameterConfiguraiton");
464         }
465         //TODO: check if this test is correct in all cases of isCopy!
466         if (!config.isCopy()) {
467             throw new IllegalArgumentException("parameterConfiguration not in engineering mode");
468         }
469         push();
470         try {
471             pop();
472             //TODO: test thoroughly may not work under some conditions !!!
473             dao.modifyParmConfig(config);
474             return config;
475         } catch (Exception exc) {
476             failInDAO(exc);
477             throw new PersistenceLayerException(exc);
478         }
479 
480     }
481 
482 
483     /**
484      * the run and end registrations are based on messages received from a subsystem.
485      * As much as possible the code tries to deal with lost messages (there is only one subsystem with a given name
486      * running at any time):
487      * <UL>
488      * <LI> if an end message is lost: next time a subsystem with same name is started
489      * the previous run is searched, if this previous run has no end date it is "guessed" that is an arbitrary date
490      * is set just before the current run.
491      * <LI> if a start message is lost  (much more unlikely) an artificial start date is created jsut after
492      * the latest run with no "next data"
493      * <LI> if both message are lost there is nothing we can do to guess the startDate of a subsystem with differnet configuration
494      * <p/>
495      * </UL>
496      *
497      * @param subsystemName
498      * @param configName
499      * @param tag
500      * @param startTime
501      * @throws PersistenceLayerException
502      */
503     public void registerRun(String subsystemName, String configName, String tag, long startTime) throws PersistenceLayerException {
504         // consistency of the algorithm supposes there are not two concurrent same subsystem running
505         //gets latest run with same subsystemName
506         // that is Timenext is zero
507         push();
508         try {
509             List list = dao.simpleHQLRequest(
510                     "from RunHistory where subsystemName = '" + subsystemName
511                             + "' and timeNext = 0");
512             if (list.size() != 0) {
513                 if (list.size() > 1) {
514                     PackCst.CURLOG.warning( "multiple runs with no next run!");
515                     //TODO repair!
516                 }
517                 //change its timenext (and possible their endTimestampLimit if it is STILL_VALID : issu a warning)
518                 RunHistory previousRun = (RunHistory) list.get(0);
519                 previousRun.setTimeNext(startTime);
520                 if (previousRun.getEndTimestampLimit() == PackCst.STILL_VALID) {
521                     previousRun.setEndTimestampLimit(startTime - 1);
522                     previousRun.setEndGuessed(true);
523                 }
524                 // save   previousRun?
525             }
526             RunHistory run = new RunHistory(subsystemName, configName, tag, startTime);
527             // save the current RunHistory object
528             dao.saveRun(run);
529             pop();
530         } catch (Exception exc) {
531             failInDAO(exc);
532             throw new PersistenceLayerException(exc);
533         }
534     }
535 
536     /**
537      * marks the end of a run.
538      * See the documentation of <TT>registerRun</TT>
539      * @param subsystemName
540      * @param configName
541      * @param tag
542      * @param endTime
543      * @throws PersistenceLayerException
544      */
545     public void endRun(String subsystemName, String configName, String tag, long endTime) throws PersistenceLayerException {
546         push();
547         if(configName == null) { configName="" ; }
548         if(tag == null) { tag = "" ; }
549         try {
550             List<RunHistory> list = (List<RunHistory>) dao.simpleHQLRequest(
551                     "from RunHistory where subsystemName = '" + subsystemName
552                             + "' and endTimestampLimit = " + PackCst.STILL_VALID);
553             if (list.size() == 0) {
554                 // means that a start message was lost
555                 // so gets previous and create an artificial start
556                 list = (List<RunHistory>) dao.simpleHQLRequest(
557                         "from RunHistory where subsystemName = '" + subsystemName
558                                 + "' and timeNext = 0");
559                 if (list.size() != 0) {
560                     if (list.size() > 1) {
561                         PackCst.CURLOG.warning( "multiple runs with no next run!");
562                         //TODO repair!
563                     }
564                     RunHistory previousRun = (RunHistory) list.get(0);
565                     // we KNOW it is not STILL_VALID
566                     long previousEnd = previousRun.getEndTimestampLimit();
567                     long fakeStart = previousEnd + 1;
568                     previousRun.setTimeNext(fakeStart);
569                     RunHistory run = new RunHistory(subsystemName, configName, tag, fakeStart);
570                     run.setStartGuessed(true);
571                     run.setEndTimestampLimit(endTime);
572                     dao.saveRun(run);
573                 }
574 
575             } else {
576                 // the try to reconstitute a run from other runs that have no next
577                 // what happens if other element in List without next?
578                 for (RunHistory run : list) {
579                     // check if config and tag match
580                     String runConfigName = run.getConfigurationName() ;
581                     if(runConfigName == null) {runConfigName = ""; }
582                     String runTagName = run.getTag() ;
583                     if(runTagName == null) {runTagName = "" ; }
584                     if (runConfigName.equals(configName) && runTagName.equals(tag)) {
585                         run.setEndTimestampLimit(endTime);
586                     } else {
587                         // if list size > 1 end everything ?
588                         PackCst.CURLOG.log(Level.WARNING, "run with no end date", run);
589                         //TODO: SHOULD NOT HAPPEN?
590                     }
591 
592                 }
593             }
594             pop();
595         } catch (Exception exc) {
596             failInDAO(exc);
597             throw new PersistenceLayerException(exc);
598         }
599 
600     }
601 
602     /**
603      * creates and registers a PreparedConfiguration.
604      * <P/>
605      *
606      * @param subsystemName can be null or empty if configName is valid
607      * @param configName    can be null or empty if subsystemName is valid
608      * @param tag
609      * @return null if preparation is impossible
610      * @throws PersistenceLayerException id db problem
611      * @throws IllegalArgumentException  if subsystemName and ConfigName are incompatible
612      */
613     public PreparedConfiguration registerPreparedConfiguration(String subsystemName, String configName, String tag, String user) throws PersistenceLayerException {
614         push();
615         try {
616             PreparedConfiguration res;
617             ComponentNode componentNode;
618             //if configName is null or "" go for the subsystem
619             if (configName == null || "".equals(configName)) {
620                 SubsystemDescription description = getActiveSubsystemDescription(subsystemName, tag);
621                 if (description == null) {
622                     pop();
623                     return null;
624                 }
625                 componentNode = description.getTopComponentNode();
626                 //TODO: change naming policy for PreparedConfiguration
627                 res = new PreparedConfiguration(subsystemName + "_default", tag, subsystemName, user, false, componentNode);
628             } else {
629                 // else find the correct configuration if subsystem does not match
630                 ConfigProfile configProfile = getActiveConfigProfile(configName, tag);
631                 // but subsystem can be null or empty
632                 if (subsystemName != null && !"".equals(subsystemName)) {
633                     String registeredName = configProfile.getSubsystemDescription().getSubsystemName();
634                     if (!registeredName.equals(subsystemName)) {
635                         throw new IllegalArgumentException(subsystemName + "differs from " +
636                                 registeredName + " in request");
637                     }
638                 }
639                 res = new PreparedConfiguration(configProfile);
640             }
641             dao.savePreparedConfiguration(res);
642             pop();
643             return res;
644         } catch (Exception exc) {
645             failInDAO(exc);
646             throw new PersistenceLayerException(exc);
647         }
648     }
649 
650     /**
651      * Gets a ComponentNode that can ge used to start a subsystem.
652      * Depending on the value of parameters this can be build from a "default" description
653      * or from an active config Profile.
654      * @param subsystemName
655      * @param configName
656      * @param tag
657      * @return
658      * @throws PersistenceLayerException
659      */
660     public ComponentNode getActiveComponentNode(String subsystemName, String configName, String tag) throws PersistenceLayerException {
661         push();
662         try {
663             ComponentNode res = null;
664             // if PreparedConfiguration exists returns content
665             List<PreparedConfiguration> list = (List<PreparedConfiguration>) dao.simpleHQLRequest("from PreparedConfiguration where "
666                     + " subsystemName = '" + subsystemName
667                     + "' AND configName = '" + configName
668                     + "' AND tag = '" + tag + "'");
669             if (list.size() != 0) {
670                 res = list.get(0).getConfiguredData();
671             } else {
672                 // may get directly from Subsystem or Config
673                 if (configName != null && !"".equals(configName)) {
674                     AConfigProfile configProfile = dao.getActiveConfigProfile(configName, tag);
675                     if (subsystemName != null && !"".equals(subsystemName)) {
676                         if (!subsystemName.equals(configProfile.getSubsystemName())) {
677                             throw new IllegalArgumentException(subsystemName + "differs from " +
678                                     configProfile.getSubsystemName() + " in config profile");
679                         }
680                     }
681                     res = configProfile.getModifiedConfigurationData();
682                 } else { //from subsystem only
683                     ASubsystemDescription description = dao.getActiveSubsystemDescription(subsystemName, tag);
684                     if (description != null) {
685                         res = description.getTopComponentNode();
686                     }
687                 }
688             }
689             pop();
690             return res;
691         } catch (Exception exc) {
692             failInDAO(exc);
693             throw new PersistenceLayerException(exc);
694         }
695     }
696     // idem with  machine Configuration
697 
698     /**
699      * gets a ComponentNode from a <TT>MachineConfiguration</TT> object
700      * @param machineConfiguration
701      * @return
702      * @throws PersistenceLayerException
703      */
704     public ComponentNode getActiveComponentNode(MachineConfiguration machineConfiguration) throws PersistenceLayerException {
705         String subsystemName = machineConfiguration.getSubsystemName();
706         String configName = machineConfiguration.getConfigName();
707         String tag = machineConfiguration.getTag();
708         return this.getActiveComponentNode(subsystemName, configName, tag);
709     }
710 
711     /**
712      * regsiters a <TT>MachineConfiguration</TT> object to the database
713      * @param machineConfiguration
714      * @throws PersistenceLayerException
715      */
716     public void registerMachineConfiguration(MachineConfiguration machineConfiguration) throws PersistenceLayerException {
717         push();
718         try {
719             dao.saveMachineConfiguration(machineConfiguration);
720         } catch (Exception exc) {
721             failInDAO(exc);
722             throw new PersistenceLayerException(exc);
723         }
724 
725     }
726 
727     /**
728      *  get a <TT>MachineConfiguration</TT> object from the database.
729      * @param macAddress
730      * @return null if none found
731      * @throws PersistenceLayerException
732      */
733     public MachineConfiguration getMachineConfiguration(String macAddress) throws PersistenceLayerException {
734         push();
735         try {
736             MachineConfiguration res = dao.getMachineConfiguration(macAddress);
737             pop();
738             return res;
739         } catch (Exception exc) {
740             failInDAO(exc);
741             throw new PersistenceLayerException(exc);
742         }
743     }
744 
745     /**
746      * returns the ConfigProfile active when a subsystem was running at that date.
747      * <BR/>
748      * <B> PROBLEM</B>: a subsystem may be running without a configProfile. so beware
749      * @param subsystemName
750      * @param date
751      * @return
752      * @throws PersistenceLayerException
753      */
754     public ConfigProfile getConfigRunningAt(String subsystemName, long date) throws PersistenceLayerException {
755         //get all RunHistory with same subsystemName , startDate < date and endDate > date
756         push();
757         try {
758             //TODO: sort by database? with a MAX?
759             List listRun = dao.simpleHQLRequest("from RunHistory where subsystemName = '"
760                     + subsystemName + "' and startTimeStamp < " + date + " and endTimeStampLimit > " + date + " ");
761             int numberRuns = listRun.size();
762             if (numberRuns != 0) {
763                 if (numberRuns > 1) {
764                     //anomaly but possible
765                     PackCst.CURLOG.log(Level.WARNING, "multiple runs in same time span", listRun);
766                     Collections.sort(listRun, new Comparator() {
767                         public int compare(Object o, Object o1) {
768                             Long longA = ((RunHistory) o).getStartTimestamp();
769                             Long longB = ((RunHistory) o1).getStartTimestamp();
770                             return longA.compareTo(longB);
771                         }
772                     });
773                 }
774                 RunHistory lastRun = (RunHistory) listRun.get(numberRuns - 1);
775                 //
776                 return getConfigValidAt(lastRun.getConfigurationName(), lastRun.getTag(), date);
777             }
778 
779             return null;
780         } catch (Exception exc) {
781             failInDAO(exc);
782             throw new PersistenceLayerException(exc);
783         }
784     }
785 
786     /**
787      * tries to get a ConfigProfile which was valid at a given date.
788      * Since there is only one Config profile active with a given name at a given date
789      * there is only one parameter needed (there is no need for a subsystem name)
790      *
791      * @param configName
792      * @param configTag
793      * @return
794      * @throws PersistenceLayerException
795      */
796 
797     public ConfigProfile getConfigValidAt(String configName, String configTag, long date) throws PersistenceLayerException {
798         push();
799         try {
800             List list = dao.simpleHQLRequest("from AConfigProfile where name ='" + configName + "' and tag ='" + configTag
801                     + "' and startTimestamp <= " + date + " ");
802             if (list.size() != 0) {
803                 pop();
804                 return (ConfigProfile) list.get(0);
805             }
806             list = dao.simpleHQLRequest("from PastConfigProfile where name ='" + configName + "' and tag ='" + configTag
807                     + "' and startTimestamp <= " + date + " and endTimestamp >= " + date + " ");
808             if (list.size() != 0) {
809                 pop();
810                 return (ConfigProfile) list.get(0);
811             }
812             pop();
813             return null;
814         } catch (Exception exc) {
815             failInDAO(exc);
816             throw new PersistenceLayerException(exc);
817 
818         }
819     }
820 
821 
822     /**
823      *  returns the value of a parameter for a subsystem at a given date.
824      * <P/>
825      * <B>BUG:</B> this method is not correct when a "default" subsystem is run without a configuration.
826      * Will be changed!
827      * @param subsystemName
828      * @param parameterPath
829      * @param date
830      * @return
831      * @throws PersistenceLayerException
832      */
833     //TODO: URGENT rewrite this method to have a subsystem without configProfile
834     public String getActiveValueAt(String subsystemName, String parameterPath, long date) throws PersistenceLayerException {
835         ConfigProfile profile = getConfigRunningAt(subsystemName, date);
836         if (profile == null) {
837             PackCst.CURLOG.warning( "no running profile at " + new Date(date) + "for subsystem "
838                     + subsystemName);
839             return null;
840         }
841         return profile.getValueAt(parameterPath, date);
842 
843     }
844 
845     /**
846      * returns the value of a parameter at a given time for a given Profile.
847      *
848      * @param profileName
849      * @param profileTag
850      * @param parameterPath a String in Path syntax ("componentName//parameterName")
851      * @param date
852      * @return null if profile or parameter not found
853      * @throws PersistenceLayerException
854      */
855 
856     public String getValueValidAt(String profileName, String profileTag, String parameterPath, long date) throws PersistenceLayerException {
857         ConfigProfile profile = getConfigValidAt(profileName, profileTag, date);
858         return profile.getValueAt(parameterPath, date);
859     }
860 
861 
862     /**
863      * gets a previous ConfigProfile with the same name and tag.
864      *
865      * @param current
866      * @return null if there is none (or if the deprecation was not carried out through a replacement or repair)
867      * @throws PersistenceLayerException
868      */
869 
870     public ConfigProfile getPrevious(ConfigProfile current) throws PersistenceLayerException {
871         if (current == null) return null;
872         long id = current.getPreviousConfigID();
873         if (0L == id) {
874             return null;
875         }
876         push();
877         try {
878             ConfigProfile res = null;
879             List list = dao.simpleHQLRequest("from PastConfigProfile where id = '" + id + "'");
880             if (list.size() != 0) {
881                 res = (ConfigProfile) list.get(0);
882             }
883             pop();
884             return res;
885         } catch (Exception exc) {
886             failInDAO(exc);
887             throw new PersistenceLayerException(exc);
888 
889         }
890     }
891 
892     /**
893      * gets the next modified ConfigProfile with same name and tag
894      *
895      * @param current
896      * @return null if none or deprecation was not carried out through a replacement or repair
897      * @throws PersistenceLayerException
898      */
899 
900     public ConfigProfile getNext(ConfigProfile current) throws PersistenceLayerException {
901         if (current == null) return null;
902         long id = current.getId();
903         if (0L == id) {
904             return null;
905         }
906         push();
907         try {
908             ConfigProfile res = null;
909             // tODO: if ghost then only one request to operate ....
910             // first try if in a new Config
911             List list = dao.simpleHQLRequest("from AConfigProfile where previousConfigID = '" + id + "'");
912             if (list.size() != 0) {
913                 res = (ConfigProfile) list.get(0);
914             } else {
915                 list = dao.simpleHQLRequest("from PastConfigProfile where previousConfigID = '" + id + "'");
916                 if (list.size() != 0) {
917                     res = (ConfigProfile) list.get(0);
918                 }
919             }
920             pop();
921             return res;
922         } catch (Exception exc) {
923             failInDAO(exc);
924             throw new PersistenceLayerException(exc);
925 
926         }
927     }
928 
929 
930     /**
931      * returns the previous description with same name and tag.
932      *
933      * @param current
934      * @return previous description or null if there was none (or if the previous one was not deprecated through a replacement)
935      * @throws PersistenceLayerException
936      */
937 
938     public SubsystemDescription getPrevious(SubsystemDescription current) throws PersistenceLayerException {
939         if (current == null) return null;
940         long id = current.getPreviousDescriptionID();
941         if (0L == id) {
942             return null;
943         }
944         push();
945         try {
946             SubsystemDescription res = null;
947             List list = dao.simpleHQLRequest("from GhostSubsystemDescription where id = '" + id + "'");
948             if (list.size() != 0) {
949                 res = (SubsystemDescription) list.get(0);
950             }
951             pop();
952             return res;
953         } catch (Exception exc) {
954             failInDAO(exc);
955             throw new PersistenceLayerException(exc);
956 
957         }
958     }
959 
960     ///////////// other utilities
961 
962 
963     /**
964      * forwards an HQLrequest to the DAO.
965      * Though this is not yet tested only "from" (SELECT) request should be accepted
966      * @param hqlString
967      * @return
968      * @throws PersistenceLayerException
969      */
970     public List<?> simpleHQLRequest(String hqlString) throws PersistenceLayerException {
971         push();
972         try {
973             List<?> res = dao.simpleHQLRequest(hqlString);
974             pop();
975             return res;
976         } catch (Exception exc) {
977             failInDAO(exc);
978             throw new PersistenceLayerException(exc);
979         }
980     }
981 
982 }