View Javadoc
1   package org.andromda.ant.task;
2   
3   import java.io.FileNotFoundException;
4   import java.net.URL;
5   import java.util.Map;
6   import org.andromda.core.AndroMDA;
7   import org.andromda.core.common.ResourceUtils;
8   import org.apache.commons.lang.StringUtils;
9   import org.apache.commons.lang.exception.ExceptionUtils;
10  import org.apache.tools.ant.BuildException;
11  import org.apache.tools.ant.taskdefs.MatchingTask;
12  
13  /**
14   * <p/> This class wraps the AndroMDA model processor so that AndroMDA can be
15   * used as an Ant task. Represents the <code>&lt;andromda&gt;</code> custom
16   * task which can be called from an Ant build script.
17   * </p>
18   *
19   * @author <a href="http://www.mbohlen.de">Matthias Bohlen </a>
20   * @author <a href="http://www.amowers.com">Anthony Mowers </a>
21   * @author Chad Brandon
22   * @see org.andromda.core.AndroMDA
23   */
24  public class AndroMDAGenTask
25      extends MatchingTask
26  {
27      /**
28       * Initialize the context class loader.
29       */
30      static
31      {
32          initializeContextClassLoader();
33      }
34  
35      /**
36       * Stores the configuration URI.
37       */
38      private URL configurationUri;
39  
40      /**
41       * Sets the URI to the configuration file.
42       *
43       * @param configurationUri
44       */
45      public void setConfigurationUri(final URL configurationUri)
46      {
47          this.configurationUri = configurationUri;
48      }
49  
50      /**
51       * <p/>
52       * Starts the generation of source code from an object model. </p>
53       * <p/>
54       * This is the main entry point of the application when running Ant. It is called by ant whenever the surrounding
55       * task is executed (which could be multiple times). </p>
56       *
57       * @throws BuildException if something goes wrong
58       */
59      public void execute()
60          throws BuildException
61      {
62          // initialize before the execute as well in case we
63          // want to execute more than once
64          initializeContextClassLoader();
65          try
66          {
67              if (this.configurationUri == null)
68              {
69                  throw new BuildException("Configuration is not a valid URI --> '" + this.configurationUri + '\'');
70              }
71              final AndroMDA andromda = AndroMDA.newInstance();
72              if (andromda != null)
73              {
74                  andromda.run(
75                      this.replaceProperties(ResourceUtils.getContents(configurationUri)));
76                  andromda.shutdown();
77              }
78          }
79          catch (Throwable throwable)
80          {
81              final Throwable cause = ExceptionUtils.getCause(throwable);
82              if (cause != null)
83              {
84                  throwable = cause;
85              }
86              if (throwable instanceof FileNotFoundException)
87              {
88                  throw new BuildException("No configuration could be loaded from --> '" + configurationUri + '\'');
89              }
90              throw new BuildException(throwable);
91          }
92          finally
93          {
94              // Set the context class loader back ot its system class loaders
95              // so that any processes running after won't be trying to use
96              // the ContextClassLoader for this class.
97              Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());
98          }
99      }
100 
101     /**
102      * Replaces all properties having the style
103      * <code>${some.property}</code> with the value
104      * of the specified property if there is one.
105      *
106      * @param string the fileContents to perform replacement on.
107      * @return replaced properties
108      */
109     protected String replaceProperties(String string)
110     {
111         @SuppressWarnings("unchecked")
112         final Map<String, Object> properties = this.getProject().getProperties();
113         if (properties != null)
114         {
115             for (Map.Entry<String, Object> entry : properties.entrySet())
116             {
117                 final String name = entry.getKey();
118                 final String value = (String)entry.getValue();
119                 final String property = "${" + name + '}';
120                 string = StringUtils.replace(string, property, value);
121             }
122         }
123 
124         // remove any left over property references
125         string = this.removePropertyReferences(string);
126         return string;
127     }
128 
129     /**
130      * The property reference pattern.
131      */
132     private static final String PROPERTY_REFERENCE = "\\$\\{.*\\}";
133 
134     /**
135      * Removes any ${some.property} type references from the string
136      * and returns the modifed string.
137      * @param string the string from which to remove the property
138      *        references
139      *
140      * @return the modified string.
141      */
142     public String removePropertyReferences(String string)
143     {
144         if (string != null)
145         {
146             string = string.replaceAll(PROPERTY_REFERENCE, "");
147         }
148         return string;
149     }
150 
151     /**
152      * Set the context class loader so that any classes using it (the contextContextClassLoader) have access to the
153      * correct loader.
154      */
155     private static void initializeContextClassLoader()
156     {
157         Thread.currentThread().setContextClassLoader(AndroMDAGenTask.class.getClassLoader());
158     }
159 }