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