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