1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
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
221
222 try {
223 getLayoutTemplate().createLayout(moduleDir);
224 } catch (IOException e) {
225
226 e.printStackTrace();
227 }
228
229 logger.debug("Created layout for module `" + getName() + "`");
230
231
232
233 setBaseDir(moduleDir);
234
235 ModuleDescriptor descriptor = new ModuleDescriptor(this);
236 try {
237 descriptor.createFile(moduleDir);
238 } catch (IOException e) {
239
240 e.printStackTrace();
241 }
242
243
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
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
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 }