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.version;
22  
23  import java.io.File;
24  import java.io.IOException;
25  import java.net.MalformedURLException;
26  import java.net.URL;
27  import java.net.URLClassLoader;
28  import java.util.ArrayList;
29  import java.util.Collections;
30  import java.util.Comparator;
31  import java.util.Enumeration;
32  import java.util.HashMap;
33  import java.util.HashSet;
34  import java.util.List;
35  import java.util.Map;
36  import java.util.Map.Entry;
37  import java.util.Set;
38  import java.util.TreeSet;
39  
40  import org.apache.commons.digester3.Digester;
41  import org.apache.commons.digester3.annotations.FromAnnotationsRuleModule;
42  import org.apache.commons.digester3.annotations.rules.BeanPropertySetter;
43  import org.apache.commons.digester3.annotations.rules.CallMethod;
44  import org.apache.commons.digester3.annotations.rules.CallParam;
45  import org.apache.commons.digester3.annotations.rules.ObjectCreate;
46  import org.apache.commons.digester3.annotations.rules.SetNext;
47  import org.apache.commons.digester3.annotations.rules.SetProperty;
48  import org.apache.commons.digester3.binder.DigesterLoader;
49  import org.apache.commons.lang3.builder.ToStringBuilder;
50  import org.apache.tools.ant.DirectoryScanner;
51  import org.efaps.admin.datamodel.Type;
52  import org.efaps.admin.program.esjp.EFapsClassLoader;
53  import org.efaps.admin.runlevel.RunLevel;
54  import org.efaps.ci.CIAdminCommon;
55  import org.efaps.db.Context;
56  import org.efaps.db.Insert;
57  import org.efaps.db.InstanceQuery;
58  import org.efaps.db.QueryBuilder;
59  import org.efaps.update.FileType;
60  import org.efaps.update.Install;
61  import org.efaps.update.Profile;
62  import org.efaps.update.schema.program.esjp.ESJPCompiler;
63  import org.efaps.update.schema.program.staticsource.AbstractStaticSourceCompiler;
64  import org.efaps.update.util.InstallationException;
65  import org.efaps.util.EFapsException;
66  import org.slf4j.Logger;
67  import org.slf4j.LoggerFactory;
68  
69  /**
70   * @author The eFaps Team
71   * @version $Id$
72   */
73  @ObjectCreate(pattern = "install")
74  public final class Application
75  {
76  
77      /**
78       * Logging instance used to give logging information of this class.
79       */
80      private static final Logger LOG = LoggerFactory.getLogger(Application.class);
81  
82      /**
83       * Default Mapping of a a file extension to a Type for import and update.
84       */
85      private static final Map<String, String> DEFAULT_TYPE_MAPPING = new HashMap<String, String>();
86      static {
87          Application.DEFAULT_TYPE_MAPPING.put("bpmn2", FileType.BPM.getType());
88          Application.DEFAULT_TYPE_MAPPING.put("css", FileType.CSS.getType());
89          Application.DEFAULT_TYPE_MAPPING.put("java", FileType.JAVA.getType());
90          Application.DEFAULT_TYPE_MAPPING.put("js", FileType.JS.getType());
91          Application.DEFAULT_TYPE_MAPPING.put("jrxml", FileType.JRXML.getType());
92          Application.DEFAULT_TYPE_MAPPING.put("wiki", FileType.WIKI.getType());
93          Application.DEFAULT_TYPE_MAPPING.put("xml", FileType.XML.getType());
94          Application.DEFAULT_TYPE_MAPPING.put("xsl", FileType.XSL.getType());
95      }
96  
97      /**
98       * Default list of includes used to evaluate the files to copy.
99       *
100      * @see #getFiles
101      */
102     private static final Set<String> DEFAULT_INCLUDES = new HashSet<String>();
103     static {
104         Application.DEFAULT_INCLUDES.add("**/*.bpmn2");
105         Application.DEFAULT_INCLUDES.add("**/*.css");
106         Application.DEFAULT_INCLUDES.add("**/*.java");
107         Application.DEFAULT_INCLUDES.add("**/*.js");
108         Application.DEFAULT_INCLUDES.add("**/*.jrxml");
109         Application.DEFAULT_INCLUDES.add("**/*.wiki");
110         Application.DEFAULT_INCLUDES.add("**/*.xml");
111         Application.DEFAULT_INCLUDES.add("**/*.xsl");
112     }
113 
114     /**
115      * Default list of excludes used to evaluate the files to copy.
116      *
117      * @see #getFiles
118      */
119     private static final Set<String> DEFAULT_EXCLUDES = new HashSet<String>();
120     static {
121         Application.DEFAULT_EXCLUDES.add("**/versions.xml");
122         Application.DEFAULT_EXCLUDES.add("**/package-info.java");
123         Application.DEFAULT_EXCLUDES.add("**/.svn/**");
124     }
125 
126     /**
127      * Stores the name of the application.
128      *
129      * @see #setApplication
130      */
131     @BeanPropertySetter(pattern = "install/application")
132     private String application = null;
133 
134 
135     /**
136      * Stores all versions of this application which must be installed.
137      *
138      * @see #getVersions()
139      */
140     private final Set<ApplicationVersion> versions = new TreeSet<ApplicationVersion>();
141 
142     /**
143      * Install instance holding all XML definition / update files.
144      *
145      * @see #addURL(URL, String)
146      */
147     private final Install install = new Install();
148 
149     /**
150      * Caches not stores versions (because if the kernel install is made, the
151      * version could not be updated till the SQL tables and the data model is
152      * already installed and the cache is reloaded).
153      */
154     private final List<Long> notStoredVersions = new ArrayList<Long>();
155 
156     /**
157      * Stores the highest or maximum number of the versions to be installed.
158      */
159     private Long maxVersion;
160 
161     /**
162      * Project class path.
163      *
164      * @see #Application(URL, List)
165      * @see #getClassPathElements()
166      */
167     private List<String> classpathElements;
168 
169     /**
170      * Dependencies to other applications for this application ordered.
171      */
172     private final List<Dependency> dependencies = new ArrayList<Dependency>();
173 
174     /**
175      * Root URL where the source files are located. Could be a file directory (
176      * for local installation) or a jar file.
177      *
178      * @see #Application(URL, List)
179      * @see #getRootUrl()
180      */
181     private URL rootUrl;
182 
183     /**
184      * Stores the name of the rootPackage.
185      */
186     @SetProperty(pattern = "install/rootPackage", attributeName = "name")
187     private String rootPackageName;
188 
189     /**
190      * Used in combination with the digester.
191      */
192     private Map<String, String> tmpElements = new HashMap<String, String>();
193 
194     /**
195      * Initializes the {@link #rootUrl root URL} of this application.
196      *
197      * @param _rootUrl root URL of the source
198      * @param _classpathElements elements of the class path
199      * @see #rootUrl
200      */
201     private Application(final URL _rootUrl,
202                         final List<String> _classpathElements)
203     {
204         this.rootUrl = _rootUrl;
205         this.classpathElements = _classpathElements;
206     }
207 
208     /**
209      * Constructor used by Digester.
210      */
211     public Application()
212     {
213     }
214 
215     /**
216      * <code>null</code> is returned, of the version file could not be opened
217      * and read.
218      *
219      * @param _versionUrl URL of the version file which defines the application
220      * @param _rootUrl root URL where the source files are located (for local
221      *            files); URL of the class file (if source is in a Jar file)
222      * @param _classpathElements elements of the class path
223      * @return application instance with all version information
224      * @throws InstallationException if version XML file could not be parsed
225      */
226     public static Application getApplication(final URL _versionUrl,
227                                              final URL _rootUrl,
228                                              final List<String> _classpathElements)
229         throws InstallationException
230     {
231         Application appl = null;
232         try {
233             final DigesterLoader loader = DigesterLoader.newLoader(new FromAnnotationsRuleModule()
234             {
235                 @Override
236                 protected void configureRules()
237                 {
238                     bindRulesFrom(Application.class);
239                 }
240             });
241             final Digester digester = loader.newDigester();
242             appl = (Application) digester.parse(_versionUrl);
243             appl.rootUrl = _rootUrl;
244             appl.classpathElements = _classpathElements;
245             for (final Entry<String, String> entry : appl.tmpElements.entrySet()) {
246                 appl.addURL(new URL(_rootUrl, entry.getKey()), entry.getValue());
247             }
248             appl.tmpElements = null;
249             Collections.sort(appl.dependencies, new Comparator<Dependency>()
250             {
251 
252                 @Override
253                 public int compare(final Dependency _dependency0,
254                                    final Dependency _dependency1)
255                 {
256                     return _dependency0.getOrder().compareTo(_dependency1.getOrder());
257                 }
258             });
259             for (final ApplicationVersion applVers : appl.getVersions()) {
260                 applVers.setApplication(appl);
261                 appl.setMaxVersion(applVers.getNumber());
262             }
263 
264         } catch (final IOException e) {
265             if (e.getCause() instanceof InstallationException) {
266                 throw (InstallationException) e.getCause();
267             } else {
268                 throw new InstallationException("Could not open / read version file '" + _versionUrl + "'");
269             }
270           //CHECKSTYLE:OFF
271         } catch (final Exception e) {
272           //CHECKSTYLE:ON
273             throw new InstallationException("Error while parsing file '" + _versionUrl + "'", e);
274         }
275         return appl;
276     }
277 
278     /**
279      * Returns the application definition read from a source directory.
280      *
281      * @param _versionFile          version file which defines the application
282      * @param _classpathElements    class path elements (required to compile)
283      * @param _eFapsDir             root directory with the XML installation
284      *                              files
285      * @param _outputDir            directory used as target for generated code
286      * @param _includes list of includes; if <code>null</code>
287      *            {@link #DEFAULT_INCLUDES} are used
288      * @param _excludes list of excludes; if <code>null</code>
289      *            {@link #DEFAULT_EXCLUDES} are used
290      * @param _file2typeMapping mapping of file extension to type; if
291      *            <code>null</code> {@link #DEFAULT_TYPE_MAPPING} is used
292      * @return application instance with all version information
293      * @throws InstallationException if version file could not be read or opened
294      */
295     public static Application getApplicationFromSource(final File _versionFile,
296                                                        final List<String> _classpathElements,
297                                                        final File _eFapsDir,
298                                                        final File _outputDir,
299                                                        final List<String> _includes,
300                                                        final List<String> _excludes,
301                                                        final Map<String, String> _file2typeMapping)
302         throws InstallationException
303     {
304         final Map<String, String> file2typeMapping = _file2typeMapping == null
305                         ? Application.DEFAULT_TYPE_MAPPING
306                         : _file2typeMapping;
307         final Application appl;
308         try {
309             appl = Application.getApplication(_versionFile.toURI().toURL(),
310                                 _eFapsDir.toURI().toURL(),
311                                 _classpathElements);
312 
313             for (final String fileName : Application.getFiles(_eFapsDir, _includes, _excludes)) {
314                 final String type = file2typeMapping.get(fileName.substring(fileName.lastIndexOf(".") + 1));
315                 appl.addURL(new File(_eFapsDir, fileName).toURI().toURL(), type);
316             }
317             if (_outputDir.exists()) {
318                 for (final String fileName : Application.getFiles(_outputDir, _includes, _excludes)) {
319                     final String type = file2typeMapping.get(fileName.substring(fileName.lastIndexOf(".") + 1));
320                     appl.addURL(new File(_outputDir, fileName).toURI().toURL(), type);
321                 }
322             }
323         } catch (final IOException e) {
324             throw new InstallationException("Could not open / read version file " + "'" + _versionFile + "'", e);
325           //CHECKSTYLE:OFF
326         } catch (final Exception e) {
327           //CHECKSTYLE:ON
328             throw new InstallationException("Read version file '" + _versionFile + "' failed", e);
329         }
330         return appl;
331     }
332 
333     /**
334      * Uses the <code>_includes</code> and <code>_excludes</code> together with
335      * the root directory <code>_eFapsDir</code> to get all related and matched
336      * files.
337      *
338      * @param _eFapsDir root directory where the file are located
339      * @param _includes defines includes; if not specified the default value is
340      *            <code>**&#x002f;*.xml</code>
341      * @param _excludes defines excludes; if not specified the default value is
342      *            <code>**&#x002f;version.xml</code>
343      * @return array of file names
344      * @see #DEFAULT_INCLUDES
345      * @see #DEFAULT_EXCLUDES
346      */
347     protected static String[] getFiles(final File _eFapsDir,
348                                        final List<String> _includes,
349                                        final List<String> _excludes)
350     {
351         final DirectoryScanner ds = new DirectoryScanner();
352         final String[] included = _includes == null
353                         ? Application.DEFAULT_INCLUDES.toArray(new String[Application.DEFAULT_INCLUDES.size()])
354                         : _includes.toArray(new String[_includes.size()]);
355         final String[] excluded = _excludes == null
356                         ? Application.DEFAULT_EXCLUDES.toArray(new String[Application.DEFAULT_EXCLUDES.size()])
357                         : _excludes.toArray(new String[_excludes.size()]);
358         ds.setIncludes(included);
359         ds.setExcludes(excluded);
360         ds.setBasedir(_eFapsDir.toString());
361         ds.setCaseSensitive(true);
362         ds.scan();
363 
364         return ds.getIncludedFiles();
365     }
366 
367     /**
368      * Method to get the applications from the class path.
369      *
370      * @param _application searched application in the class path
371      * @param _classpath class path (list of the complete class path)
372      * @return List of applications
373      * @throws InstallationException if the install.xml file in the class path
374      *             could not be accessed
375      */
376     public static Application getApplicationFromClassPath(final String _application,
377                                                           final List<String> _classpath)
378         throws InstallationException
379     {
380         return Application.getApplicationsFromClassPath(_application, _classpath).get(_application);
381     }
382 
383 
384     /**
385      * Method to get all applications from the class path.
386      *
387      * @param _application searched application in the class path
388      * @param _classpath class path (list of the complete class path)
389      * @return List of applications
390      * @throws InstallationException if the install.xml file in the class path
391      *             could not be accessed
392      */
393     public static Map<String, Application> getApplicationsFromClassPath(final String _application,
394                                                                         final List<String> _classpath)
395         throws InstallationException
396     {
397         final Map<String, Application> appls = new HashMap<String, Application>();
398         try {
399             final ClassLoader parent = Application.class.getClassLoader();
400             final List<URL> urls = new ArrayList<>();
401             for (final String pathElement : _classpath) {
402                 urls.add(new File(pathElement).toURI().toURL());
403             }
404             final URLClassLoader cl = URLClassLoader.newInstance(urls.toArray(new URL[urls.size()]), parent);
405             // get install application (read from all install xml files)
406             final Enumeration<URL> urlEnum = cl.getResources("META-INF/efaps/install.xml");
407             while (urlEnum.hasMoreElements()) {
408                 // TODO: why class path?
409                 final URL url = urlEnum.nextElement();
410                 final Application appl = Application.getApplication(url, new URL(url, "../../../"), _classpath);
411                 appls.put(appl.getApplication(), appl);
412             }
413         } catch (final IOException e) {
414             throw new InstallationException("Could not access the install.xml file "
415                             + "(in path META-INF/efaps/ path of each eFaps install jar).", e);
416         }
417         return appls;
418     }
419 
420     /**
421      * Returns the application read from given JAR file <code>_jarFile</code>.
422      *
423      * @param _jarFile JAR file with the application to install
424      * @param _classpath class path (required to compile)
425      * @return application instance
426      * @throws InstallationException if application could not be fetched from
427      *             the JAR file or the version XML file could not be parsed
428      */
429     public static Application getApplicationFromJarFile(final File _jarFile,
430                                                         final List<String> _classpath)
431         throws InstallationException
432     {
433         try {
434             final URL url = new URL("jar", null, 0, _jarFile.toURI().toURL().toString() + "!/");
435             final URL url2 = new URL(url, "/META-INF/efaps/install.xml");
436             return Application.getApplication(url2, new URL(url2, "../../../"), _classpath);
437         } catch (final IOException e) {
438             throw new InstallationException("URL could not be parsed", e);
439         }
440     }
441 
442     /**
443      * Compiles the ESJP's and all Cascade Styles Sheets within eFaps.
444      *
445      * @param _userName name of logged in user for which the compile is done
446      *            (could be also <code>null</code>)
447      * @param _classpath class path elements
448      * @param _addRuntimeClassPath must the classpath from the runtime be added
449      *                  to the classpath also
450      * @throws InstallationException if reload cache of compile failed
451      * @see #compileAll(String)
452      */
453     public static void compileAll(final String _userName,
454                                   final List<String> _classpath,
455                                   final boolean _addRuntimeClassPath)
456         throws InstallationException
457     {
458         new Application((URL) null, _classpath).compileAll(_userName, _addRuntimeClassPath);
459     }
460 
461     /**
462      * Compiles the ESJP's and all Cascade Styles Sheets within eFaps.
463      *
464      * @param _userName name of logged in user for which the compile is done
465      *            (could be also <code>null</code>)
466      * @param _addRuntimeClassPath must the classpath from the runtime be added
467      *                  to the classpath also
468      * @throws InstallationException if reload cache of compile failed
469      */
470     public void compileAll(final String _userName,
471                            final boolean _addRuntimeClassPath)
472         throws InstallationException
473     {
474         if (Application.LOG.isInfoEnabled()) {
475             Application.LOG.info("..Compiling");
476         }
477 
478         reloadCache();
479 
480         try {
481             Context.begin(_userName);
482             try {
483                 new ESJPCompiler(getClassPathElements()).compile(null, _addRuntimeClassPath);
484             } catch (final InstallationException e) {
485                 Application.LOG.error(" error during compilation of ESJP.");
486             }
487             AbstractStaticSourceCompiler.compileAll(this.classpathElements);
488             Context.commit();
489         } catch (final EFapsException e) {
490             throw new InstallationException("Compile failed", e);
491         }
492     }
493 
494     /**
495      * Installs current application including existing {@link #dependencies}.
496      *
497      * @param _userName name of logged in user
498      * @param _password password of logged in user
499      * @param _profiles set of profile to be applied
500      * @throws InstallationException for all cases the installation failed
501      * @see #install(String, String, boolean)
502      */
503     public void install(final String _userName,
504                         final String _password,
505                         final Set<Profile> _profiles)
506         throws InstallationException
507     {
508         this.install(_userName, _password, _profiles, null);
509     }
510 
511     /**
512      * Installs current application including existing {@link #dependencies}.
513      *
514      * @param _userName name of logged in user
515      * @param _password password of logged in user
516      * @param _profiles set of profile to be applied
517      * @param _compile  compile during the install/ if null the setting from the version.xml applies
518      * @throws InstallationException for all cases the installation failed
519      * @see #install(String, String, boolean)
520      */
521     public void install(final String _userName,
522                         final String _password,
523                         final Set<Profile> _profiles,
524                         final Boolean _compile)
525         throws InstallationException
526     {
527         this.install(_userName, _password, _profiles, _compile, true);
528     }
529 
530     /**
531      * For each version in {@link #versions} is tested, if it is already
532      * installed. If not already installed, the version is installed. Only if
533      * <code>_withDependency</code> is defined, also the {@link #dependencies}
534      * are installed.
535      *
536      * @param _userName name of the installation user
537      * @param _password password of the installation user
538      * @param _profiles set of profile to be applied
539      * @param _compile  compile during the install/ if null the setting from the version.xml applies
540      * @param _withDependency must the dependency also installed?
541      * @throws InstallationException if installation failed
542      */
543     protected void install(final String _userName,
544                            final String _password,
545                            final Set<Profile> _profiles,
546                            final Boolean _compile,
547                            final boolean _withDependency)
548         throws InstallationException
549     {
550 
551         // install dependency if required
552         if (_withDependency) {
553             for (final Dependency dependency : this.dependencies) {
554                 dependency.resolve();
555                 final Application appl = Application.getApplicationFromJarFile(
556                                 dependency.getJarFile(), this.classpathElements);
557                 appl.install(_userName, _password, dependency.getProfiles(), _compile, false);
558             }
559         }
560 
561         // reload cache (if possible)
562         reloadCache();
563 
564         // load latest installed versions
565         final Map<String, Integer> latestVersions;
566         try {
567             Context.begin();
568             EFapsClassLoader.getOfflineInstance(getClass().getClassLoader());
569             latestVersions = this.install.getLatestVersions();
570             Context.rollback();
571         } catch (final EFapsException e) {
572             throw new InstallationException("Could not get information about installed versions", e);
573         }
574         final Integer latestVersion = latestVersions.get(this.application);
575 
576         Application.LOG.info("Install application '" + this.application + "'");
577 
578         for (final ApplicationVersion version : this.versions) {
579             Application.LOG.info("Check version '{}'", version.getNumber());
580             if (_compile != null) {
581                 version.setCompile(_compile);
582             }
583             if (latestVersion != null && version.getNumber() < latestVersion) {
584                 if (Application.LOG.isInfoEnabled()) {
585                     Application.LOG.info("Version " + version.getNumber() + " already installed");
586                 }
587             } else {
588                 if (Application.LOG.isInfoEnabled()) {
589                     Application.LOG.info("Starting installation of version " + version.getNumber());
590                     final String desc = version.getDescription();
591                     if (!"".equals(desc)) {
592                         Application.LOG.info(desc);
593                     }
594                 }
595                 try {
596                     version.install(this.install, getLastVersion().getNumber(), _profiles, _userName, _password);
597                   //CHECKSTYLE:OFF
598                 } catch (final Exception e) {
599                   //CHECKSTYLE:ON
600                     throw new InstallationException("Installation failed", e);
601                 }
602                 storeVersion(_userName, version.getNumber());
603 
604                 if (Application.LOG.isInfoEnabled()) {
605                     Application.LOG.info("Finished installation of version " + version.getNumber());
606                 }
607             }
608         }
609 
610         // reload cache (if possible)
611         reloadCache();
612     }
613 
614     /**
615      * Updates the last installed version.
616      *
617      * @param _userName name of logged in user
618      * @param _password password of logged in user
619      * @param _profiles Profiles to be applied
620      * @throws Exception on error
621      */
622     public void updateLastVersion(final String _userName,
623                                   final String _password,
624                                   final Set<Profile> _profiles)
625         throws Exception
626     {
627         // reload cache (if possible)
628         reloadCache();
629 
630         // load installed versions
631         Context.begin();
632         EFapsClassLoader.getOfflineInstance(getClass().getClassLoader());
633         final Map<String, Integer> latestVersions = this.install.getLatestVersions();
634         Context.rollback();
635         final long latestVersion = latestVersions.get(this.application);
636 
637         final ApplicationVersion version = getLastVersion();
638         if (version.getNumber() == latestVersion) {
639             Application.LOG.info("Update version '{}' of application '{}' ", version.getNumber(), this.application);
640 
641             version.install(this.install, version.getNumber(), _profiles, _userName, _password);
642 
643             Application.LOG.info("Finished update of version '{}'", version.getNumber());
644         } else {
645             Application.LOG.error("Version {}' of application '{}'  not installed and could not updated!",
646                             version.getNumber(), this.application);
647         }
648     }
649 
650     /**
651      * Store for this application that the version is already installed. If data
652      * model in the local type cache is not loaded (because, e.g., it is a new
653      * kernel install), the version numbers are cached.<br/>
654      * The first time, the version type could be get from the type cache, all
655      * cached versions are stored in eFaps.
656      *
657      * @param _userName logged in user name
658      * @param _version version id to store
659      * @throws InstallationException if version could not be stored
660      */
661     protected void storeVersion(final String _userName,
662                                 final Long _version)
663         throws InstallationException
664     {
665         try {
666             Context.begin(_userName);
667             final Type versionType = CIAdminCommon.Version.getType();
668             if (versionType != null) {
669                 // store cached versions
670                 for (final Long version : this.notStoredVersions) {
671                     final Insert insert = new Insert(versionType);
672                     insert.add(CIAdminCommon.Version.Name, this.application);
673                     insert.add(CIAdminCommon.Version.Revision, version);
674                     insert.execute();
675                 }
676                 this.notStoredVersions.clear();
677 
678                 final QueryBuilder queryBldr = new QueryBuilder(CIAdminCommon.Version);
679                 queryBldr.addWhereAttrEqValue(CIAdminCommon.Version.Name, this.application);
680                 queryBldr.addWhereAttrEqValue(CIAdminCommon.Version.Revision, _version);
681                 final InstanceQuery query = queryBldr.getQuery();
682                 query.execute();
683                 if (!query.next()) {
684                     // store current version
685                     final Insert insert = new Insert(CIAdminCommon.Version);
686                     insert.add(CIAdminCommon.Version.Name, this.application);
687                     insert.add(CIAdminCommon.Version.Revision, _version);
688                     insert.execute();
689                 }
690             } else {
691                 // if version could not be stored, cache the version information
692                 this.notStoredVersions.add(_version);
693             }
694             Context.commit();
695         } catch (final EFapsException e) {
696             throw new InstallationException("Update of the version information failed", e);
697         }
698     }
699 
700     /**
701      *
702      * @param _dependency  dependency
703      */
704     @SetNext
705     public void addDependency(final Dependency _dependency)
706     {
707         this.dependencies.add(_dependency);
708     }
709 
710     /**
711      * Reloads the eFaps cache.
712      *
713      * @throws InstallationException if reload of the cache failed
714      */
715     protected void reloadCache()
716         throws InstallationException
717     {
718         try {
719             Context.begin();
720             if (RunLevel.isInitialisable()) {
721                 RunLevel.init("shell");
722                 RunLevel.execute();
723             }
724             Context.rollback();
725         } catch (final EFapsException e) {
726             throw new InstallationException("Reload cache failed", e);
727         }
728     }
729 
730     /**
731      * Adds a n ew application version to this application which should be
732      * installed.
733      *
734      * @param _version new application version to add
735      */
736     @SetNext
737     public void addVersion(final ApplicationVersion _version)
738     {
739         this.versions.add(_version);
740     }
741 
742     /**
743      * Returns the last application version which must be installed.
744      *
745      * @return last application version to install
746      */
747     public ApplicationVersion getLastVersion()
748     {
749         return (ApplicationVersion) this.versions.toArray()[this.versions.size() - 1];
750     }
751 
752     /**
753      * Setter method for instance variable {@link #application}.
754      *
755      * @param _application value for instance variable {@link #application}
756      */
757 
758     public void setApplication(final String _application)
759     {
760         this.application = _application;
761     }
762 
763 
764     /**
765      * Adds a new URL with the XML definition file.
766      *
767      * @param _url url of XML definition files used to install
768      * @param _type type of the URL
769      * @see #install(String, String)
770      */
771     public void addURL(final URL _url,
772                        final String _type)
773     {
774         this.install.addFile(_url, _type);
775     }
776 
777     /**
778      * Searches for the given file name (parameter _classPathFile) in the class
779      * path and adds them as URL to the list of XML installation / update /
780      * definition files ({@link #install}).
781      *
782      * @param _classPathFile file name from the class path to add
783      * @param _type             type of the file to be added
784      * @throws MalformedURLException on error with the URL
785      * @see #addURL(URL, String)
786      */
787     @CallMethod(pattern = "install/files/file")
788     public void addClassPathFile(
789                         @CallParam(pattern = "install/files/file", attributeName = "name") final String _classPathFile,
790                         @CallParam(pattern = "install/files/file", attributeName = "type") final String _type)
791         throws MalformedURLException
792     {
793         this.tmpElements.put(_classPathFile, _type);
794     }
795 
796     /**
797      * Getter method for the instance variable {@link #install}.
798      *
799      * @return value of instance variable {@link #install}
800      */
801     public Install getInstall()
802     {
803         return this.install;
804     }
805 
806     /**
807      * Getter method for the instance variable {@link #dependencies}.
808      *
809      * @return value of instance variable {@link #dependencies}
810      */
811     public List<Dependency> getDependencies()
812     {
813         return this.dependencies;
814     }
815 
816     /**
817      * This is the getter method for instance variable {@link #application}.
818      *
819      * @return value of instance variable {@link #application}
820      * @see #application
821      */
822     public String getApplication()
823     {
824         return this.application;
825     }
826 
827     /**
828      * Returns all class path elements required to compile.
829      *
830      * @return class path elements
831      * @see #classpathElements
832      */
833     public List<String> getClassPathElements()
834     {
835         return this.classpathElements;
836     }
837 
838     /**
839      * Returns the root URL where the source is located. For local sources it is
840      * an URL to a directory, for a Jar file it is the URL to the Jar file.
841      *
842      * @return value of instance variable {@link #rootUrl}
843      */
844     public URL getRootUrl()
845     {
846         return this.rootUrl;
847     }
848 
849     /**
850      * This is the getter method for instance variable {@link #versions}.
851      *
852      * @return value of instance variable {@link #versions}
853      * @see #versions
854      */
855     public Set<ApplicationVersion> getVersions()
856     {
857         return this.versions;
858     }
859 
860     /**
861      * This is the setter method for the instance variable {@link #maxVersion}.
862      *
863      * @param _maxVersion the maxVersion to set
864      */
865     public void setMaxVersion(final Long _maxVersion)
866     {
867         this.maxVersion = _maxVersion;
868     }
869 
870     /**
871      * This is the getter method for the instance variable {@link #maxVersion}.
872      *
873      * @return value of instance variable {@link #maxVersion}
874      */
875     public Long getMaxVersion()
876     {
877         return this.maxVersion;
878     }
879 
880     /**
881      * Setter method for instance variable {@link #rootPackageName}.
882      *
883      * @param _rootPackageName value for instance variable
884      *            {@link #rootPackageName}
885      */
886     public void setRootPackageName(final String _rootPackageName)
887     {
888         this.rootPackageName = _rootPackageName;
889     }
890 
891     /**
892      * Getter method for instance variable {@link #rootPackageName}.
893      *
894      * @return value of instance variable {@link #rootPackageName}
895      */
896     public String getRootPackageName()
897     {
898         return this.rootPackageName;
899     }
900 
901     /**
902      * Returns a string representation with values of all instance variables.
903      *
904      * @return string representation of this Application
905      */
906     @Override
907     public String toString()
908     {
909         return new ToStringBuilder(this)
910                         .append("application", this.application)
911                         .append("dependencies", this.dependencies)
912                         .append("versions", this.versions)
913                         .append("install", this.install)
914                         .toString();
915     }
916 }