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.module;
20  
21  import net.sf.sillyexceptions.OutOfTheBlueException;
22  import nl.toolforge.core.util.file.MyFileUtils;
23  import nl.toolforge.karma.core.KarmaRuntimeException;
24  import nl.toolforge.karma.core.Version;
25  import nl.toolforge.karma.core.history.ModuleHistory;
26  import nl.toolforge.karma.core.history.ModuleHistoryEvent;
27  import nl.toolforge.karma.core.history.ModuleHistoryException;
28  import nl.toolforge.karma.core.history.ModuleHistoryFactory;
29  import nl.toolforge.karma.core.location.Location;
30  import nl.toolforge.karma.core.manifest.ManifestException;
31  import nl.toolforge.karma.core.module.template.ModuleLayoutTemplate;
32  import nl.toolforge.karma.core.scm.digester.ModuleDependencyCreationFactory;
33  import nl.toolforge.karma.core.vc.AuthenticationException;
34  import nl.toolforge.karma.core.vc.Authenticator;
35  import nl.toolforge.karma.core.vc.Authenticators;
36  import nl.toolforge.karma.core.vc.DevelopmentLine;
37  import nl.toolforge.karma.core.vc.PatchLine;
38  import nl.toolforge.karma.core.vc.RunnerFactory;
39  import nl.toolforge.karma.core.vc.VersionControlException;
40  import nl.toolforge.karma.core.vc.cvsimpl.CVSRunner;
41  import org.apache.commons.digester.Digester;
42  import org.apache.commons.io.FileUtils;
43  import org.apache.commons.logging.Log;
44  import org.apache.commons.logging.LogFactory;
45  import org.xml.sax.SAXException;
46  
47  import java.io.File;
48  import java.io.IOException;
49  import java.util.Date;
50  import java.util.HashSet;
51  import java.util.Set;
52  import java.util.regex.PatternSyntaxException;
53  
54  /***
55   * The name says it all. This class is the base (template) for a module.
56   *
57   * @author D.A. Smedes
58   * @version $Id: BaseModule.java,v 1.2 2004/11/10 23:53:09 asmedes Exp $
59   */
60  public abstract class BaseModule implements Module {
61  
62    protected static Log logger = LogFactory.getLog(BaseModule.class);
63  
64    private Location location = null;
65    private String name = null;
66  
67    private File baseDir = null;
68  
69    private Version version = null;
70    private boolean patchLine = false;
71    private boolean developmentLine = false;
72  
73    public BaseModule(String name, Location location, Version version) {
74      this(name, location);
75      this.version = version;
76    }
77  
78    public BaseModule(String name, Location location) {
79  
80      if (!name.matches(ModuleDigester.NAME_PATTERN_STRING)) {
81        throw new PatternSyntaxException(
82            "Pattern mismatch for 'name'. Should match " + ModuleDigester.NAME_PATTERN_STRING, name, -1);
83      }
84      if (location == null) {
85        throw new IllegalArgumentException("Location cannot be null.");
86      }
87  
88      this.name = name;
89      this.location = location;
90    }
91  
92    /***
93     * Gets the modules' name.
94     *
95     * @see Module#getName
96     */
97    public final String getName() {
98      return name;
99    }
100 
101   /***
102    * Gets the modules' location.
103    *
104    * @return See {@link nl.toolforge.karma.core.location.Location}, and all implementing classes.
105    */
106   public final Location getLocation() {
107     return location;
108   }
109 
110   public boolean equals(Object obj) {
111 
112     if (obj instanceof BaseModule) {
113       if (((BaseModule) obj).getName().equals(getName()) &&
114           ((BaseModule) obj).getLocation().equals(getLocation())) {
115         return true;
116       }
117     }
118     return false;
119   }
120 
121   public int hashCode() {
122     return getName().hashCode() + getLocation().hashCode();
123   }
124 
125 
126   /***
127    * Future functionality. Not yet supported. Returns <code>false</code>.
128    *
129    * @return <code>false</code>.
130    */
131   public final boolean hasDevelopmentLine() {
132     return false;
133   }
134 
135   public final void markDevelopmentLine(boolean mark) {
136     developmentLine = mark;
137   }
138 
139   public final DevelopmentLine getPatchLine() {
140     return new PatchLine(getVersion());
141   }
142 
143   public final void markPatchLine(boolean mark) {
144     patchLine = true;
145   }
146 
147   public final Version getVersion() {
148     return version;
149   }
150 
151   /***
152    * If the module element in the manifest contains a <code>version</code> attribute, this method will return the
153    * value of that attribute.
154    *
155    * @return The module version, or <code>N/A</code>, when no version number exists.
156    */
157   public final String getVersionAsString() {
158     return (version == null ? "N/A" : version.getVersionNumber());
159   }
160 
161   public final boolean hasVersion() {
162     return version != null;
163   }
164 
165 
166   /***
167    * Checks if this module has been patched (and is thus part of a <code>ReleaseManifest</code>).
168    *
169    * @return <code>true</code> when this module has a <code>PatchLine</code> attached to it, <code>false</code> if it
170    *         hasn't.
171    */
172   public final boolean hasPatchLine() {
173     return patchLine;
174   }
175 
176   /***
177    * When initialized by <code>AbstractManifest</code>, a module is assigned its base directory, relative to the manifest. The
178    * base directory is used internally for base-directory-aware methods.
179    *
180    * @param baseDir
181    */
182   public final void setBaseDir(File baseDir) {
183 
184     if (baseDir == null) {
185       throw new IllegalArgumentException("If you use it, initialize it with a valid 'File' instance ...");
186     }
187     this.baseDir = baseDir;
188   }
189 
190   public final File getBaseDir() {
191 
192     if (baseDir == null) {
193       throw new KarmaRuntimeException("Basedir not set.");
194     }
195     return baseDir;
196   }
197 
198   public abstract ModuleLayoutTemplate getLayoutTemplate();
199 
200   /***
201    *
202    * @param createComment
203    * @throws VersionControlException
204    * @throws AuthenticationException
205    */
206   public final void createRemote(Authenticator authenticator, String createComment) throws AuthenticationException, VersionControlException {
207 
208     // Create the layout and return its location.
209     //
210     File tmpDir = null;
211     try {
212       tmpDir = MyFileUtils.createTempDirectory();
213     } catch (IOException e) {
214       throw new KarmaRuntimeException("Could not create temporary directory.");
215     }
216 
217     File moduleDir = new File(tmpDir, getName());
218     moduleDir.mkdir();
219 
220     // Create the modules' layout
221     //
222     try {
223       getLayoutTemplate().createLayout(moduleDir);
224     } catch (IOException e) {
225       // todo
226       e.printStackTrace();
227     }
228 
229     logger.debug("Created layout for module `" + getName() + "`");
230 
231     // Add the module to the version control system
232     //
233     setBaseDir(moduleDir);
234 
235     ModuleDescriptor descriptor = new ModuleDescriptor(this);
236     try {
237       descriptor.createFile(moduleDir);
238     } catch (IOException e) {
239       // todo
240       e.printStackTrace();
241     }
242 
243     // Prepare the module history
244     //
245     ModuleHistory history = null;
246     try {
247       history = ModuleHistoryFactory.getInstance(getBaseDir()).getModuleHistory(this);
248     } catch (ModuleHistoryException e) {
249       throw new OutOfTheBlueException("Module history does not yet exist, so this is impossible.");
250     }
251 
252     ModuleHistoryEvent event = new ModuleHistoryEvent();
253     event.setType(ModuleHistoryEvent.CREATE_MODULE_EVENT);
254     event.setVersion(Version.INITIAL_VERSION);
255     event.setDatetime(new Date());
256 
257     // Is a requirement.
258     //
259     Authenticator a = Authenticators.getAuthenticator(authenticator);
260 
261     event.setAuthor(a.getUsername());
262     event.setComment(createComment);
263     history.addEvent(event);
264     try {
265       history.save();
266     } catch (ModuleHistoryException mhe) {
267       logger.error("Troubles when saving the module history.", mhe);
268     }
269 
270     CVSRunner runner = (CVSRunner) RunnerFactory.getRunner(getLocation());
271     runner.addModule(this, createComment);
272 
273     try {
274       FileUtils.deleteDirectory(tmpDir);
275     } catch (IOException e) {
276       logger.warn("Could not remove temporary directory. It is probably still locked by the OS.");
277     }
278   }
279 
280   /***
281    * Reads <code>module-descriptor.xml</code>-file from the module base directory. If the base directory does not exist,
282    * <code>Module.UNKNOWN</code> is returned.
283    *
284    * @return The module type.
285    * @throws nl.toolforge.karma.core.module.ModuleTypeException When <code>module-descriptor</code> is non-existing. This is possible when the
286    *   module is not locally available.
287    */
288   public final Type getType() throws ModuleTypeException {
289 
290     try {
291       getBaseDir();
292     } catch (KarmaRuntimeException k) {
293       return Module.UNKNOWN;
294     }
295 
296     if (!new File(getBaseDir(), Module.MODULE_DESCRIPTOR).exists()) {
297       throw new ModuleTypeException(ModuleTypeException.MISSING_MODULE_DESCRIPTOR);
298     }
299 
300     Digester digester = new Digester();
301 
302     digester.addObjectCreate("module-descriptor", Module.Type.class);
303     digester.addCallMethod("module-descriptor/type", "setType", 0);
304 
305     try {
306       return (Type) digester.parse(new File(getBaseDir(), Module.MODULE_DESCRIPTOR).getPath());
307     } catch (IOException e) {
308       throw new ModuleTypeException(ModuleTypeException.INVALID_MODULE_DESCRIPTOR);
309     } catch (SAXException e) {
310       throw new ModuleTypeException(ModuleTypeException.INVALID_MODULE_DESCRIPTOR);
311     }
312   }
313 
314   /***
315    * See {@link Module#getDependencies}. This implementation throws a <code>KarmaRuntimeException</code> when the
316    *  modules' <code>dependencies.xml</code> could not be parsed properly. When no dependencies have been specified, or
317    * when the file does not exist, the method returns an empty <code>Set</code>.
318    *
319    * @return A <code>Set</code> containing {@link nl.toolforge.karma.core.scm.ModuleDependency} instances.
320    */
321   public final Set getDependencies() {
322 
323     Set dependencies = new HashSet();
324 
325     // Read in the base dependency structure of a Maven project.xml file
326     //
327     Digester digester = new Digester();
328 
329     digester.addObjectCreate("*/dependencies", HashSet.class);
330     digester.addFactoryCreate("*/dependency", ModuleDependencyCreationFactory.class);
331     digester.addSetNext("*/dependency", "add");
332 
333     try {
334 
335       dependencies = (Set) digester.parse(new File(getBaseDir(), "dependencies.xml"));
336 
337     } catch (IOException e) {
338       return new HashSet();
339     } catch (SAXException e) {
340       throw new KarmaRuntimeException(ManifestException.DEPENDENCY_FILE_LOAD_ERROR, new Object[]{getName()});
341     }
342     return dependencies;
343   }
344 
345   /***
346    * Returns the module name.
347    *
348    * @return
349    */
350   public String toString() {
351     return getName();
352   }
353 
354 }