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 }