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.schema.program;
22  
23  import java.io.ByteArrayInputStream;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.InputStreamReader;
27  import java.io.Reader;
28  import java.io.UnsupportedEncodingException;
29  import java.net.URL;
30  import java.util.UUID;
31  
32  import org.efaps.ci.CIAdminProgram;
33  import org.efaps.ci.CIType;
34  import org.efaps.db.Checkin;
35  import org.efaps.db.Insert;
36  import org.efaps.db.Instance;
37  import org.efaps.db.InstanceQuery;
38  import org.efaps.db.QueryBuilder;
39  import org.efaps.update.util.InstallationException;
40  import org.efaps.util.EFapsException;
41  
42  /**
43   * Class used to import programs into eFaps.
44   *
45   * @author The eFaps Team
46   * @version $Id$
47   */
48  public abstract class AbstractSourceImporter
49  {
50      /**
51       * Defines the encoding of source code within eFaps.
52       *
53       * @see #readCode()
54       * @see #newCodeInputStream()
55       */
56      protected static final String ENCODING = "UTF8";
57  
58      /**
59       * URL of the source file in file system (or in jar, ...).
60       */
61      private final URL url;
62  
63      /**
64       * Source code itself.
65       */
66      private final StringBuilder code = new StringBuilder();
67  
68      /**
69       * eFaps UUID of the program.
70       */
71      private final UUID eFapsUUID;
72  
73      /**
74       * eFaps revision of the program.
75       */
76      private final String revision;
77  
78      /**
79       * Name of the program in eFaps.
80       *
81       * @see #getClassName
82       */
83      private final String programName;
84  
85      /**
86       * CIType.
87       */
88      private final CIType ciType;
89  
90      /**
91       * Constructor used to read the source code from given URL and extract the
92       * class name.
93       *
94       * @param _type     related eFaps type
95       * @param _url      URL to the source code
96       * @throws InstallationException on error
97       * @see #readCode()
98       * @see #evalProgramName()
99       * @see #evalUUID()
100      * @see #evalRevision()
101      */
102     public AbstractSourceImporter(final CIType _type,
103                                   final URL _url)
104         throws InstallationException
105     {
106         this.ciType = _type;
107         this.url = _url;
108         readCode();
109         this.programName = evalProgramName();
110         this.eFapsUUID = evalUUID();
111         this.revision = evalRevision();
112     }
113 
114     /**
115      * Read the code from the file defined through {@link #url} with character
116      * set {@link #ENCODING}.
117      *
118      * @throws InstallationException if the source code could not read from URL
119      * @see #url
120      * @see #ENCODING
121      */
122     protected void readCode()
123         throws InstallationException
124     {
125         try {
126             final char[] buf = new char[1024];
127 
128             final InputStream input = getUrl().openStream();
129 
130             final Reader reader = new InputStreamReader(input, AbstractSourceImporter.ENCODING);
131             int length;
132             while ((length = reader.read(buf)) > 0) {
133                 this.code.append(buf, 0, length);
134             }
135             reader.close();
136         } catch (final IOException e) {
137             throw new InstallationException("Could not read source code from url '" + this.url + "'.", e);
138         }
139     }
140 
141     /**
142      * This Method extracts the Name from the program.
143      *
144      * @return Name of the program
145      * @throws InstallationException on error
146      */
147     protected abstract String evalProgramName()
148         throws InstallationException;
149 
150     /**
151      * This Method extracts the UUID from the program.
152      *
153      * @return UUID of the program
154      * @throws InstallationException on error
155      */
156     protected abstract UUID evalUUID()
157         throws InstallationException;;
158 
159     /**
160      * This Method extracts the Revision from the program.
161      *
162      * @return Revision of the program
163      * @throws InstallationException on error
164      */
165     protected abstract String evalRevision()
166          throws InstallationException;;
167 
168     /**
169      * Import related source code into the eFaps DataBase. If the source code
170      * does not exists, the source code is created in eFaps.
171      *
172      * @throws InstallationException on error
173      * @see #searchInstance()
174      * @see #createInstance()
175      * @see #updateDB(Instance)
176      */
177     public void execute()
178         throws InstallationException
179     {
180         Instance instance = searchInstance();
181         if (instance == null) {
182             instance = createInstance();
183         }
184         updateDB(instance);
185     }
186 
187     /**
188      * Method to search the Instance which is imported.
189      *
190      * @return Instance of the imported program
191      * @throws InstallationException if search failed
192      */
193     public Instance searchInstance()
194         throws InstallationException
195     {
196         Instance instance = null;
197         try {
198             // check if type exists. Necessary for first time installations
199             if (this.ciType.getType() != null) {
200                 final QueryBuilder queryBldr = new QueryBuilder(this.ciType);
201                 queryBldr.addWhereAttrEqValue(CIAdminProgram.Abstract.Name, this.programName);
202                 final InstanceQuery query = queryBldr.getQuery();
203                 query.executeWithoutAccessCheck();
204                 if (query.next()) {
205                     instance = query.getCurrentValue();
206                 }
207             }
208         } catch (final EFapsException e)  {
209             throw new InstallationException("Could not found '" + this.ciType + "' '" + this.programName + "'", e);
210         }
211 
212         return instance;
213     }
214 
215     /**
216      * Creates an instance of a source object in eFaps for given name.
217      *
218      * @return new created instance
219      * @throws InstallationException on error
220      * @see #programName
221      */
222     protected Instance createInstance()
223         throws InstallationException
224     {
225         final Insert insert;
226         try {
227             insert = new Insert(this.ciType);
228             insert.add("Name", this.programName);
229             if (getEFapsUUID() != null) {
230                 insert.add("UUID", getEFapsUUID().toString());
231             }
232             insert.execute();
233         } catch (final EFapsException e)  {
234             throw new InstallationException("Could not create " + this.ciType + " " + getProgramName(), e);
235         }
236         return insert.getInstance();
237     }
238 
239     /**
240      * Stores the read source code in eFaps. This is done with a check in.
241      *
242      * @param _instance         instance (object id) of the source code object
243      *                          in eFaps
244      * @throws InstallationException   if source code in eFaps could not
245      *                                 updated or the source code could not encoded
246      */
247     public void updateDB(final Instance _instance)
248         throws InstallationException
249     {
250         try {
251             final InputStream is = newCodeInputStream();
252             final Checkin checkin = new Checkin(_instance);
253             checkin.executeWithoutAccessCheck(getProgramName(), is, is.available());
254         } catch (final UnsupportedEncodingException e) {
255             throw new InstallationException("Encoding failed for " + this.programName, e);
256         } catch (final EFapsException e)  {
257             throw new InstallationException("Could not check in " + this.programName, e);
258         } catch (final IOException e) {
259             throw new InstallationException("Reading from inoutstream faild for " + this.programName, e);
260         }
261     }
262 
263     /**
264      * Creates a new byte array input stream for {@link #code} which is encoded
265      * in character set {@link #ENCODING}.
266      *
267      * @return byte array input stream
268      * @throws UnsupportedEncodingException if {@link #code} could not be
269      *                                      encoded
270      * @see #code
271      */
272     protected InputStream newCodeInputStream()
273         throws UnsupportedEncodingException
274     {
275         return new ByteArrayInputStream(this.code.toString().getBytes(AbstractSourceImporter.ENCODING));
276     }
277 
278     /**
279      * Getter method for instance variable {@link #code}.
280      *
281      * @return value for instance variable {@link #code}
282      */
283     public StringBuilder getCode()
284     {
285         return this.code;
286     }
287 
288     /**
289      * Getter method for instance variable {@link #url}.
290      *
291      * @return value for instance variable {@link #url}
292      */
293     public URL getUrl()
294     {
295         return this.url;
296     }
297 
298     /**
299      * Getter Method for instance variable {@link #eFapsUUID}.
300      *
301      * @return value for instance variable {@link #eFapsUUID}
302      */
303     public UUID getEFapsUUID()
304     {
305         return this.eFapsUUID;
306     }
307 
308     /**
309      * Getter Method for instance variable {@link #revision}.
310      *
311      * @return value for instance variable {@link #revision}
312      */
313     public String getRevision()
314     {
315         return this.revision;
316     }
317 
318     /**
319      * Getter method for instance variable {@link #programName}.
320      *
321      * @return value of instance variable className
322      * @see #programName
323      */
324     public final String getProgramName()
325     {
326         return this.programName;
327     }
328 }