View Javadoc
1   /*
2    *   Copyright (C) Christian Schulte <cs@schulte.it>, 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 5043 2015-05-27 07:03:39Z schulte $
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.DefaultModelValidator;
57  import org.jomc.model.modlet.ModelHelper;
58  import org.jomc.modlet.Model;
59  import org.jomc.modlet.ModelContext;
60  import org.jomc.modlet.ModelException;
61  import org.jomc.tools.modlet.ToolsModelProcessor;
62  import org.jomc.tools.modlet.ToolsModelProvider;
63  
64  /**
65   * Base class for executing model based tasks.
66   *
67   * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
68   * @version $JOMC: JomcModelTask.java 5043 2015-05-27 07:03:39Z schulte $
69   */
70  public class JomcModelTask extends JomcTask
71  {
72  
73      /**
74       * Controls model object class path resolution.
75       */
76      private boolean modelObjectClasspathResolutionEnabled = true;
77  
78      /**
79       * The location to search for modules.
80       */
81      private String moduleLocation;
82  
83      /**
84       * The location to search for transformers.
85       */
86      private String transformerLocation;
87  
88      /**
89       * Module resources.
90       */
91      private Set<ModuleResourceType> moduleResources;
92  
93      /**
94       * The flag indicating JAXP schema validation of model resources is enabled.
95       */
96      private boolean modelResourceValidationEnabled = true;
97  
98      /**
99       * The flag indicating Java validation is enabled.
100      */
101     private boolean javaValidationEnabled = true;
102 
103     /**
104      * Creates a new {@code JomcModelTask} instance.
105      */
106     public JomcModelTask()
107     {
108         super();
109     }
110 
111     /**
112      * Gets the location searched for modules.
113      *
114      * @return The location searched for modules or {@code null}.
115      *
116      * @see #setModuleLocation(java.lang.String)
117      */
118     public final String getModuleLocation()
119     {
120         return this.moduleLocation;
121     }
122 
123     /**
124      * Sets the location to search for modules.
125      *
126      * @param value The new location to search for modules or {@code null}.
127      *
128      * @see #getModuleLocation()
129      */
130     public final void setModuleLocation( final String value )
131     {
132         this.moduleLocation = value;
133     }
134 
135     /**
136      * Gets the location searched for transformers.
137      *
138      * @return The location searched for transformers or {@code null}.
139      *
140      * @see #setTransformerLocation(java.lang.String)
141      */
142     public final String getTransformerLocation()
143     {
144         return this.transformerLocation;
145     }
146 
147     /**
148      * Sets the location to search for transformers.
149      *
150      * @param value The new location to search for transformers or {@code null}.
151      *
152      * @see #getTransformerLocation()
153      */
154     public final void setTransformerLocation( final String value )
155     {
156         this.transformerLocation = value;
157     }
158 
159     /**
160      * Gets a flag indicating model object class path resolution is enabled.
161      *
162      * @return {@code true}, if model object class path resolution is enabled; {@code false}, else.
163      *
164      * @see #setModelObjectClasspathResolutionEnabled(boolean)
165      */
166     public final boolean isModelObjectClasspathResolutionEnabled()
167     {
168         return this.modelObjectClasspathResolutionEnabled;
169     }
170 
171     /**
172      * Sets the flag indicating model object class path resolution is enabled.
173      *
174      * @param value {@code true}, to enable model object class path resolution; {@code false}, to disable model object
175      * class path resolution.
176      *
177      * @see #isModelObjectClasspathResolutionEnabled()
178      */
179     public final void setModelObjectClasspathResolutionEnabled( final boolean value )
180     {
181         this.modelObjectClasspathResolutionEnabled = value;
182     }
183 
184     /**
185      * Gets a set of module resources.
186      * <p>
187      * This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make
188      * to the returned set will be present inside the object. This is why there is no {@code set} method for the
189      * module resources property.
190      * </p>
191      *
192      * @return A set of module resources.
193      *
194      * @see #createModuleResource()
195      */
196     public Set<ModuleResourceType> getModuleResources()
197     {
198         if ( this.moduleResources == null )
199         {
200             this.moduleResources = new HashSet<ModuleResourceType>();
201         }
202 
203         return this.moduleResources;
204     }
205 
206     /**
207      * Creates a new {@code moduleResource} element instance.
208      *
209      * @return A new {@code moduleResource} element instance.
210      *
211      * @see #getModuleResources()
212      */
213     public ModuleResourceType createModuleResource()
214     {
215         final ModuleResourceType moduleResource = new ModuleResourceType();
216         this.getModuleResources().add( moduleResource );
217         return moduleResource;
218     }
219 
220     /**
221      * Gets a flag indicating JAXP schema validation of model resources is enabled.
222      *
223      * @return {@code true}, if JAXP schema validation of model resources is enabled; {@code false}, else.
224      *
225      * @see #setModelResourceValidationEnabled(boolean)
226      */
227     public final boolean isModelResourceValidationEnabled()
228     {
229         return this.modelResourceValidationEnabled;
230     }
231 
232     /**
233      * Sets the flag indicating JAXP schema validation of model resources is enabled.
234      *
235      * @param value {@code true}, to enable JAXP schema validation of model resources; {@code false}, to disable JAXP
236      * schema validation of model resources.
237      *
238      * @see #isModelResourceValidationEnabled()
239      */
240     public final void setModelResourceValidationEnabled( final boolean value )
241     {
242         this.modelResourceValidationEnabled = value;
243     }
244 
245     /**
246      * Gets a flag indicating Java validation is enabled.
247      *
248      * @return {@code true}, if Java validation is enabled; {@code false}, else.
249      *
250      * @see #setJavaValidationEnabled(boolean)
251      *
252      * @since 1.4
253      */
254     public final boolean isJavaValidationEnabled()
255     {
256         return this.javaValidationEnabled;
257     }
258 
259     /**
260      * Sets the flag indicating Java validation is enabled.
261      *
262      * @param value {@code true}, to enable Java validation; {@code false}, to disable Java validation.
263      *
264      * @see #isJavaValidationEnabled()
265      *
266      * @since 1.4
267      */
268     public final void setJavaValidationEnabled( final boolean value )
269     {
270         this.javaValidationEnabled = value;
271     }
272 
273     /**
274      * Gets a {@code Model} from a given {@code ModelContext}.
275      *
276      * @param context The context to get a {@code Model} from.
277      *
278      * @return The {@code Model} from {@code context}.
279      *
280      * @throws NullPointerException if {@code contexŧ} is {@code null}.
281      * @throws BuildException if no model is found.
282      * @throws ModelException if getting the model fails.
283      *
284      * @see #getModel()
285      * @see #isModelObjectClasspathResolutionEnabled()
286      * @see #isModelProcessingEnabled()
287      */
288     @Override
289     public Model getModel( final ModelContext context ) throws BuildException, ModelException
290     {
291         if ( context == null )
292         {
293             throw new NullPointerException( "context" );
294         }
295 
296         Model model = new Model();
297         model.setIdentifier( this.getModel() );
298         Modules modules = new Modules();
299         ModelHelper.setModules( model, modules );
300         Unmarshaller unmarshaller = null;
301 
302         for ( final ResourceType resource : this.getModuleResources() )
303         {
304             final URL[] urls = this.getResources( context, resource.getLocation() );
305 
306             if ( urls.length == 0 )
307             {
308                 if ( resource.isOptional() )
309                 {
310                     this.logMessage( Level.WARNING, Messages.getMessage( "moduleResourceNotFound",
311                                                                          resource.getLocation() ) );
312 
313                 }
314                 else
315                 {
316                     throw new BuildException( Messages.getMessage( "moduleResourceNotFound", resource.getLocation() ),
317                                               this.getLocation() );
318 
319                 }
320             }
321 
322             for ( int i = urls.length - 1; i >= 0; i-- )
323             {
324                 InputStream in = null;
325                 boolean suppressExceptionOnClose = true;
326 
327                 try
328                 {
329                     this.logMessage( Level.FINEST, Messages.getMessage( "reading", urls[i].toExternalForm() ) );
330 
331                     final URLConnection con = urls[i].openConnection();
332                     con.setConnectTimeout( resource.getConnectTimeout() );
333                     con.setReadTimeout( resource.getReadTimeout() );
334                     con.connect();
335                     in = con.getInputStream();
336 
337                     final Source source = new StreamSource( in, urls[i].toURI().toASCIIString() );
338 
339                     if ( unmarshaller == null )
340                     {
341                         unmarshaller = context.createUnmarshaller( this.getModel() );
342                         if ( this.isModelResourceValidationEnabled() )
343                         {
344                             unmarshaller.setSchema( context.createSchema( this.getModel() ) );
345                         }
346                     }
347 
348                     Object o = unmarshaller.unmarshal( source );
349                     if ( o instanceof JAXBElement<?> )
350                     {
351                         o = ( (JAXBElement<?>) o ).getValue();
352                     }
353 
354                     if ( o instanceof Module )
355                     {
356                         modules.getModule().add( (Module) o );
357                     }
358                     else
359                     {
360                         this.log( Messages.getMessage( "unsupportedModuleResource", urls[i].toExternalForm() ),
361                                   Project.MSG_WARN );
362 
363                     }
364 
365                     suppressExceptionOnClose = false;
366                 }
367                 catch ( final SocketTimeoutException e )
368                 {
369                     String message = Messages.getMessage( e );
370                     message = Messages.getMessage( "resourceTimeout", message != null ? " " + message : "" );
371 
372                     if ( resource.isOptional() )
373                     {
374                         this.getProject().log( message, e, Project.MSG_WARN );
375                     }
376                     else
377                     {
378                         throw new BuildException( message, e, this.getLocation() );
379                     }
380                 }
381                 catch ( final IOException e )
382                 {
383                     String message = Messages.getMessage( e );
384                     message = Messages.getMessage( "resourceFailure", message != null ? " " + message : "" );
385 
386                     if ( resource.isOptional() )
387                     {
388                         this.getProject().log( message, e, Project.MSG_WARN );
389                     }
390                     else
391                     {
392                         throw new BuildException( message, e, this.getLocation() );
393                     }
394                 }
395                 catch ( final URISyntaxException e )
396                 {
397                     throw new BuildException( Messages.getMessage( e ), e, this.getLocation() );
398                 }
399                 catch ( final JAXBException e )
400                 {
401                     String message = Messages.getMessage( e );
402                     if ( message == null )
403                     {
404                         message = Messages.getMessage( e.getLinkedException() );
405                     }
406 
407                     throw new BuildException( message, e, this.getLocation() );
408                 }
409                 finally
410                 {
411                     try
412                     {
413                         if ( in != null )
414                         {
415                             in.close();
416                         }
417                     }
418                     catch ( final IOException e )
419                     {
420                         if ( suppressExceptionOnClose )
421                         {
422                             this.logMessage( Level.SEVERE, Messages.getMessage( e ), e );
423                         }
424                         else
425                         {
426                             throw new BuildException( Messages.getMessage( e ), e, this.getLocation() );
427                         }
428                     }
429                 }
430             }
431         }
432 
433         model = context.findModel( model );
434         modules = ModelHelper.getModules( model );
435 
436         if ( modules != null && this.isModelObjectClasspathResolutionEnabled() )
437         {
438             final Module classpathModule =
439                 modules.getClasspathModule( Modules.getDefaultClasspathModuleName(), context.getClassLoader() );
440 
441             if ( classpathModule != null && modules.getModule( Modules.getDefaultClasspathModuleName() ) == null )
442             {
443                 modules.getModule().add( classpathModule );
444             }
445         }
446 
447         if ( this.isModelProcessingEnabled() )
448         {
449             model = context.processModel( model );
450         }
451 
452         return model;
453     }
454 
455     /**
456      * {@inheritDoc}
457      */
458     @Override
459     public void preExecuteTask() throws BuildException
460     {
461         super.preExecuteTask();
462         this.assertLocationsNotNull( this.getModuleResources() );
463     }
464 
465     /**
466      * {@inheritDoc}
467      */
468     @Override
469     public ModelContext newModelContext( final ClassLoader classLoader ) throws ModelException
470     {
471         final ModelContext modelContext = super.newModelContext( classLoader );
472 
473         if ( this.getTransformerLocation() != null )
474         {
475             modelContext.setAttribute( DefaultModelProcessor.TRANSFORMER_LOCATION_ATTRIBUTE_NAME,
476                                        this.getTransformerLocation() );
477 
478         }
479 
480         if ( this.getModuleLocation() != null )
481         {
482             modelContext.setAttribute( DefaultModelProvider.MODULE_LOCATION_ATTRIBUTE_NAME, this.getModuleLocation() );
483         }
484 
485         modelContext.setAttribute( ToolsModelProvider.MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME,
486                                    this.isModelObjectClasspathResolutionEnabled() );
487 
488         modelContext.setAttribute( ToolsModelProcessor.MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME,
489                                    this.isModelObjectClasspathResolutionEnabled() );
490 
491         modelContext.setAttribute( DefaultModelProvider.VALIDATING_ATTRIBUTE_NAME,
492                                    this.isModelResourceValidationEnabled() );
493 
494         modelContext.setAttribute( DefaultModelValidator.VALIDATE_JAVA_ATTRIBUTE_NAME, this.isJavaValidationEnabled() );
495 
496         for ( int i = 0, s0 = this.getModelContextAttributes().size(); i < s0; i++ )
497         {
498             final KeyValueType kv = this.getModelContextAttributes().get( i );
499             final Object object = kv.getObject( this.getLocation() );
500 
501             if ( object != null )
502             {
503                 modelContext.setAttribute( kv.getKey(), object );
504             }
505             else
506             {
507                 modelContext.clearAttribute( kv.getKey() );
508             }
509         }
510 
511         return modelContext;
512     }
513 
514     /**
515      * {@inheritDoc}
516      */
517     @Override
518     public JomcModelTask clone()
519     {
520         final JomcModelTask clone = (JomcModelTask) super.clone();
521 
522         if ( this.moduleResources != null )
523         {
524             clone.moduleResources = new HashSet<ModuleResourceType>( this.moduleResources.size() );
525             for ( final ModuleResourceType e : this.moduleResources )
526             {
527                 clone.moduleResources.add( e.clone() );
528             }
529         }
530 
531         return clone;
532     }
533 
534 }