View Javadoc

1   /*
2   Karma core - Core of the Karma application
3   Copyright (C) 2004  Toolforge <www.toolforge.nl>
4   
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Lesser General Public
7   License as published by the Free Software Foundation; either
8   version 2.1 of the License, or (at your option) any later version.
9   
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  Lesser General Public License for more details.
14  
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, write to the Free Software
17  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19  package nl.toolforge.karma.core.cmd.impl;
20  
21  import nl.toolforge.core.util.file.MyFileUtils;
22  import nl.toolforge.karma.core.KarmaRuntimeException;
23  import nl.toolforge.karma.core.cmd.CommandDescriptor;
24  import nl.toolforge.karma.core.cmd.CommandException;
25  import nl.toolforge.karma.core.cmd.DefaultCommand;
26  import nl.toolforge.karma.core.cmd.util.AntLogger;
27  import nl.toolforge.karma.core.cmd.util.BuildEnvironment;
28  import nl.toolforge.karma.core.manifest.Manifest;
29  import nl.toolforge.karma.core.manifest.ManifestException;
30  import nl.toolforge.karma.core.module.Module;
31  import nl.toolforge.karma.core.module.ModuleTypeException;
32  import org.apache.commons.io.FileUtils;
33  import org.apache.commons.logging.Log;
34  import org.apache.commons.logging.LogFactory;
35  import org.apache.tools.ant.Project;
36  import org.apache.tools.ant.ProjectHelper;
37  import org.apache.tools.ant.helper.ProjectHelperImpl;
38  import org.apache.tools.ant.taskdefs.Delete;
39  import org.apache.tools.ant.taskdefs.Mkdir;
40  import org.apache.tools.ant.types.FileSet;
41  
42  import java.io.BufferedReader;
43  import java.io.BufferedWriter;
44  import java.io.File;
45  import java.io.FileWriter;
46  import java.io.IOException;
47  import java.io.InputStreamReader;
48  
49  /***
50   * Superclass for all commands dealing with building modules. This class provides all basic property mappers and methods
51   * that are required to use the <code>build-module.xml</code> properly.
52   *
53   * @author D.A. Smedes
54   * @author W.H. Schraal
55   * @version $Id: AbstractBuildCommand.java,v 1.38 2004/11/10 23:53:08 asmedes Exp $
56   */
57  public abstract class AbstractBuildCommand extends DefaultCommand {
58  
59    private static final Log logger = LogFactory.getLog(AbstractBuildCommand.class);
60  
61    protected Module module = null;
62  
63    private File tempBuildFileLocation = null; // Maintains a hook to a temp location for the Ant build file.
64    private Project project = null;
65    private BuildEnvironment env = null;
66  
67    /***
68     * Creates a command by initializing the command through its <code>CommandDescriptor</code>.
69     *
70     * @param descriptor The command descriptor instance containing the basic information for this command
71     */
72    public AbstractBuildCommand(CommandDescriptor descriptor) {
73      super(descriptor);
74    }
75  
76    public void execute() throws CommandException {
77  
78      if (!getContext().isManifestLoaded()) {
79        throw new CommandException(ManifestException.NO_ACTIVE_MANIFEST);
80      }
81  
82      if (getCommandLine().hasOption("m")) {
83        String moduleName = getCommandLine().getOptionValue("m");
84  
85        try {
86          // todo move this bit to aspect-code.
87          //
88          module = getCurrentManifest().getModule(moduleName);
89  
90          if (module == null) {
91            throw new CommandException(ManifestException.MODULE_NOT_FOUND, new Object[]{module});
92          }
93  
94          if (!getCurrentManifest().isLocal(module)) {
95            throw new CommandException(ManifestException.MODULE_NOT_LOCAL, new Object[]{module});
96          }
97  
98  
99        } catch (ManifestException m) {
100         throw new CommandException(m.getErrorCode(), m.getMessageArguments());
101       }
102     }
103 
104     // Initialize the current build environment.
105     //
106     env = new BuildEnvironment(getCurrentManifest(), module);
107   }
108 
109   /***
110    * Helper method to retrieve the current manifest.
111    *
112    * @return The current manifest.
113    */
114   protected final Manifest getCurrentManifest() {
115     return getContext().getCurrentManifest();
116   }
117 
118   /***
119    * Helper method to get to the current module.
120    *
121    * @return
122    */
123   protected Module getCurrentModule() {
124 
125     if (module == null) {
126       throw new KarmaRuntimeException("Module is null. Execute method has not been called by subclass.");
127     }
128     return module;
129   }
130 
131   /***
132    * Retrieve the build environment, which is initialized with the current manifest and module.
133    */
134   protected BuildEnvironment getBuildEnvironment() {
135     return env;
136   }
137 
138   /***
139    * Returns the compile directory for a module, relative to the manifests' <code>build</code> directory.
140    *
141    * @return
142    */
143   protected final File getCompileDirectory() throws ModuleTypeException {
144 
145     if (module == null) {
146       throw new IllegalArgumentException("Module cannot be null.");
147     }
148 
149     File base = env.getModuleBuildRootDirectory();
150 
151     if (module.getType().equals(Module.JAVA_WEB_APPLICATION)) {
152       return new File(base, "build/WEB-INF/classes");
153     } else {
154       return new File(base, "build");
155     }
156   }
157 
158   /***
159    * Gets an Ant project initializing the project with <code>buildFile</code> which should be located on the
160    * classpath in the <code>ant</code> subdirectory.
161    *
162    * @param buildFile The build file that should be loaded.
163    * @return An Ant project initialized with <code>buildFile</code>.
164    */
165   protected Project getAntProject(String buildFile) throws CommandException {
166 
167     ProjectHelper helper = new ProjectHelperImpl();
168     try {
169       File tmp = getBuildFile(buildFile);
170       helper.parse(getProjectInstance(), tmp);
171       setBuildFileLocation(tmp);
172     } catch (IOException e) {
173       throw new CommandException(e, CommandException.BUILD_FAILED, new Object[] {getCurrentModule().getName()});
174     }
175 
176     return project;
177   }
178 
179 
180   /***
181    * Gets an Ant <code>Project</code> for a module.
182    *
183    * @return
184    * @throws CommandException
185    */
186   protected Project getProjectInstance() throws CommandException {
187 
188     if (project == null) {
189 
190       //DefaultLogger logger = new DefaultLogger();
191       AntLogger logger = new AntLogger(this);
192 
193       // todo hmm, this mechanism doesn't integrate with the commandresponse mechanism
194       //
195       logger.setOutputPrintStream(System.out);
196 
197       //todo: this has to be configurable
198       String logLevel = System.getProperty("antloglevel");
199       int antLogLevel = Project.MSG_WARN;
200       if (logLevel.equalsIgnoreCase("debug")) {
201         antLogLevel = Project.MSG_DEBUG;
202       } else if (logLevel.equalsIgnoreCase("info")) {
203         antLogLevel = Project.MSG_INFO;
204       }
205       logger.setMessageOutputLevel(antLogLevel);
206 
207       // Configure underlying ant to run a command.
208       //
209       project = new Project();
210       project.addBuildListener(logger);
211       project.init();
212     }
213 
214     return project;
215   }
216 
217   /***
218    * Performs an &lt;mkdir&gt;-task on this commands' Ant project.
219    */
220   public void executeMkdir(File dir) throws CommandException {
221 
222     if (project == null) {
223       project = getProjectInstance();
224     }
225 
226     Mkdir mkdir = new Mkdir();
227     mkdir.setProject(project);
228     mkdir.setDir(dir);
229     mkdir.execute();
230   }
231 
232   /***
233    * Performs a &lt;delete&gt;-task on this commands' Ant project.
234    */
235   public void executeDelete(File dir, String includes) throws CommandException {
236 
237     if (project == null) {
238       project = getProjectInstance();
239     }
240 
241     if (dir != null && dir.exists()) {
242 
243       // <delete>
244       //
245       Delete delete = new Delete();
246       delete.setProject(project);
247 
248       FileSet fileset = new FileSet();
249       fileset.setDir(dir);
250       fileset.setIncludes(includes);
251 
252       delete.addFileset(fileset);
253       delete.execute();
254     }
255   }
256 
257   public void executeDelete(File dir) throws CommandException {
258 
259     if (project == null) {
260       project = getProjectInstance();
261     }
262 
263     try {
264 
265       // <delete>
266       //
267       Delete delete = new Delete();
268       delete.setProject(project);
269 
270       if (dir.equals(new File("."))) {
271         throw new KarmaRuntimeException("We don't do that stuff here ...");
272       }
273 
274       delete.setDir(dir);
275       delete.execute();
276     } catch (RuntimeException r) {
277       throw new CommandException(CommandException.BUILD_FAILED, new Object[]{r.getMessage()});
278     }
279   }
280 
281   private final File getBuildFile(String buildFile) throws IOException {
282 
283     File tmp = null;
284 
285     tmp = MyFileUtils.createTempDirectory();
286 
287     ClassLoader loader = this.getClass().getClassLoader();
288 
289     BufferedReader in =
290         new BufferedReader(new InputStreamReader(loader.getResourceAsStream("ant/" + buildFile)));
291     BufferedWriter out =
292         new BufferedWriter(new FileWriter(new File(tmp, buildFile)));
293 
294     String str;
295     while ((str = in.readLine()) != null) {
296       out.write(str);
297     }
298     out.close();
299     in.close();
300 
301     // Return a temp reference to the file
302     //
303     return new File(tmp, buildFile);
304   }
305 
306   private void setBuildFileLocation(File tmpBuildFileLocation) {
307     this.tempBuildFileLocation = tmpBuildFileLocation;
308   }
309 
310   /***
311    * Called by {@link nl.toolforge.karma.core.cmd.CommandContext} after executing a command.
312    */
313   public final void cleanUp() {
314 
315     try {
316       if (tempBuildFileLocation != null) {
317         FileUtils.deleteDirectory(tempBuildFileLocation.getParentFile());
318       }
319     } catch (IOException e) {
320       logger.warn("Could not remove temporary directory for Ant build file.");
321     }
322   }
323 
324 }