View Javadoc

1   /*
2    *   Copyright (C) Christian Schulte, 2005-206
3    *   All rights reserved.
4    *
5    *   Redistribution and use in source and binary forms, with or without
6    *   modification, are permitted provided that the following conditions
7    *   are met:
8    *
9    *     o Redistributions of source code must retain the above copyright
10   *       notice, this list of conditions and the following disclaimer.
11   *
12   *     o Redistributions in binary form must reproduce the above copyright
13   *       notice, this list of conditions and the following disclaimer in
14   *       the documentation and/or other materials provided with the
15   *       distribution.
16   *
17   *   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
18   *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
19   *   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
20   *   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
21   *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22   *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23   *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24   *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25   *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26   *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27   *
28   *   $JOMC: JomcModelTask.java 4200 2012-01-25 09:46:13Z schulte2005 $
29   *
30   */
31  package org.jomc.ant;
32  
33  import java.io.IOException;
34  import java.io.InputStream;
35  import java.net.SocketTimeoutException;
36  import java.net.URISyntaxException;
37  import java.net.URL;
38  import java.net.URLConnection;
39  import java.util.HashSet;
40  import java.util.Set;
41  import java.util.logging.Level;
42  import javax.xml.bind.JAXBElement;
43  import javax.xml.bind.JAXBException;
44  import javax.xml.bind.Unmarshaller;
45  import javax.xml.transform.Source;
46  import javax.xml.transform.stream.StreamSource;
47  import org.apache.tools.ant.BuildException;
48  import org.apache.tools.ant.Project;
49  import org.jomc.ant.types.KeyValueType;
50  import org.jomc.ant.types.ModuleResourceType;
51  import org.jomc.ant.types.ResourceType;
52  import org.jomc.model.Module;
53  import org.jomc.model.Modules;
54  import org.jomc.model.modlet.DefaultModelProcessor;
55  import org.jomc.model.modlet.DefaultModelProvider;
56  import org.jomc.model.modlet.ModelHelper;
57  import org.jomc.modlet.Model;
58  import org.jomc.modlet.ModelContext;
59  import org.jomc.modlet.ModelException;
60  import org.jomc.tools.modlet.ToolsModelProcessor;
61  import org.jomc.tools.modlet.ToolsModelProvider;
62  
63  /**
64   * Base class for executing model based tasks.
65   *
66   * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a>
67   * @version $JOMC: JomcModelTask.java 4200 2012-01-25 09:46:13Z schulte2005 $
68   */
69  public class JomcModelTask extends JomcTask
70  {
71  
72      /** Controls model object class path resolution. */
73      private boolean modelObjectClasspathResolutionEnabled = true;
74  
75      /** The location to search for modules. */
76      private String moduleLocation;
77  
78      /** The location to search for transformers. */
79      private String transformerLocation;
80  
81      /** Module resources. */
82      private Set<ModuleResourceType> moduleResources;
83  
84      /** The flag indicating JAXP schema validation of model resources is enabled. */
85      private boolean modelResourceValidationEnabled = true;
86  
87      /** Creates a new {@code JomcModelTask} instance. */
88      public JomcModelTask()
89      {
90          super();
91      }
92  
93      /**
94       * Gets the location searched for modules.
95       *
96       * @return The location searched for modules or {@code null}.
97       *
98       * @see #setModuleLocation(java.lang.String)
99       */
100     public final String getModuleLocation()
101     {
102         return this.moduleLocation;
103     }
104 
105     /**
106      * Sets the location to search for modules.
107      *
108      * @param value The new location to search for modules or {@code null}.
109      *
110      * @see #getModuleLocation()
111      */
112     public final void setModuleLocation( final String value )
113     {
114         this.moduleLocation = value;
115     }
116 
117     /**
118      * Gets the location searched for transformers.
119      *
120      * @return The location searched for transformers or {@code null}.
121      *
122      * @see #setTransformerLocation(java.lang.String)
123      */
124     public final String getTransformerLocation()
125     {
126         return this.transformerLocation;
127     }
128 
129     /**
130      * Sets the location to search for transformers.
131      *
132      * @param value The new location to search for transformers or {@code null}.
133      *
134      * @see #getTransformerLocation()
135      */
136     public final void setTransformerLocation( final String value )
137     {
138         this.transformerLocation = value;
139     }
140 
141     /**
142      * Gets a flag indicating model object class path resolution is enabled.
143      *
144      * @return {@code true}, if model object class path resolution is enabled; {@code false}, else.
145      *
146      * @see #setModelObjectClasspathResolutionEnabled(boolean)
147      */
148     public final boolean isModelObjectClasspathResolutionEnabled()
149     {
150         return this.modelObjectClasspathResolutionEnabled;
151     }
152 
153     /**
154      * Sets the flag indicating model object class path resolution is enabled.
155      *
156      * @param value {@code true}, to enable model object class path resolution; {@code false}, to disable model object
157      * class path resolution.
158      *
159      * @see #isModelObjectClasspathResolutionEnabled()
160      */
161     public final void setModelObjectClasspathResolutionEnabled( final boolean value )
162     {
163         this.modelObjectClasspathResolutionEnabled = value;
164     }
165 
166     /**
167      * Gets a set of module resources.
168      * <p>This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make
169      * to the returned set will be present inside the object. This is why there is no {@code set} method for the
170      * module resources property.</p>
171      *
172      * @return A set of module resources.
173      *
174      * @see #createModuleResource()
175      */
176     public Set<ModuleResourceType> getModuleResources()
177     {
178         if ( this.moduleResources == null )
179         {
180             this.moduleResources = new HashSet<ModuleResourceType>();
181         }
182 
183         return this.moduleResources;
184     }
185 
186     /**
187      * Creates a new {@code moduleResource} element instance.
188      *
189      * @return A new {@code moduleResource} element instance.
190      *
191      * @see #getModuleResources()
192      */
193     public ModuleResourceType createModuleResource()
194     {
195         final ModuleResourceType moduleResource = new ModuleResourceType();
196         this.getModuleResources().add( moduleResource );
197         return moduleResource;
198     }
199 
200     /**
201      * Gets a flag indicating JAXP schema validation of model resources is enabled.
202      *
203      * @return {@code true}, if JAXP schema validation of model resources is enabled; {@code false}, else.
204      *
205      * @see #setModelResourceValidationEnabled(boolean)
206      */
207     public final boolean isModelResourceValidationEnabled()
208     {
209         return this.modelResourceValidationEnabled;
210     }
211 
212     /**
213      * Sets the flag indicating JAXP schema validation of model resources is enabled.
214      *
215      * @param value {@code true}, to enable JAXP schema validation of model resources; {@code false}, to disable JAXP
216      * schema validation of model resources.
217      *
218      * @see #isModelResourceValidationEnabled()
219      */
220     public final void setModelResourceValidationEnabled( final boolean value )
221     {
222         this.modelResourceValidationEnabled = value;
223     }
224 
225     /**
226      * Gets a {@code Model} from a given {@code ModelContext}.
227      *
228      * @param context The context to get a {@code Model} from.
229      *
230      * @return The {@code Model} from {@code context}.
231      *
232      * @throws NullPointerException if {@code contexŧ} is {@code null}.
233      * @throws BuildException if no model is found.
234      * @throws ModelException if getting the model fails.
235      *
236      * @see #getModel()
237      * @see #isModelObjectClasspathResolutionEnabled()
238      * @see #isModelProcessingEnabled()
239      */
240     @Override
241     public Model getModel( final ModelContext context ) throws BuildException, ModelException
242     {
243         if ( context == null )
244         {
245             throw new NullPointerException( "context" );
246         }
247 
248         Model model = new Model();
249         model.setIdentifier( this.getModel() );
250         Modules modules = new Modules();
251         ModelHelper.setModules( model, modules );
252         Unmarshaller unmarshaller = null;
253 
254         for ( ResourceType resource : this.getModuleResources() )
255         {
256             final URL[] urls = this.getResources( context, resource.getLocation() );
257 
258             if ( urls.length == 0 )
259             {
260                 if ( resource.isOptional() )
261                 {
262                     this.logMessage( Level.WARNING, Messages.getMessage( "moduleResourceNotFound",
263                                                                          resource.getLocation() ) );
264 
265                 }
266                 else
267                 {
268                     throw new BuildException( Messages.getMessage( "moduleResourceNotFound", resource.getLocation() ),
269                                               this.getLocation() );
270 
271                 }
272             }
273 
274             for ( int i = urls.length - 1; i >= 0; i-- )
275             {
276                 InputStream in = null;
277                 boolean suppressExceptionOnClose = true;
278 
279                 try
280                 {
281                     this.logMessage( Level.FINEST, Messages.getMessage( "reading", urls[i].toExternalForm() ) );
282 
283                     final URLConnection con = urls[i].openConnection();
284                     con.setConnectTimeout( resource.getConnectTimeout() );
285                     con.setReadTimeout( resource.getReadTimeout() );
286                     con.connect();
287                     in = con.getInputStream();
288 
289                     final Source source = new StreamSource( in, urls[i].toURI().toASCIIString() );
290 
291                     if ( unmarshaller == null )
292                     {
293                         unmarshaller = context.createUnmarshaller( this.getModel() );
294                         if ( this.isModelResourceValidationEnabled() )
295                         {
296                             unmarshaller.setSchema( context.createSchema( this.getModel() ) );
297                         }
298                     }
299 
300                     Object o = unmarshaller.unmarshal( source );
301                     if ( o instanceof JAXBElement<?> )
302                     {
303                         o = ( (JAXBElement<?>) o ).getValue();
304                     }
305 
306                     if ( o instanceof Module )
307                     {
308                         modules.getModule().add( (Module) o );
309                     }
310                     else
311                     {
312                         this.log( Messages.getMessage( "unsupportedModuleResource", urls[i].toExternalForm() ),
313                                   Project.MSG_WARN );
314 
315                     }
316 
317                     suppressExceptionOnClose = false;
318                 }
319                 catch ( final SocketTimeoutException e )
320                 {
321                     String message = Messages.getMessage( e );
322                     message = Messages.getMessage( "resourceTimeout", message != null ? " " + message : "" );
323 
324                     if ( resource.isOptional() )
325                     {
326                         this.getProject().log( message, e, Project.MSG_WARN );
327                     }
328                     else
329                     {
330                         throw new BuildException( message, e, this.getLocation() );
331                     }
332                 }
333                 catch ( final IOException e )
334                 {
335                     String message = Messages.getMessage( e );
336                     message = Messages.getMessage( "resourceFailure", message != null ? " " + message : "" );
337 
338                     if ( resource.isOptional() )
339                     {
340                         this.getProject().log( message, e, Project.MSG_WARN );
341                     }
342                     else
343                     {
344                         throw new BuildException( message, e, this.getLocation() );
345                     }
346                 }
347                 catch ( final URISyntaxException e )
348                 {
349                     throw new BuildException( Messages.getMessage( e ), e, this.getLocation() );
350                 }
351                 catch ( final JAXBException e )
352                 {
353                     String message = Messages.getMessage( e );
354                     if ( message == null )
355                     {
356                         message = Messages.getMessage( e.getLinkedException() );
357                     }
358 
359                     throw new BuildException( message, e, this.getLocation() );
360                 }
361                 finally
362                 {
363                     try
364                     {
365                         if ( in != null )
366                         {
367                             in.close();
368                         }
369                     }
370                     catch ( final IOException e )
371                     {
372                         if ( suppressExceptionOnClose )
373                         {
374                             this.logMessage( Level.SEVERE, Messages.getMessage( e ), e );
375                         }
376                         else
377                         {
378                             throw new BuildException( Messages.getMessage( e ), e, this.getLocation() );
379                         }
380                     }
381                 }
382             }
383         }
384 
385         model = context.findModel( model );
386         modules = ModelHelper.getModules( model );
387 
388         if ( modules != null && this.isModelObjectClasspathResolutionEnabled() )
389         {
390             final Module classpathModule =
391                 modules.getClasspathModule( Modules.getDefaultClasspathModuleName(), context.getClassLoader() );
392 
393             if ( classpathModule != null && modules.getModule( Modules.getDefaultClasspathModuleName() ) == null )
394             {
395                 modules.getModule().add( classpathModule );
396             }
397         }
398 
399         if ( this.isModelProcessingEnabled() )
400         {
401             model = context.processModel( model );
402         }
403 
404         return model;
405     }
406 
407     /** {@inheritDoc} */
408     @Override
409     public void preExecuteTask() throws BuildException
410     {
411         super.preExecuteTask();
412         this.assertLocationsNotNull( this.getModuleResources() );
413     }
414 
415     /** {@inheritDoc} */
416     @Override
417     public ModelContext newModelContext( final ClassLoader classLoader ) throws ModelException
418     {
419         final ModelContext modelContext = super.newModelContext( classLoader );
420 
421         if ( this.getTransformerLocation() != null )
422         {
423             modelContext.setAttribute( DefaultModelProcessor.TRANSFORMER_LOCATION_ATTRIBUTE_NAME,
424                                        this.getTransformerLocation() );
425 
426         }
427 
428         if ( this.getModuleLocation() != null )
429         {
430             modelContext.setAttribute( DefaultModelProvider.MODULE_LOCATION_ATTRIBUTE_NAME, this.getModuleLocation() );
431         }
432 
433         modelContext.setAttribute( ToolsModelProvider.MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME,
434                                    this.isModelObjectClasspathResolutionEnabled() );
435 
436         modelContext.setAttribute( ToolsModelProcessor.MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME,
437                                    this.isModelObjectClasspathResolutionEnabled() );
438 
439         modelContext.setAttribute( DefaultModelProvider.VALIDATING_ATTRIBUTE_NAME,
440                                    this.isModelResourceValidationEnabled() );
441 
442         for ( int i = 0, s0 = this.getModelContextAttributes().size(); i < s0; i++ )
443         {
444             final KeyValueType kv = this.getModelContextAttributes().get( i );
445             final Object object = kv.getObject( this.getLocation() );
446 
447             if ( object != null )
448             {
449                 modelContext.setAttribute( kv.getKey(), object );
450             }
451             else
452             {
453                 modelContext.clearAttribute( kv.getKey() );
454             }
455         }
456 
457 
458         return modelContext;
459     }
460 
461     /** {@inheritDoc} */
462     @Override
463     public JomcModelTask clone()
464     {
465         final JomcModelTask clone = (JomcModelTask) super.clone();
466 
467         if ( this.moduleResources != null )
468         {
469             clone.moduleResources = new HashSet<ModuleResourceType>( this.moduleResources.size() );
470             for ( ModuleResourceType e : this.moduleResources )
471             {
472                 clone.moduleResources.add( e.clone() );
473             }
474         }
475 
476         return clone;
477     }
478 
479 }