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.boot;
20  
21  import nl.toolforge.karma.core.ErrorCode;
22  import nl.toolforge.karma.core.KarmaRuntimeException;
23  import nl.toolforge.karma.core.location.LocationException;
24  import nl.toolforge.karma.core.location.LocationLoader;
25  import nl.toolforge.karma.core.manifest.ManifestCollector;
26  import nl.toolforge.karma.core.manifest.ManifestLoader;
27  import org.apache.commons.io.FileUtils;
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  import org.apache.log4j.FileAppender;
31  import org.apache.log4j.Level;
32  import org.apache.log4j.Logger;
33  import org.apache.log4j.PatternLayout;
34  
35  import java.io.File;
36  import java.io.FileInputStream;
37  import java.io.FileNotFoundException;
38  import java.io.IOException;
39  import java.util.Properties;
40  
41  /***
42   * <p>A <code>WorkingContext</code> is used by Karma to determine the environment in which the user wants to use Karma. A
43   * working context is represented on your local harddisk by a directory in which a developers' project work will be
44   * stored. The <code>WorkingContext</code> class is the bridge from Karma domain objects (<code>Manifest</code> and
45   * <code>Module</code> to name the most important ones) to a developer's harddisk.
46   *
47   * <p>A WorkingContext should be configured before it can be constructed. The
48   * {@link #configure(WorkingContextConfiguration)}-method should be called to configure a WorkingContext.
49   *
50   * @author D.A. Smedes
51   * @version $Id: WorkingContext.java,v 1.24 2004/11/16 22:18:28 hippe Exp $
52   */
53  public final class WorkingContext {
54  
55    public final static ErrorCode CANNOT_REMOVE_ACTIVE_WORKING_CONTEXT = new ErrorCode("WCO-00001");
56  
57    public final static String WORKING_CONTEXT_PREFERENCE = "karma.working-context";
58  
59    private final static String DEFAULT_CONVERSION_PATTERN = "%d{HH:mm:ss} [%5p] - %m%n";
60  
61    static {
62  
63      // Configure the logging system.
64      //
65      try {
66  
67        if (WorkingContext.class.getClassLoader().getResource("log4j.xml") != null) {
68  
69          Logger.getLogger(WorkingContext.class).info("'Log4j.xml' used to for logging configuration.");
70  
71        } else {
72  
73          // Initialize default logging.
74  
75          Logger root = Logger.getRootLogger();
76  
77          String karmaHome = System.getProperty("karma.home", System.getProperty("user.home"));
78  
79          // Create the log directory
80          //
81          new File(karmaHome, "logs").mkdirs();
82  
83          File defaultLogFile = new File(karmaHome, "logs/karma-default.log");
84  
85          PatternLayout patternLayout = new PatternLayout(DEFAULT_CONVERSION_PATTERN);
86          //the log file will be truncated everytime it is opened.
87          FileAppender fileAppender = new FileAppender(patternLayout, defaultLogFile.getPath(), false);
88          fileAppender.setName("Default Karma logging appender.");
89  
90          // The default Appender for a Logger. We don't want it.
91          //
92          root.removeAppender(root.getAppender("console"));
93  
94          root.addAppender(fileAppender);
95  
96          String logLevel = null;
97          logLevel = (System.getProperty("loglevel") == null ? "DEBUG" : System.getProperty("loglevel")); // Pass 1
98          logLevel = (logLevel.toUpperCase().matches("ALL|DEBUG|ERROR|FATAL|INFO|OFF|WARN") ? logLevel : "DEBUG");  // Pass 2
99  
100         root.setLevel(Level.toLevel(logLevel));
101 
102         // By default, disable commons.digester messages.
103         //
104         Logger dig = Logger.getLogger("org.apache.commons.digester");
105         dig.isAttached(fileAppender);
106         dig.setLevel(Level.OFF);
107 
108         Logger.getLogger(WorkingContext.class).info(
109             "Default logging configuration enabled; override by placing 'log4j.xml' and 'log4j.dtd' on your classpath.");
110       }
111 
112     } catch (Exception e) {
113       e.printStackTrace();
114       throw new KarmaRuntimeException("*** PANIC *** Log4j system could not be initialized.");
115     }
116   }
117 
118   public static final String CONFIGURATION_BASE_DIRECTORY = System.getProperty("user.home") + File.separator + ".karma";
119 
120 
121   /***
122    * Property indicating the base directory for development projects. All manifests will be checked out under this
123    * directory, and the manifest store and location store are checked out at this location as well.
124    */
125   public static final String PROJECT_BASE_DIRECTORY_PROPERTY = "project.basedir";
126 
127   /***
128    * Property indicating the root of a repository directory `Maven style`. For those who don't know Maven, check out
129    * the <a href="http://maven.apache.org">Maven</a> website. This directory does not have to be the default Maven
130    * repository directory. It can be any directory on a users' harddisk, as long as binary dependencies can be resolved
131    * `Maven style`, because that is what Karma does as well.
132    */
133   public static final String PROJECT_LOCAL_REPOSITORY_PROPERTY = "project.local.repository";
134 
135   public static final String MANIFEST_STORE_MODULE = "manifest-store.module";
136   public static final String LOCATION_STORE_MODULE = "location-store.module";
137 
138   /***
139    * The property that identifies the local directory where jar dependencies can be found. Dependencies are
140    * resolved Maven style, but to support environments where Maven is not available, the directory is configurable.
141    * The default Maven repository is used when this property is not set in <code>karma.properties</code>.
142    */
143 
144   /*** The default working context. */
145   public static final String DEFAULT = "default";
146 
147   private static File localRepository = null;
148 
149   private String workingContext = null;
150 
151   private ManifestCollector manifestCollector = null;
152   private ManifestLoader manifestLoader = null;
153   private LocationLoader locationLoader = null;
154 
155   private WorkingContextConfiguration configuration = null;
156 
157   private static final Log logger = LogFactory.getLog(WorkingContext.class);
158 
159   private static File configurationBaseDir = null;
160 
161   /***
162    * Constructs a <code>WorkingContext</code> in the default configuration base directory. The
163    * {@link #configure(WorkingContextConfiguration)}-method should be called to configure this working context.
164    *
165    * @param workingContext A working context name. If <code>workingContext</code> doesn't match the <code>\w+</code>
166    *                       pattern, {@link DEFAULT} is assumed.
167    */
168   public WorkingContext(String workingContext) {
169     this(workingContext, new File(CONFIGURATION_BASE_DIRECTORY));
170   }
171 
172   /***
173    * Constructs a <code>WorkingContext</code> with <code>configBaseDir</code> as the configuration base directory. When
174    * <code>configBaseDir</code> does not exist, it will be created. The {@link #configure(WorkingContextConfiguration)}-
175    * method should be called to configure this working context.
176    *
177    * @param workingContext A working context name. If <code>workingContext</code> doesn't match the <code>\w+</code>
178    *                       pattern, {@link DEFAULT} is assumed.
179    * @param configBaseDir  The configuration base directory. If the directory does not exist, it will be created.
180    */
181   public WorkingContext(String workingContext, File configBaseDir) {
182 
183     if (!workingContext.matches("//w[//w//-]*")) {
184       workingContext = DEFAULT;
185     }
186     this.workingContext = workingContext;
187 
188     if (configBaseDir == null) {
189       throw new IllegalArgumentException("Configuration base directory cannot be null.");
190     }
191     configurationBaseDir = configBaseDir;
192     configurationBaseDir.mkdirs();
193   }
194 
195   /***
196    * Returns a <code>File</code> reference to the default base directory for Karma configuration files. When the
197    * directory does not exist, it is created.
198    *
199    * @return A <code>File</code> reference to the default base directory for Karma configuration files.
200    */
201   public static File getConfigurationBaseDir() {
202 
203     if (configurationBaseDir == null) {
204       throw new KarmaRuntimeException(
205           "For all practical purposes, the configuration base directory has to " +
206           "be initialized. The static call you made should be preceded once by calling the " +
207           "WorkingContext constructor (will be replaced by a better solution in later releases).");
208     }
209     return configurationBaseDir;
210   }
211 
212   public void configure(WorkingContextConfiguration configuration) {
213     if (configuration == null) {
214       throw new IllegalArgumentException("Configuration cannot be null for a working context.");
215     }
216 
217     this.configuration = configuration;
218 
219     String p = configuration.getProperty(PROJECT_LOCAL_REPOSITORY_PROPERTY);
220     if (p == null || "".equals(p)) {
221       localRepository = new File(System.getProperty("user.home"), ".maven/repository");
222     } else {
223       localRepository = new File(p);
224     }
225   }
226 
227   /***
228    * Returns the name of this working context.
229    *
230    * @return The name of this working context.
231    */
232   public String getName() {
233     return workingContext;
234   }
235 
236   /***
237    * Get the configuration for this working context or <code>null</code> it this working context had not been
238    * configured.
239    *
240    * @return The configuration for this working context.
241    */
242   public WorkingContextConfiguration getConfiguration() {
243     return configuration;
244   }
245 
246   /***
247    * Get the properties of this working context. The properties are
248    * stored in the karma.properties, which are located in the project base dir.
249    *
250    * @return A Properties object containing the properties of this working
251    * context or an empty Properties object when something went wrong.
252    */
253   public Properties getProperties() {
254     Properties properties = new Properties();
255     try {
256       properties.load(new FileInputStream(new File(getProjectBaseDirectory(), "karma.properties")));
257     } catch (FileNotFoundException fnfe) {
258       logger.info("karma.properties not found for working context '"+getName()+"'.");
259     } catch (IOException ioe) {
260       logger.error("karma.properties could not be loaded for working context '"+getName()+"'", ioe);
261     }
262     return properties;
263   }
264 
265   /***
266    * Removes a working contexts' configuration directory.
267    */
268   public synchronized void remove() throws IOException {
269     FileUtils.deleteDirectory(getWorkingContextConfigurationBaseDir());
270   }
271 
272 
273   /***
274    * Returns a <code>File</code> reference to the configuration directory for the current working context. When the
275    * directory does not exist, it is created. This method will return the directory <code>File</code> reference to
276    * <code>$HOME/.karma/working-contexts/&lt;working-context-name&gt;</code>.
277    *
278    * @return a <code>File</code> reference to the configuration directory for the current working context.
279    */
280   public File getWorkingContextConfigurationBaseDir() {
281 
282     // Create the base configuration directory the base directory where working contexts are stored.
283     //
284     File workingContextsDir = new File(configurationBaseDir, "working-contexts");
285     workingContextsDir.mkdir();
286 
287     File file = new File(workingContextsDir, workingContext);
288     if (!file.exists()) {
289       file.mkdir();
290     }
291     return file;
292   }
293 
294   /***
295    * Returns a <code>File</code> reference to the project base directory, which can be configured by the
296    * <code>projects.basedir</code> property in the <code>working-context.xml</code> file.
297    *
298    * @return a <code>File</code> reference to the project base directory.
299    */
300   public File getProjectBaseDirectory() {
301 
302     String projectBaseDirProperty = getConfiguration().getProperty(PROJECT_BASE_DIRECTORY_PROPERTY);
303 
304     if (projectBaseDirProperty == null) {
305       throw new KarmaRuntimeException("Property `project.basedir` is not configured for working context `" + this + "`.");
306     }
307 
308     File projectBaseDir = new File(projectBaseDirProperty);
309     if (!projectBaseDir.exists()) {
310       projectBaseDir.mkdir();
311     }
312     return projectBaseDir;
313   }
314 
315   /***
316    * Returns a <code>File</code> reference to the administration directory for the working context. In the
317    * administration directory, the manifest store and the location store are located for the current working context.
318    *
319    * @return A reference to the administration directory for the current working context.
320    */
321   public File getAdminDir() {
322 
323     File m = new File(getProjectBaseDirectory(), ".admin");
324     if (!m.exists()) {
325       m.mkdir();
326     }
327 
328     return m;
329   }
330 
331   /***
332    * Returns a <code>File</code> reference to the manifest store directory for the working context. When the directory
333    * does not exist, it will be created. In this directory, the manifest store will be checked out.
334    *
335    * @return A reference to the manifest store directory.
336    */
337   public File getManifestStoreBasedir() {
338 
339     File l = new File(getAdminDir(), "manifest-store");
340     if (!l.exists()) {
341       l.mkdirs();
342     }
343 
344     return l;
345   }
346 
347   /***
348    * Returns a <code>File</code> reference to the location store directory for the working context. When the directory
349    * does not exist, it will be created. In this directory, the location store will be checked out.
350    *
351    * @return A reference to the location store directory.
352    */
353   public File getLocationStoreBasedir() {
354 
355     File l = new File(getAdminDir(), "location-store");
356     if (!l.exists()) {
357       l.mkdirs();
358     }
359 
360     return l;
361   }
362 
363   /***
364    * See {@link PROJECT_LOCAL_REPOSITORY_PROPERTY}. When the property is not set, the default repository is
365    * assumed to be in {@link #getConfigurationBaseDir()}/<code>.repository</code>.
366    *
367    * @return See method description.
368    *
369    * @see PROJECT_LOCAL_REPOSITORY_PROPERTY
370    */
371   public static File getLocalRepository() {
372     return localRepository;
373   }
374 
375   public static File getKarmaHome() {
376 
377     if (System.getProperty("karma.home") == null) {
378       throw new KarmaRuntimeException("KARMA_HOME (karma.home) environment variable has not been set.");
379     }
380 
381     return new File(System.getProperty("karma.home"));
382   }
383 
384   /***
385    * <p>Determines the last used manifest for this working context. This fact is maintained in the <code>.java</code> file
386    * on a users' harddisk, as per the specification for <code>java.template.prefs</code>, included in the JDK since
387    * <code>1.4</code>.
388    *
389    * <p>A <code>String</code> made up of the working context name and <code>karma.manifest.last</code>.
390    */
391   public String getContextManifestPreference() {
392     return getName() + "." + "karma.manifest.last";
393   }
394 
395   /***
396    * Returns a reference to the <code>LocationLoader</code> for the working context.
397    * @return A location loader.
398    */
399   public LocationLoader getLocationLoader() throws LocationException {
400     if (locationLoader == null) {
401       locationLoader = new LocationLoader(this);
402       locationLoader.load();
403     }
404     return locationLoader;
405   }
406 
407   /***
408    * Returns a reference to the <code>ManifestCollector</code> for the working context.
409    * @return A manifest loader.
410    */
411   public ManifestCollector getManifestCollector() {
412     if (manifestCollector == null) {
413       manifestCollector = new ManifestCollector(this);
414     }
415     return manifestCollector;
416   }
417 
418   /***
419    * Returns a reference to the <code>ManifestLoader</code> for the working context.
420    * @return A manifest loader.
421    */
422   public ManifestLoader getManifestLoader() {
423     if (manifestLoader == null) {
424       manifestLoader = new ManifestLoader(this);
425     }
426     return manifestLoader;
427   }
428 
429   /***
430    * Returns the working contexts' name.
431    *
432    * @return The working contexts' name.
433    */
434   public String toString() {
435     return getName();
436   }
437 
438 }