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          * &quot;UUID&quot; 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 &quot;UUID&quot;.
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 }