1 /*
2 * Copyright 2003 - 2013 The eFaps Team
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * Revision: $Rev$
17 * Last Changed: $Date$
18 * Last Changed By: $Author$
19 */
20
21 package org.efaps.update;
22
23 import java.net.URL;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.Comparator;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.Iterator;
30 import java.util.LinkedHashSet;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Map.Entry;
34 import java.util.Set;
35
36 import org.apache.commons.collections4.CollectionUtils;
37 import org.apache.commons.collections4.ListUtils;
38 import org.apache.commons.jexl2.Expression;
39 import org.apache.commons.jexl2.JexlContext;
40 import org.apache.commons.jexl2.JexlEngine;
41 import org.apache.commons.lang3.builder.ToStringBuilder;
42 import org.efaps.admin.datamodel.Type;
43 import org.efaps.ci.CIAdmin;
44 import org.efaps.ci.CIAdminCommon;
45 import org.efaps.ci.CIAdminEvent;
46 import org.efaps.db.AttributeQuery;
47 import org.efaps.db.Delete;
48 import org.efaps.db.Insert;
49 import org.efaps.db.Instance;
50 import org.efaps.db.InstanceQuery;
51 import org.efaps.db.QueryBuilder;
52 import org.efaps.db.Update;
53 import org.efaps.update.event.Event;
54 import org.efaps.update.util.InstallationException;
55 import org.efaps.util.EFapsException;
56 import org.efaps.util.cache.CacheReloadException;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59 import org.xml.sax.SAXException;
60
61 /**
62 * <p>
63 * This class is the major class for importing or updating of types, commands
64 * and so on in eFaps.
65 * <p/>
66 * <p>
67 * For every kind of Object in eFaps a own class extends this AbstractUpdate. In
68 * this classes the XML-Files is read and with the digester converted in
69 * Objects. After reading all Objects of one XML-File the Objects are inserted
70 * corresponding to the Version.
71 * </p>
72 *
73 * @author The eFaps Team
74 * @version $Id$
75 */
76 public abstract class AbstractUpdate
77 implements IUpdate
78 {
79
80 /**
81 * Logging instance used to give logging information of this class.
82 */
83 public static final Logger LOG = LoggerFactory.getLogger(AbstractUpdate.class);
84
85 /**
86 * JexlEngine for expression evaluation.
87 */
88 private static final JexlEngine JEXL = new JexlEngine();
89
90 /**
91 * The URL of the xml file is stored in this instance variable.
92 */
93 private final URL url;
94
95 /**
96 * The name of the data model type is store in this instance variable.
97 */
98 private final String dataModelTypeName;
99
100 /**
101 * All known link types are set to this instance variable.
102 */
103 private final Set<Link> allLinkTypes;
104
105 /**
106 * The universal unique identifier of the object is stored in this instance
107 * variable.
108 *
109 * @see #setUUID
110 */
111 private String uuid = null;
112
113 /**
114 * Name of the file application for which this XML file is defined.
115 *
116 * @see #setFileApplication
117 */
118 private String fileApplication = null;
119
120 /**
121 * Revision of the XML file.
122 *
123 * @see #setFileRevision
124 */
125 private String fileRevision = null;
126
127 /**
128 * All definitions of versions are added to this list.
129 */
130 private final List<AbstractDefinition> definitions = new ArrayList<AbstractDefinition>();
131
132 /**
133 * Default constructor with no defined possible links for given
134 * <code>_dataModelTypeName</code>.
135 *
136 * @param _url URL of the update file
137 * @param _dataModelTypeName name of the data model type to update
138 */
139 protected AbstractUpdate(final URL _url,
140 final String _dataModelTypeName)
141 {
142 this(_url, _dataModelTypeName, null);
143 }
144
145 /**
146 * Default constructor with defined possible links
147 * <code>_allLinkTypes</code> for given <code>_dataModelTypeName</code>.
148 *
149 * @param _url URL of the update file
150 * @param _dataModelTypeName name of the data model type to update
151 * @param _allLinkTypes all possible type link
152 */
153 protected AbstractUpdate(final URL _url,
154 final String _dataModelTypeName,
155 final Set<Link> _allLinkTypes)
156 {
157 this.url = _url;
158 this.dataModelTypeName = _dataModelTypeName;
159 this.allLinkTypes = _allLinkTypes;
160 }
161
162 /**
163 * Read event for given tags path with attributes and text.
164 *
165 * @param _tags tags path as list
166 * @param _attributes map of attributes for current tag
167 * @param _text content text of this tags path TODO: error could not be
168 * thrown because db properties is not read correctly
169 * @throws SAXException on error
170 * @throws EFapsException on error
171 */
172 @Override
173 public void readXML(final List<String> _tags,
174 final Map<String, String> _attributes,
175 final String _text)
176 throws SAXException, EFapsException
177 {
178 if (_tags.size() == 1) {
179 final String value = _tags.get(0);
180 if ("uuid".equals(value)) {
181 this.uuid = _text;
182 } else if ("file-application".equals(value)) {
183 this.fileApplication = _text;
184 } else if ("file-revision".equals(value)) {
185 this.fileRevision = _text;
186 } else if ("definition".equals(value)) {
187 this.definitions.add(newDefinition());
188 }
189 } else if ("definition".equals(_tags.get(0))) {
190 final AbstractDefinition curDef = this.definitions.get(this.definitions.size() - 1);
191 curDef.readXML(_tags.subList(1, _tags.size()), _attributes, _text);
192 } else {
193 throw new SAXException("Unknown XML Tag: " + _tags + " for: " + this.url);
194 }
195 }
196
197 /**
198 * Creates a new definition instance used from
199 * {@link #readXML(List, Map, String)}.
200 *
201 * @return new definition instance
202 */
203 protected abstract AbstractDefinition newDefinition();
204
205 /**
206 * Adds one definition of a update for a specific version to all definitions
207 * in {@link #definitions}.
208 *
209 * @param _definition definition to add
210 * @see #definitions
211 */
212 protected void addDefinition(final AbstractDefinition _definition)
213 {
214 this.definitions.add(_definition);
215 }
216
217 /**
218 * The instance method returns the eFaps instance representing the read XML
219 * configuration. If not already get from the eFaps database, the
220 * information is read. If no instance exists in the database, a new one is
221 * automatically created. The method searches for the given universal unique
222 * identifier in {@link #uuid} the instance in the eFaps database and stores
223 * the result in {@link #instance}. If no object is found in eFaps,
224 * {@link #instance} is set to <code>null</code>. A new instance is created
225 * in the eFaps DB for given universal unique identifier in {@link #uuid}.
226 * The name of the access set is also the universal unique identifier,
227 * because the name of access set is first updates in the version
228 * definition.<br/>
229 * The new created object is stored as instance information in
230 * {@link #instance}.
231 *
232 * @param _jexlContext context used to evaluate JEXL expressions
233 * @param _step current step of the update life cycle
234 * @param _profiles the Profiles assigned
235 * @throws InstallationException from called update methods
236 */
237 @Override
238 public void updateInDB(final JexlContext _jexlContext,
239 final UpdateLifecycle _step,
240 final Set<Profile> _profiles)
241 throws InstallationException
242 {
243 for (final AbstractDefinition def : this.definitions) {
244 // only execute if
245 // 1. valid version
246 // 2. application dependcies are met
247 // 3. profiles is empty or the profile list contains the profile of the definition
248 // or the default profile must be applied
249 if (def.isValidVersion(_jexlContext) && def.appDependenciesMet()
250 && (def.getProfiles().isEmpty()
251 || CollectionUtils.containsAny(_profiles, def.getProfiles())
252 || def.isApplyDefault(_profiles, this.definitions))) {
253 if (this.url != null && AbstractUpdate.LOG.isDebugEnabled()) {
254 AbstractUpdate.LOG.debug("Executing '" + this.url.toString() + "'");
255 }
256 def.updateInDB(_step, this.allLinkTypes);
257 }
258 }
259 }
260
261 /**
262 * This is the getter method for the instance variable {@link #url}.
263 *
264 * @return value of instance variable {@link #url}
265 */
266 @Override
267 public URL getURL()
268 {
269 return this.url;
270 }
271
272 /**
273 * Defines the new {@link #uuid UUID} of the updated object.
274 *
275 * @param _uuid new UUID for the object to update
276 * @see #uuid
277 */
278 protected void setUUID(final String _uuid)
279 {
280 this.uuid = _uuid;
281 }
282
283 /**
284 * This is the getter method for instance variable {@link #uuid}.
285 *
286 * @return value of instance variable {@link #uuid}
287 * @see #uuid
288 * @see #setUUID
289 */
290 public String getUUID()
291 {
292 return this.uuid;
293 }
294
295 /**
296 * This is the setter method for instance variable {@link #fileApplication}.
297 *
298 * @param _fileApplication new value for instance variable
299 * {@link #fileApplication}
300 * @see #fileApplication
301 * @see #getFileApplication
302 */
303 public void setFileApplication(final String _fileApplication)
304 {
305 this.fileApplication = _fileApplication;
306 }
307
308 /**
309 * This is the getter method for instance variable {@link #fileApplication}.
310 *
311 * @return value of instance variable {@link #fileApplication}
312 * @see #fileApplication
313 * @see #setFileApplication
314 */
315 @Override
316 public String getFileApplication()
317 {
318 return this.fileApplication;
319 }
320
321 /**
322 * This is the setter method for instance variable {@link #fileRevision}.
323 *
324 * @param _fileRevision new value for instance variable
325 * {@link #fileRevision}
326 * @see #fileRevision
327 * @see #getFileRevision
328 */
329 public void setFileRevision(final String _fileRevision)
330 {
331 this.fileRevision = _fileRevision;
332 }
333
334 /**
335 * This is the getter method for instance variable {@link #fileRevision}.
336 *
337 * @return value of instance variable {@link #fileRevision}
338 * @see #fileRevision
339 * @see #setFileRevision
340 */
341 public String getFileRevision()
342 {
343 return this.fileRevision;
344 }
345
346 /**
347 * This is the getter method for instance variable {@link #definitions}.
348 *
349 * @return value of instance variable {@link #definitions}
350 * @see #definitions
351 */
352 public List<AbstractDefinition> getDefinitions()
353 {
354 return this.definitions;
355 }
356
357 /**
358 * This is the getter method for the instance variable
359 * {@link #dataModelTypeName}.
360 *
361 * @return value of instance variable {@link #dataModelTypeName}
362 */
363 public String getDataModelTypeName()
364 {
365 return this.dataModelTypeName;
366 }
367
368 /**
369 * This is the getter method for the instance variable {@link #allLinkTypes}
370 * .
371 *
372 * @return value of instance variable {@link #allLinkTypes}
373 */
374 protected Set<Link> getAllLinkTypes()
375 {
376 return this.allLinkTypes;
377 }
378
379 /**
380 * Returns a string representation with values of all instance variables.
381 *
382 * @return string representation of this abstract update
383 */
384 @Override
385 public String toString()
386 {
387 return new ToStringBuilder(this).append("uuid", this.uuid).append("fileApplication", this.fileApplication)
388 .append("fileRevision", this.fileRevision).append("definitions", this.definitions).toString();
389 }
390
391 /**
392 * The class is used to define the links with all information needed to
393 * update the link information between the object to update and the related
394 * objects.
395 *
396 * @see #setLinksInDB
397 */
398 protected static class Link
399 {
400 /** Name of the link. */
401 private final String linkName;
402
403 /**
404 * Name of the parent attribute in the link. The parent attribute stores
405 * the id of the objecto udpate.
406 */
407 private final String parentAttrName;
408
409 /**
410 * Name of the child type used to query for the given name to which a
411 * link must be set.
412 */
413 private final String childTypeName;
414
415 /** Name of the child attribute in the link. */
416 private final String childAttrName;
417
418 /**
419 * set of key attributes.
420 */
421 private final Set<String> keyAttributes = new HashSet<String>();
422
423 /**
424 * Include the child types during evaluation.
425 */
426 private boolean includeChildTypes = false;
427
428 /**
429 * Constructor used to initialize the instance variables.
430 *
431 * @param _linkName name of the link itself
432 * @param _parentAttrName name of the parent attribute in the link
433 * @param _childTypeName name of the child type
434 * @param _childAttrName name of the child attribute in the link
435 * @param _keyAttributes list of attributes used to identify the object
436 * to be connected default "Name"
437 * @see #linkName
438 * @see #parentAttrName
439 * @see #childTypeName
440 * @see #childAttrName
441 */
442 public Link(final String _linkName,
443 final String _parentAttrName,
444 final String _childTypeName,
445 final String _childAttrName,
446 final String... _keyAttributes)
447 {
448 this.linkName = _linkName;
449 this.parentAttrName = _parentAttrName;
450 this.childTypeName = _childTypeName;
451 this.childAttrName = _childAttrName;
452 for (final String keyAttribute : _keyAttributes) {
453 this.keyAttributes.add(keyAttribute);
454 }
455 // set the default if necessary
456 if (this.keyAttributes.size() < 1) {
457 this.keyAttributes.add("Name");
458 }
459 }
460
461 /**
462 * Child Type extracted from the child type name.
463 *
464 * @return child type extracted from the child type name
465 * @throws CacheReloadException on error
466 */
467 public Type getChildType()
468 throws CacheReloadException
469 {
470 return Type.get(this.childTypeName);
471 }
472
473 /**
474 * Link Type extracted from the link name.
475 *
476 * @return link type extracted from the link name
477 * @throws CacheReloadException on error
478 */
479 public Type getLinkType()
480 throws CacheReloadException
481 {
482 return Type.get(this.linkName);
483 }
484
485 /**
486 * Getter method for instance variable {@link #keyAttributes}.
487 *
488 * @return value of instance variable {@link #keyAttributes}
489 */
490 public Set<String> getKeyAttributes()
491 {
492 return this.keyAttributes;
493 }
494
495 /**
496 * Getter method for the instance variable {@link #includeChildTypes}.
497 *
498 * @return value of instance variable {@link #includeChildTypes}
499 */
500 public boolean isIncludeChildTypes()
501 {
502 return this.includeChildTypes;
503 }
504
505 /**
506 * Setter method for instance variable {@link #includeChildTypes}.
507 *
508 * @param _includeChildTypes value for instance variable {@link #includeChildTypes}
509 * @return this instance for chaining
510 */
511 public Link setIncludeChildTypes(final boolean _includeChildTypes)
512 {
513 this.includeChildTypes = _includeChildTypes;
514 return this;
515 }
516
517 /**
518 * Returns a string representation with values of all instance variables
519 * of a link.
520 *
521 * @return string representation of this link
522 */
523 @Override
524 public String toString()
525 {
526 return new ToStringBuilder(this).append("linkName", this.linkName).append("parentAttrName",
527 this.parentAttrName).append("childTypeName", this.childTypeName).append("childAttrName",
528 this.childAttrName).toString();
529 }
530 }
531
532 /**
533 * Some links has a order in the database. This means that the connections
534 * must be made in the order they are defined in the XML update file.
535 */
536 protected static class OrderedLink
537 extends AbstractUpdate.Link
538 {
539 /**
540 * @param _linkName name of the link itself
541 * @param _parentAttrName name of the parent attribute in the link
542 * @param _childTypeName name of the child type
543 * @param _childAttrName name of the child attribute in the link
544 */
545 public OrderedLink(final String _linkName,
546 final String _parentAttrName,
547 final String _childTypeName,
548 final String _childAttrName)
549 {
550 super(_linkName, _parentAttrName, _childTypeName, _childAttrName);
551 }
552 }
553
554 /**
555 * Some links has a order in the database. This means that the connections
556 * must be made in the order they are defined in the XML update file.
557 */
558 protected static class UniqueLink
559 extends AbstractUpdate.Link
560 {
561 /**
562 * Link must be unique over a group of links.
563 */
564 private final Set<Link> uniqueGroup = new HashSet<Link>();
565
566 /**
567 * @param _linkName name of the link itself
568 * @param _parentAttrName name of the parent attribute in the link
569 * @param _childTypeName name of the child type
570 * @param _childAttrName name of the child attribute in the link
571 */
572 public UniqueLink(final String _linkName,
573 final String _parentAttrName,
574 final String _childTypeName,
575 final String _childAttrName)
576 {
577 super(_linkName, _parentAttrName, _childTypeName, _childAttrName);
578 }
579
580 /**
581 * @param _link link to be added to the unique group
582 */
583 public void add2UniqueGroup(final Link _link)
584 {
585 this.uniqueGroup.add(_link);
586 }
587
588 /**
589 * Getter method for the instance variable {@link #uniqueGroup}.
590 *
591 * @return value of instance variable {@link #uniqueGroup}
592 */
593 public Set<Link> getUniqueGroup()
594 {
595 return this.uniqueGroup;
596 }
597 }
598
599 /**
600 * Base Definition.
601 */
602 public abstract class AbstractDefinition
603 {
604
605 /**
606 * Expression of this definition if this definition must be installed.
607 *
608 * @see #readXML(List, Map, String) defines this expression
609 * @see #isValidVersion(JexlContext) test this expression
610 */
611 private String expression = null;
612
613 /**
614 * Instance of this definition.
615 */
616 private Instance instance = null;
617
618 /**
619 * The value depending on the attribute name for this definition.
620 *
621 * @see #addValue
622 * @see #getValue
623 */
624 private final Map<String, String> values = new HashMap<String, String>();
625
626 /**
627 * Property value depending on the property name for this definition.
628 *
629 * @see #addProperty.
630 */
631 private final Map<String, String> properties = new HashMap<String, String>();
632
633 /**
634 *
635 */
636 private final Map<AbstractUpdate.Link, Set<LinkInstance>> links = new HashMap<AbstractUpdate.Link,
637 Set<LinkInstance>>();
638
639 /**
640 * Name of attribute by which the search in the database is done. If not
641 * specified (defined to <code>null</code>), the attribute
642 * "UUID" is used.
643 *
644 * @see #searchInstance
645 */
646 private final String searchAttrName;
647
648 /**
649 * list of events.
650 */
651 private final List<Event> events = new ArrayList<Event>();
652
653 /**
654 * Profiles this Definition is activated for.
655 */
656 private final Set<Profile> profiles = new HashSet<Profile>();
657
658 /**
659 * Application Dependencies this Definition is activated for.
660 */
661 private final Map<AppDependency, Boolean> appDependencies = new HashMap<AppDependency, Boolean>();
662
663 /**
664 * Default constructor for the attribute by which the object is searched
665 * is "UUID".
666 */
667 protected AbstractDefinition()
668 {
669 this(null);
670 }
671
672 /**
673 * Constructor defining the search attribute.
674 *
675 * @param _searchAttrName name of attribute by which the object is
676 * searched
677 * @see #searchInstance method using the search attribute
678 * @see #searchAttrName
679 */
680 protected AbstractDefinition(final String _searchAttrName)
681 {
682 this.searchAttrName = _searchAttrName;
683 }
684
685 /**
686 * @param _tags tag to reas
687 * @param _attributes attributes
688 * @param _text text
689 * @throws EFapsException on error
690 */
691 protected void readXML(final List<String> _tags,
692 final Map<String, String> _attributes,
693 final String _text)
694 throws EFapsException
695 {
696 final String value = _tags.get(0);
697 if ("name".equals(value)) {
698 setName(_text);
699 } else if ("property".equals(value)) {
700 this.properties.put(_attributes.get("name"), _text);
701 } else if ("version-expression".equals(value)) {
702 this.expression = _text;
703 } else if ("profiles".equals(value)) {
704 if (_tags.size() > 1) {
705 final String subValue = _tags.get(1);
706 if ("profile".equals(subValue)) {
707 this.profiles.add(Profile.getProfile(_attributes.get("name")));
708 }
709 }
710 } else if ("application-dependencies".equals(value)) {
711 if (_tags.size() > 1) {
712 final String subValue = _tags.get(1);
713 if ("application".equals(subValue)) {
714 final AppDependency dep = AppDependency.getAppDependency(_attributes.get("name"));
715 final Boolean exclude = Boolean.valueOf(_attributes.get("exclude"));
716 this.appDependencies.put(dep, exclude);
717 }
718 }
719 } else {
720 throw new Error("Unknown Tag '" + _tags + "' (file " + AbstractUpdate.this.url + ")");
721 }
722 }
723
724 /**
725 * Evaluates the JEXP expression defined in {@link #expression}. If this
726 * expression returns true, the definition is a valid version and could
727 * be executed.
728 *
729 * @param _jexlContext context used to evaluate JEXL expressions
730 * @return <i>true</i> if the definition is valid
731 * @throws InstallationException if the JEXL expression in {@link #expression}
732 * could not be evaluated
733 * @see #expression
734 */
735 public boolean isValidVersion(final JexlContext _jexlContext)
736 throws InstallationException
737 {
738 boolean exec;
739 try {
740 if (this.expression == null) {
741 final Expression jexlExpr = JEXL.createExpression("version==latest");
742 exec = Boolean.parseBoolean(jexlExpr.evaluate(_jexlContext).toString());
743 } else {
744 final Expression jexlExpr = JEXL.createExpression(this.expression);
745 exec = Boolean.parseBoolean(jexlExpr.evaluate(_jexlContext).toString());
746 }
747 //CHECKSTYLE:OFF
748 } catch (final Exception e) {
749 //CHECKSTYLE:ON
750 throw new InstallationException("isValidVersion.JEXLExpressionNotEvaluatable", e);
751 }
752 return exec;
753 }
754
755 /**
756 * @return true if one of the AppDependencies is met
757 * @throws InstallationException on error
758 */
759 public boolean appDependenciesMet()
760 throws InstallationException
761 {
762 boolean ret;
763 if (this.appDependencies.isEmpty()) {
764 ret = true;
765 } else {
766 ret = false;
767 for (final Entry<AppDependency, Boolean> entry : this.appDependencies.entrySet()) {
768 final boolean met = entry.getKey().isMet();
769 if (met && !entry.getValue() || !met && entry.getValue()) {
770 ret = true;
771 break;
772 }
773 }
774 }
775 return ret;
776 }
777
778 /**
779 * @param _profiles set of profiles as defined by an version
780 * file o via SystemConfiguration for the execution of an CIItem
781 * @param _definitions the list of definitions that are contained in the same CIItem
782 * @return true if this definition must be
783 * @throws InstallationException on error
784 */
785 public boolean isApplyDefault(final Set<Profile> _profiles,
786 final List<AbstractDefinition> _definitions)
787 throws InstallationException
788 {
789 boolean ret = false;
790 // only applies if this definition is marked as the default
791 if (getProfiles().contains(Profile.getDefaultProfile())) {
792 ret = true;
793 // if one of the definitions is enabled by its profile the default must not be applied
794 for (final AbstractDefinition def: _definitions) {
795 if (CollectionUtils.containsAny(_profiles, def.getProfiles())) {
796 ret = false;
797 break;
798 }
799 }
800 }
801 return ret;
802 }
803
804 /**
805 * In case that this Definition does not have a profile assigned,
806 * the Default Profile will be assigned on the first call of this method.
807 * @param _profile Profile to be checked
808 * @return true if this Definition belongs to the given Profile,
809 * else false
810 */
811 public boolean assignedTo(final Profile _profile)
812 {
813 return this.profiles.isEmpty() ? true : this.profiles.contains(_profile);
814 }
815
816 /**
817 * @param _step current update step
818 * @param _allLinkTypes set of all type of links
819 * @throws InstallationException if update failed
820 */
821 protected void updateInDB(final UpdateLifecycle _step,
822 final Set<AbstractUpdate.Link> _allLinkTypes)
823 throws InstallationException
824 {
825 if (_step == UpdateLifecycle.EFAPS_CREATE) {
826 searchInstance();
827
828 // if no instance exists, a new insert must be done
829 if (this.instance == null) {
830 final Insert insert;
831 try {
832 insert = new Insert(getDataModelTypeName());
833 insert.add("UUID", AbstractUpdate.this.uuid);
834 } catch (final EFapsException e) {
835 throw new InstallationException("Initialize for the insert of '"
836 + getDataModelTypeName() + "' with UUID '"
837 + AbstractUpdate.this.uuid + "' failed", e);
838 }
839 createInDB(insert);
840 }
841
842 } else if (_step == UpdateLifecycle.EFAPS_UPDATE
843 && this.instance != null && this.instance.isValid()) {
844 try {
845 final String name = this.values.get("Name");
846 final Update update = new Update(this.instance);
847 if (this.instance.getType().getAttribute("Revision") != null) {
848 update.add("Revision", AbstractUpdate.this.fileRevision);
849 }
850 for (final Map.Entry<String, String> entry : this.values.entrySet()) {
851 update.add(entry.getKey(), entry.getValue());
852 }
853 if (AbstractUpdate.LOG.isInfoEnabled() && name != null) {
854 AbstractUpdate.LOG.info(" Update " + this.instance.getType().getName() + " '" + name + "'");
855 }
856 update.executeWithoutAccessCheck();
857
858 if (_allLinkTypes != null) {
859 for (final Link linkType : _allLinkTypes) {
860 setLinksInDB(this.instance, linkType, this.links.get(linkType));
861 }
862 }
863 setPropertiesInDb(this.instance, this.properties);
864 final List<Instance> eventInstList = new ArrayList<Instance>();
865 for (final Event event : getEvents()) {
866 final Instance eventInst = event.updateInDB(this.instance, getValue("Name"));
867 setPropertiesInDb(eventInst, event.getProperties());
868 eventInstList.add(eventInst);
869 }
870 removeObsoleteEvents(this.instance, eventInstList);
871 } catch (final EFapsException e) {
872 throw new InstallationException("update did not work", e);
873 }
874 }
875 }
876
877 /**
878 * @param _instance Instance that will be checked 4 obsolete events
879 * @param _eventInstList list of valid event Instances
880 * @throws EFapsException on error
881 */
882 protected void removeObsoleteEvents(final Instance _instance,
883 final List<Instance> _eventInstList)
884 throws EFapsException
885 {
886 // check it not first install and only for the objects that are inside the "t_cmabstract" sql table
887 if (CIAdminEvent.Definition.getType().getMainTable() != null
888 && CIAdmin.Abstract.getType().getMainTable() != null
889 && CIAdmin.Abstract.getType().getMainTable().equals(_instance.getType().getMainTable())) {
890 final QueryBuilder queryBldr = new QueryBuilder(CIAdminEvent.Definition);
891 queryBldr.addWhereAttrEqValue(CIAdminEvent.Definition.Abstract, _instance);
892 final InstanceQuery query = queryBldr.getQuery();
893 final List<Instance> instances = query.executeWithoutAccessCheck();
894 final List<?> obsoletes = ListUtils.removeAll(instances, _eventInstList);
895 for (final Object inst : obsoletes) {
896 new Delete((Instance) inst).executeWithoutAccessCheck();
897 }
898 }
899 }
900
901 /**
902 * Search for given data model type. If an attribute name for the search
903 * is defined in {@link #searchAttrName}, the search is done with this
904 * given attribute. If no attribute name is defined (value is
905 * <code>null</code>, the method searches for given UUID. The result is
906 * stored in {@link #instance} (or set to null, if not found).<br/>
907 * The search is only done, if no instance is defined (meaning if
908 * {@link #instance} has no value).
909 *
910 * @see #instance variable in which the search result is stored (and the
911 * search is only done if the value is <code>null</code>)
912 * @see #searchAttrName name of the attribute which them the search is
913 * done
914 * @throws InstallationException if search for the instance failed
915 */
916 protected void searchInstance()
917 throws InstallationException
918 {
919 if (this.instance == null) {
920 try {
921 final QueryBuilder queryBldr = new QueryBuilder(Type.get(getDataModelTypeName()));
922 if (this.searchAttrName == null) {
923 queryBldr.addWhereAttrEqValue("UUID", AbstractUpdate.this.uuid);
924 } else {
925 queryBldr.addWhereAttrEqValue(this.searchAttrName, this.values.get(this.searchAttrName));
926 }
927 final InstanceQuery query = queryBldr.getQuery();
928 query.executeWithoutAccessCheck();
929 if (query.next()) {
930 this.instance = query.getCurrentValue();
931 }
932 } catch (final EFapsException e) {
933 throw new InstallationException("Search for '" + getDataModelTypeName() + "' for '"
934 + (this.searchAttrName == null
935 ? AbstractUpdate.this.uuid
936 : this.values.get(this.searchAttrName))
937 + "' failed", e);
938 }
939 }
940 }
941
942 /**
943 * Inserts current instance defined by <code>_insert</code> into the
944 * eFaps database without any access check.
945 *
946 * @param _insert insert instance
947 * @throws InstallationException if insert failed
948 */
949 protected void createInDB(final Insert _insert)
950 throws InstallationException
951 {
952 try {
953 if (_insert.getInstance().getType().getAttribute("Revision") != null) {
954 _insert.add("Revision", AbstractUpdate.this.fileRevision);
955 }
956 final String name = this.values.get("Name");
957 _insert.add("Name", name == null ? "-" : name);
958 if (AbstractUpdate.LOG.isInfoEnabled()) {
959 AbstractUpdate.LOG.info(" Insert " + _insert.getInstance().getType().getName()
960 + " '" + name + "'");
961 }
962 _insert.executeWithoutAccessCheck();
963 } catch (final EFapsException e) {
964 throw new InstallationException("Insert for '" + _insert.getInstance().getType().getName()
965 + "' '" + this.values.get("Name") + " failed", e);
966 }
967 this.instance = _insert.getInstance();
968 }
969
970 /**
971 * Remove all links of a specific type from a
972 * given object (defined by the instance).
973 *
974 * @param _instance instance for which all links must be removed
975 * @param _linkType type of link which must be removed
976 * @throws EFapsException if existing links could not be removed
977 * (deleted)
978 * @see #setLinksInDB used to remove all links for given instance with a
979 * zero length set of link instances
980 */
981 protected void removeLinksInDB(final Instance _instance,
982 final Link _linkType)
983 throws EFapsException
984 {
985 setLinksInDB(_instance, _linkType, new HashSet<LinkInstance>());
986 }
987
988 /**
989 * Sets the links from this object to the given list of objects (with
990 * the object name) in the eFaps database.
991 *
992 * @param _instance instance for which the links must be defined
993 * @param _linktype type of the link to be updated
994 * @param _links all links of the type _linktype which will be connected
995 * to this instance
996 * @throws EFapsException if links could not be defined
997 */
998 protected void setLinksInDB(final Instance _instance,
999 final Link _linktype,
1000 final Set<LinkInstance> _links)
1001 throws EFapsException
1002 {
1003 if (_links != null) {
1004 final Iterator<LinkInstance> linkIter = _links.iterator();
1005 // 1. search the object to be linked to, if not found remove it
1006 while (linkIter.hasNext()) {
1007 final LinkInstance oneLink = linkIter.next();
1008 final QueryBuilder queryBldr = new QueryBuilder(Type.get(_linktype.childTypeName));
1009 for (final Entry<String, String> entry : oneLink.getKeyAttr2Value().entrySet()) {
1010 queryBldr.addWhereAttrEqValue(entry.getKey(), entry.getValue());
1011 }
1012 final InstanceQuery query = queryBldr.getQuery();
1013 final List<Instance> childInsts = query.executeWithoutAccessCheck();
1014 // only if a child object is found the next steps are done
1015 if (childInsts.size() == 1) {
1016 oneLink.setChildInstance(childInsts.get(0));
1017 // 2. search if a link already exists
1018 final QueryBuilder linkQueryBldr = new QueryBuilder(Type.get(_linktype.linkName));
1019 linkQueryBldr.addWhereAttrEqValue(_linktype.parentAttrName, _instance);
1020 linkQueryBldr.addWhereAttrEqValue(_linktype.childAttrName, oneLink.getChildInstance());
1021 final InstanceQuery linkQuery = linkQueryBldr.getQuery();
1022 final List<Instance> linkInsts = linkQuery.executeWithoutAccessCheck();
1023 if (linkInsts.size() == 1) {
1024 oneLink.setInstance(linkInsts.get(0));
1025 }
1026 } else if (childInsts.size() < 1) {
1027 linkIter.remove();
1028 AbstractUpdate.LOG.error("No object found for link definition {}", oneLink);
1029 } else {
1030 linkIter.remove();
1031 AbstractUpdate.LOG.error("more than one object found for link definition {}", oneLink);
1032 }
1033 }
1034
1035 final List<Instance> childInsts = new ArrayList<Instance>();
1036 for (final LinkInstance oneLink : _links) {
1037 childInsts.add(oneLink.getChildInstance());
1038 }
1039
1040 // 3. look if there are any other links of this type already connected to the parent
1041 // which are not given explicitly and remove them
1042 final QueryBuilder attrQueryBldr = new QueryBuilder(Type.get(_linktype.childTypeName));
1043 final AttributeQuery attrQuery = attrQueryBldr.getAttributeQuery("ID");
1044 attrQuery.setIncludeChildTypes(_linktype.isIncludeChildTypes());
1045 final QueryBuilder queryBldr = new QueryBuilder(Type.get(_linktype.linkName));
1046 queryBldr.addWhereAttrEqValue(_linktype.parentAttrName, _instance);
1047 queryBldr.addWhereAttrInQuery(_linktype.childAttrName, attrQuery);
1048 if (!childInsts.isEmpty()) {
1049 queryBldr.addWhereAttrNotEqValue(_linktype.childAttrName, childInsts.toArray());
1050 }
1051 final InstanceQuery query = queryBldr.getQuery();
1052 query.executeWithoutAccessCheck();
1053 while (query.next()) {
1054 new Delete(query.getCurrentValue()).executeWithoutTrigger();
1055 }
1056
1057 // 4. check if the link must be unique and remove existing links
1058 if (_linktype instanceof UniqueLink) {
1059 // remove also the links from the unique group
1060 for (final Link checkLink : ((UniqueLink) _linktype).getUniqueGroup()) {
1061 final QueryBuilder unGrpAttrQueryBldr = new QueryBuilder(Type.get(checkLink.childTypeName));
1062 final AttributeQuery unGrpAttrQuery = unGrpAttrQueryBldr.getAttributeQuery("ID");
1063 unGrpAttrQuery.setIncludeChildTypes(_linktype.isIncludeChildTypes());
1064 final QueryBuilder unGrpQueryBldr = new QueryBuilder(Type.get(checkLink.linkName));
1065 unGrpQueryBldr.addWhereAttrEqValue(checkLink.parentAttrName, _instance);
1066 unGrpQueryBldr.addWhereAttrInQuery(checkLink.childAttrName, unGrpAttrQuery);
1067 final InstanceQuery unGrpQuery = unGrpQueryBldr.getQuery();
1068 unGrpQuery.executeWithoutAccessCheck();
1069 while (unGrpQuery.next()) {
1070 new Delete(unGrpQuery.getCurrentValue()).executeWithoutTrigger();
1071 }
1072 }
1073 }
1074
1075 // 5. Insert the links which are not existing yet and update given values
1076 for (final LinkInstance oneLink : _links) {
1077 Update update;
1078 boolean exec = false;
1079 if (oneLink.getInstance() == null) {
1080 update = new Insert(_linktype.linkName);
1081 update.add(_linktype.parentAttrName, _instance);
1082 update.add(_linktype.childAttrName, oneLink.getChildInstance());
1083 exec = true;
1084 } else {
1085 update = new Update(oneLink.getInstance());
1086 }
1087 for (final Entry<String, String> entry : oneLink.getValuesMap().entrySet()) {
1088 update.add(entry.getKey(), entry.getValue());
1089 exec = true;
1090 }
1091 if (exec) {
1092 update.executeWithoutAccessCheck();
1093 oneLink.setInstance(update.getInstance());
1094 }
1095 }
1096
1097 // 6. Order if necessary
1098 if (_linktype instanceof OrderedLink) {
1099 final List<Instance> childOrder = new ArrayList<Instance>();
1100 for (final LinkInstance oneLink : _links) {
1101 childOrder.add(oneLink.getChildInstance());
1102 }
1103 final ArrayList<LinkInstance> linkOrder = new ArrayList<LinkInstance>(_links);
1104 Collections.sort(linkOrder, new Comparator<LinkInstance>()
1105 {
1106 @Override
1107 public int compare(final LinkInstance _o1,
1108 final LinkInstance _o2)
1109 {
1110 return Long.valueOf(_o1.getInstance().getId()).compareTo(
1111 Long.valueOf(_o2.getInstance().getId()));
1112 }
1113 });
1114
1115 final Iterator<LinkInstance> sortedIter = linkOrder.iterator();
1116 final Iterator<Instance> childIter = childOrder.iterator();
1117 while (sortedIter.hasNext()) {
1118 final LinkInstance linkInst = sortedIter.next();
1119 final Instance childInst = childIter.next();
1120 if (!linkInst.getChildInstance().equals(childInst)) {
1121 final Update update = new Update(linkInst.getInstance());
1122 update.add(_linktype.childAttrName, childInst);
1123 update.executeWithoutAccessCheck();
1124 }
1125 }
1126 }
1127 }
1128 }
1129
1130 /**
1131 * The properties are only set if the object to update could own
1132 * properties (meaning derived from 'Admin_Abstract').
1133 *
1134 * @param _instance instance for which the properties must be set
1135 * @param _properties new properties to set
1136 * @throws EFapsException if properties could not be set TODO: rework of
1137 * the update algorithm (not always a complete delete and
1138 * and new create is needed)
1139 */
1140 protected void setPropertiesInDb(final Instance _instance,
1141 final Map<String, String> _properties)
1142 throws EFapsException
1143 {
1144
1145 if (_instance.getType().isKindOf(CIAdmin.Abstract.getType())) {
1146 // remove old properties
1147 final QueryBuilder queryBldr = new QueryBuilder(CIAdminCommon.Property);
1148 queryBldr.addWhereAttrEqValue(CIAdminCommon.Property.Abstract, _instance.getId());
1149 final InstanceQuery query = queryBldr.getQuery();
1150 query.executeWithoutAccessCheck();
1151 while (query.next()) {
1152 new Delete(query.getCurrentValue()).executeWithoutAccessCheck();
1153 }
1154
1155 // add current properties
1156 if (_properties != null) {
1157 for (final Map.Entry<String, String> entry : _properties.entrySet()) {
1158 final Insert insert = new Insert("Admin_Common_Property");
1159 insert.add("Name", entry.getKey());
1160 insert.add("Value", entry.getValue());
1161 insert.add("Abstract", "" + _instance.getId());
1162 insert.executeWithoutAccessCheck();
1163 }
1164 }
1165 }
1166 }
1167
1168 /**
1169 * @param _link link type
1170 * @param _linkinstance name of the object which is linked to and values
1171 * in the link itself (or null)
1172 */
1173 protected void addLink(final Link _link,
1174 final LinkInstance _linkinstance)
1175 {
1176 Set<LinkInstance> oneLink = this.links.get(_link);
1177 if (oneLink == null) {
1178 if (_link instanceof OrderedLink) {
1179 oneLink = new LinkedHashSet<LinkInstance>();
1180 } else {
1181 oneLink = new HashSet<LinkInstance>();
1182 }
1183 this.links.put(_link, oneLink);
1184 }
1185 oneLink.add(_linkinstance);
1186 }
1187
1188 /**
1189 * @param _linkType type the links are wanted for
1190 * @return Set of links
1191 */
1192 public Set<LinkInstance> getLinks(final Link _linkType)
1193 {
1194 return this.links.get(_linkType);
1195 }
1196
1197 /**
1198 * @param _name name of the attribute
1199 * @param _value value of the attribute
1200 * @see #values
1201 */
1202 protected void addValue(final String _name,
1203 final String _value)
1204 {
1205 this.values.put(_name, _value);
1206 }
1207
1208 /**
1209 * @param _name name of the attribute
1210 * @return value of the set attribute value in this definition
1211 * @see #values
1212 */
1213 public String getValue(final String _name)
1214 {
1215 return this.values.get(_name);
1216 }
1217
1218 /**
1219 * @param _name Name ot set
1220 */
1221 protected void setName(final String _name)
1222 {
1223 addValue("Name", _name);
1224 }
1225
1226 /**
1227 * Adds a trigger <code>_event</code> to this definition.
1228 *
1229 * @param _event trigger event to add
1230 * @see #events
1231 */
1232 protected void addEvent(final Event _event)
1233 {
1234 this.events.add(_event);
1235 }
1236
1237 /**
1238 * This is the getter method for the instance variable {@link #events}.
1239 *
1240 * @return value of instance variable {@link #events}
1241 */
1242 public List<Event> getEvents()
1243 {
1244 return this.events;
1245 }
1246
1247 /**
1248 * This is the getter method for the instance variable
1249 * {@link #properties}.
1250 *
1251 * @return value of instance variable {@link #properties}
1252 */
1253 public Map<String, String> getProperties()
1254 {
1255 return this.properties;
1256 }
1257
1258 /**
1259 * This is the getter method for the instance variable {@link #instance}.
1260 *
1261 * @return value of instance variable {@link #instance}
1262 */
1263 protected Instance getInstance()
1264 {
1265 return this.instance;
1266 }
1267
1268 /**
1269 * This is the setter method for the instance variable {@link #instance}.
1270 *
1271 * @param _instance value for instance variable {@link #instance}
1272 */
1273 protected void setInstance(final Instance _instance)
1274 {
1275 this.instance = _instance;
1276 }
1277
1278 /**
1279 * Getter method for the instance variable {@link #profiles}.
1280 *
1281 * @return value of instance variable {@link #profiles}
1282 */
1283 public Set<Profile> getProfiles()
1284 {
1285 return this.profiles;
1286 }
1287
1288 /**
1289 * Returns a string representation with values of all instance variables
1290 * of a definition.
1291 *
1292 * @return string representation of this definition of an access type
1293 * update
1294 */
1295 @Override
1296 public String toString()
1297 {
1298 return ToStringBuilder.reflectionToString(this);
1299 }
1300 }
1301 }