EMMA Coverage Report (generated Tue Nov 24 15:49:41 EST 2009)
[all classes][atg.nucleus]

COVERAGE SUMMARY FOR SOURCE FILE [NucleusTestUtils.java]

nameclass, %method, %block, %line, %
NucleusTestUtils.java100% (2/2)68%  (23/34)53%  (721/1355)56%  (158.6/283)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class NucleusTestUtils$NucleusStartupOptions100% (1/1)58%  (7/12)33%  (35/105)42%  (13/31)
NucleusTestUtils$NucleusStartupOptions (String [], Class, String): void 0%   (0/1)0%   (0/23)0%   (0/6)
getLayers (): String [] 0%   (0/1)0%   (0/3)0%   (0/1)
modifyNucleusCommandLineOptions (List): void 0%   (0/1)0%   (0/1)0%   (0/1)
setLayers (String []): void 0%   (0/1)0%   (0/4)0%   (0/2)
setLiveconfig (boolean): void 0%   (0/1)0%   (0/4)0%   (0/2)
getLayersAsString (): String 100% (1/1)12%  (5/40)25%  (2/8)
NucleusTestUtils$NucleusStartupOptions (String [], Class, String, String): void 100% (1/1)100% (15/15)100% (6/6)
getBaseConfigDirectory (): String 100% (1/1)100% (3/3)100% (1/1)
getClassRelativeTo (): Class 100% (1/1)100% (3/3)100% (1/1)
getInitialService (): String 100% (1/1)100% (3/3)100% (1/1)
getLiveconfig (): boolean 100% (1/1)100% (3/3)100% (1/1)
getModules (): String [] 100% (1/1)100% (3/3)100% (1/1)
     
class NucleusTestUtils100% (1/1)73%  (16/22)55%  (686/1250)58%  (145.6/252)
findFreePort (): int 0%   (0/1)0%   (0/32)0%   (0/11)
getConfigpath (String): File 0%   (0/1)0%   (0/5)0%   (0/1)
getConfigpath (String, boolean): File 0%   (0/1)0%   (0/5)0%   (0/1)
setAbsoluteName (String, GenericService): void 0%   (0/1)0%   (0/4)0%   (0/2)
startNucleusWithModules (String [], Class, String): Nucleus 0%   (0/1)0%   (0/17)0%   (0/1)
urlToFile (URL): File 0%   (0/1)0%   (0/59)0%   (0/10)
findDynamoRoot (): String 100% (1/1)9%   (16/182)16%  (7/45)
extractJarDataURL (URL): File 100% (1/1)13%  (14/104)16%  (3/19)
shutdownNucleus (Nucleus): void 100% (1/1)35%  (31/88)53%  (7.4/14)
createTempServerDir (): File 100% (1/1)59%  (37/63)78%  (7/9)
getGlobalTestConfig (): String 100% (1/1)70%  (7/10)75%  (3/4)
startNucleusWithModules (NucleusTestUtils$NucleusStartupOptions): Nucleus 100% (1/1)73%  (185/255)76%  (38/50)
createProperties (String, File, String, Properties): File 100% (1/1)82%  (97/119)90%  (18.8/21)
createInitial (File, List): File 100% (1/1)91%  (40/44)93%  (7.4/8)
getConfigpath (Class, String, boolean): File 100% (1/1)98%  (118/121)96%  (26/27)
addComponent (Nucleus, String, Object): void 100% (1/1)99%  (84/85)93%  (13/14)
<static initializer> 100% (1/1)100% (17/17)100% (5/5)
NucleusTestUtils (): void 100% (1/1)100% (3/3)100% (2/2)
getConfigpath (Class, String): File 100% (1/1)100% (5/5)100% (1/1)
startNucleus (File): Nucleus 100% (1/1)100% (4/4)100% (1/1)
startNucleus (String): Nucleus 100% (1/1)100% (19/19)100% (5/5)
startNucleusWithModules (String [], Class, String, String): Nucleus 100% (1/1)100% (9/9)100% (1/1)

1/**
2 * Copyright 2009 ATG DUST Project
3 * 
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * 
7 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8 * 
9 * Unless required by applicable law or agreed to in writing, software 
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and limitations under the License.
13 */
14package atg.nucleus;
15 
16import java.io.File;
17import java.io.FileWriter;
18import java.io.IOException;
19import java.net.MalformedURLException;
20import java.net.ServerSocket;
21import java.net.URI;
22import java.net.URISyntaxException;
23import java.net.URL;
24import java.util.ArrayList;
25import java.util.Collections;
26import java.util.HashMap;
27import java.util.Iterator;
28import java.util.List;
29import java.util.Map;
30import java.util.Properties;
31 
32import javax.servlet.ServletException;
33 
34import org.apache.log4j.Logger;
35 
36import atg.applauncher.AppLauncher;
37import atg.applauncher.AppLauncherException;
38import atg.applauncher.AppModuleManager;
39import atg.applauncher.MultiInstallLocalAppModuleManager;
40import atg.applauncher.dynamo.DynamoServerLauncher;
41import atg.core.io.FileUtils;
42import atg.core.util.CommandProcessor;
43import atg.core.util.JarUtils;
44import atg.core.util.StringUtils;
45import atg.nucleus.naming.ComponentName;
46import atg.nucleus.servlet.NucleusServlet;
47import atg.service.dynamo.ServerConfig;
48import atg.test.util.FileUtil;
49 
50/**
51 * NucleusTestUtils
52 * @author adamb
53 * @version $Id: //test/UnitTests/base/main/src/Java/atg/nucleus/NucleusTestUtils.java#21 $
54 * 
55 * This class contains some utility methods to make it faster
56 * to write a unit test that needs to resolve componants against Nucleus.
57 *
58 */
59public class NucleusTestUtils {
60 
61  //-------------------------------------
62  /** Class version string */
63 
64  public static String CLASS_VERSION = "$Id: //test/UnitTests/base/main/src/Java/atg/nucleus/NucleusTestUtils.java#21 $$Change: 556195 $";
65 
66  private static final String EXTRACT_TEMP_JAR_FILE_FOR_PATH = "Extract temp jar file for path ";
67  private static final String ATG_DUST_CONFIG = "atg-dust-config-";
68  private static final String FILE                             = "file:";
69  public static final Logger log                              = Logger
70                                                                   .getLogger(NucleusTestUtils.class);
71  public static final String ATG_DUST_TESTCONFIG = "atg.dust.testconfig";
72  private static final String ATG_DUST_TESTCONFIG_ENV = "ATG_DUST_TESTCONFIG";
73 
74  /** A map from Nucleus instance to temporary directory. Used by
75   * startNucleusWithModules. */
76  static Map<Nucleus, File> sNucleusToTempAtgServerDirectory =
77    Collections.synchronizedMap(new HashMap<Nucleus, File>());
78 
79  /** Whether or not to remove tempoary ATG server directories
80   * created by startNucleusWithModules(). True by default, but
81   * can be set to false for debugging. */
82  public static boolean sRemoveTempAtgServerDirectories = true;
83  
84  
85  /**
86   * Creates an Initial.properties file
87   * pRoot The root directory of the configpath
88   * pInitialServices A list of initial services
89   * @param pRoot The root of the config path entry.
90   * @param pInitialServices initial services list
91   * @return the create initial services properties file. 
92   * @exception IOException if an error occurs
93   */
94  public static File createInitial(File pRoot,List pInitialServices) throws IOException {
95    Properties prop = new Properties();
96    Iterator iter = pInitialServices.iterator();
97    StringBuffer services = new StringBuffer();
98    while (iter.hasNext()) {
99      if (services.length() != 0) services.append(",");
100      services.append((String)iter.next());
101    }
102    prop.put("initialServices",services.toString());
103    return NucleusTestUtils.createProperties("Initial", new File(pRoot.getAbsolutePath()),"atg.nucleus.InitialService", prop);
104  }
105  
106  /**
107   * Allows the absoluteName of the given service to be explicitly defined.
108   * Normally this is determined by the object's location in the Nucleus
109   * hierarchy.  For test items that are not really bound to Nucleus, it's
110   * convenient to just give it an absolute name rather than going through
111   * the whole configuration and binding process.
112   
113   * @param pName The absolute name value to set
114   * @param pService The service whose absolute nameshould be set.
115   */
116  public static void setAbsoluteName(String pName, GenericService pService) {
117    pService.mAbsoluteName = pName;
118  }
119  // ---------------------
120  /**
121   * Adds the given object, pComponent to Nucleus, pNucleus at the path given
122   * by pComponentPath.
123   * @param pNucleus The Nucleus instance to which the component should be added
124   * @param pComponentPath the component path at which the component should be added
125   * @param pComponent the component instance to add
126   */
127  public static void addComponent(
128    Nucleus pNucleus,
129    String pComponentPath,
130    Object pComponent) {
131    // make sure it's not already there
132    if (pNucleus.resolveName(pComponentPath) != null)
133      return;
134    ComponentName name = ComponentName.getComponentName(pComponentPath);
135    ComponentName[] subNames = name.getSubNames();
136    GenericContext[] contexts = new GenericContext[subNames.length - 1];
137    contexts[0] = pNucleus;
138    for (int i = 1; i < subNames.length - 1; i++) {
139      contexts[i] = new GenericContext();
140      // Make sure it's not there
141      GenericContext tmpContext =
142        (GenericContext) contexts[i - 1].getElement(subNames[i].getName());
143      if (tmpContext == null)
144        contexts[i - 1].putElement(subNames[i].getName(), contexts[i]);
145      else
146        contexts[i] = tmpContext;
147    }
148    contexts[contexts.length
149      - 1].putElement(subNames[subNames.length - 1].getName(), pComponent);
150  }
151  
152  /**
153   * Creates a .properties file
154   * @param pComponentName Name of the component
155   * @param pConfigDir Name of the configuration directory. If null,
156   *   will add to the current working directory.
157   * @param pClass The class of the component (used for $class property).
158   * @param pProps Other properties of the component.
159   * @return The created file. 
160   * @throws IOException
161   */
162  public static File createProperties(String pComponentName, File pConfigDir, String pClass, Properties pProps)
163  throws IOException {
164  File prop;
165  if (pConfigDir == null)
166    prop = new File("./" + pComponentName + ".properties");
167  else {
168    pConfigDir.mkdirs();
169    prop = new File(pConfigDir, pComponentName + ".properties");
170    new File(prop.getParent()).mkdirs();
171  }
172  
173  if (prop.exists()) prop.delete();
174  prop.createNewFile();
175  FileWriter fw = new FileWriter(prop);
176  String classLine = "$class=" + pClass + "\n";
177  try {
178    if (pClass != null) fw.write(classLine);
179    if (pProps != null) {
180      Iterator iter = pProps.keySet().iterator();
181      while (iter.hasNext()) {
182        String key = (String) iter.next();
183        String thisLine = key + "=" + StringUtils.replace(pProps.getProperty(key),'\\', "\\\\") + "\n";
184        fw.write(thisLine);
185      }
186    }
187  }
188  finally {
189    fw.flush();
190    fw.close();
191  }
192  return prop;
193}
194  /**
195   * Starts Nucleus using the given config directory
196   * @param configpath the config path directory entry
197   *  to use as the entire config path.
198   * @return the started Nucleus
199   */
200  public static  Nucleus startNucleus(File configpath) {
201    return startNucleus(configpath.getAbsolutePath());
202  }
203  
204  /**
205   * Starts Nucleus using the given config directory
206   * @param pSingleConfigpathEntry the path name of the config path
207   *  entry to specify.
208   * @return The started nucleus. 
209   */
210  public static Nucleus startNucleus(String pSingleConfigpathEntry) {
211    System.setProperty("atg.dynamo.license.read", "true");
212    System.setProperty("atg.license.read", "true");    
213    NucleusServlet.addNamingFactoriesAndProtocolHandlers();
214    Nucleus n = Nucleus.startNucleus(new String[] {pSingleConfigpathEntry});
215    return n;
216  }
217 
218 
219  /** Cache of the config path for a given Class. Used by getConfigpath. */
220  static Map<Class, Map<String, File>> sConfigDir =
221    new HashMap<Class, Map<String, File>>();
222 
223 
224  /**
225   * A convenience method for returning the configpath for a test.
226   * pConfigDirectory is the top level name to be used for the configpath.
227   * Returns a file in the "config/data" subdirectory of the passed in file.
228   *
229   * @param pBaseConfigDirectory the base configuration directory.
230   * @return The calculated configuration path.
231   */
232  public static File getConfigpath(String pBaseConfigDirectory) {
233    return getConfigpath(NucleusTestUtils.class, pBaseConfigDirectory, true);
234  }
235 
236 
237  /**
238   * A convenience method for returning the configpath for a test.
239   * pConfigDirectory is the top level name to be used for the configpath.
240   * Returns a file in the "data" subdirectory of the passed in file.
241   *
242   * @param pBaseConfigDirectory the base configuration directory.
243   * @param pCreate whether to create the config/data subdirectory if
244   *  it does not exist.
245   *  
246   *
247   * @return The calculated configuration path.
248   */
249  public static File getConfigpath(String pBaseConfigDirectory, boolean pCreate) {
250    return getConfigpath(NucleusTestUtils.class, pBaseConfigDirectory, pCreate);
251  }
252  /**
253   * A convenience method for returning the configpath for a test.
254   * pConfigDirectory is the top level name to be used for the configpath.
255   * Returns a file in the pBaseConfigDirectory (or pBaseConfigDirectory +
256   * "data") subdirectory of the the passed in class's location.
257   * <P>
258   * The directory location is calculated as (in psuedocode): <code>
259   *   (pClassRelativeTo's package location) + "/" + (pConfigDirectory or "data") + "/config"
260   * </code>
261   * This method always creates the config/data subdirectory if it does not
262   * exist.
263   * 
264   * @param pClassRelativeTo
265   *          the class whose package the config/data (or
266   *          pBaseConfigDirectory/data) should be relative in.
267   * @param pBaseConfigDirectory
268   *          the base configuration directory If null, uses "config".
269   * @return The calculated configuration path.
270   */
271 
272  public static File getConfigpath(Class pClassRelativeTo,
273      String pBaseConfigDirectory) {
274    return getConfigpath(pClassRelativeTo, pBaseConfigDirectory, true);
275  }
276 
277  
278  
279  /**
280   * A convenience method for returning the configpath for a test.
281   * pConfigDirectory is the top level name to be used for the configpath.
282   * Returns a file in the pBaseConfigDirectory (or pBaseConfigDirectory +
283   * "data") subdirectory of the the passed in class's location.<P>
284   *
285   * The directory location is calculated as (in psuedocode):
286   * <code>
287   *   (pClassRelativeTo's package location) + "/" + (pConfigDirectory or "data") + "/config"
288   * </code>
289   * 
290   *
291   * @param pClassRelativeTo the class whose package the config/data
292   *  (or pBaseConfigDirectory/data) should be relative in.
293   * @param pBaseConfigDirectory the base configuration directory If null,
294   *  uses "config".
295   * @param pCreate whether to create the config/data subdirectory if
296   *  it does not exist.
297   *  
298   *
299   * @return The calculated configuration path.
300   */
301  public static File getConfigpath(Class pClassRelativeTo, String pBaseConfigDirectory, boolean pCreate) {
302    //System.out.println("getConfigpath(" +
303    //                   pClassRelativeTo + ", " + 
304    //                   pBaseConfigDirectory + "," + pCreate + ")");
305    Map<String, File> baseConfigToFile = sConfigDir.get(pClassRelativeTo);
306    if (baseConfigToFile == null) {
307      baseConfigToFile = new HashMap<String, File>();
308      sConfigDir.put(pClassRelativeTo, baseConfigToFile);
309    }
310 
311    File fileFound = baseConfigToFile.get(pBaseConfigDirectory);
312 
313    if (fileFound == null) {
314      String configdirname = "config";
315      String packageName = StringUtils.replace(pClassRelativeTo.getPackage()
316          .getName(), '.', "/");
317      if (pBaseConfigDirectory != null)
318        configdirname = pBaseConfigDirectory;
319 
320      String configFolder = packageName + "/data/" + configdirname;
321      URL dataURL = pClassRelativeTo.getClassLoader().getResource(configFolder);
322 
323      // Mkdir
324      if (dataURL == null) {
325        URL root = pClassRelativeTo.getClassLoader().getResource(packageName);
326 
327        File f = new File(root.getFile());
328        File f2 = new File(f, "/data/" + configdirname);
329        if (pCreate) {
330          f2.mkdirs();
331        }
332        dataURL = NucleusTestUtils.class.getClassLoader().getResource(configFolder);
333        if (dataURL == null) {
334          System.err.println("Warning: Could not find resource \"" +
335                             configFolder + "\" in CLASSPATH");
336        }
337      }
338      if (dataURL != null) {// check if this URL is contained within a jar file
339        // if so, extract to a temp dir, otherwise just return
340        // the directory
341        fileFound = extractJarDataURL(dataURL);
342        baseConfigToFile.put(pBaseConfigDirectory ,fileFound);
343      }
344    }
345    if (fileFound != null)
346      System.setProperty("atg.configpath",
347                       fileFound.getAbsolutePath());
348    return fileFound;
349  }
350 
351  
352  // ------------------------------------
353  /**
354   * This method is used to extract a configdir from a jar archive.
355   * Given a URL this method will extract the jar contents to a temp dir and return that path.
356   * It also adds a shutdown hook to cleanup the tmp dir on normal jvm completion.
357   * If the given URL does not appear to be a path into a jar archive, this method returns
358   * a new File object initialied with <code>dataURL.getFile()</code>.
359   * @ param A URL referring to an archive path contained within a jar. Ex: jar:file:foo.jar!/atg/data/config
360   * @return A temporary directory to be used as a configdir
361   */
362  private static File extractJarDataURL(URL dataURL) {
363    // TODO: Extract to a temp location
364    // atg.core.util.JarUtils.extractEntry(arg0, arg1, arg2)
365    int endIndex = dataURL.getFile().lastIndexOf('!');
366    if (endIndex == -1) {
367      // Not a jar file url
368      return new File(dataURL.getFile());
369    }
370    log.info(EXTRACT_TEMP_JAR_FILE_FOR_PATH + dataURL.getFile());
371    File configDir = null;
372    try {
373      File tmpFile = File.createTempFile("atg-dust" + System.currentTimeMillis() ,".tmp");
374          tmpFile.deleteOnExit();
375      final File tmpDir = new File(tmpFile.getParentFile(), ATG_DUST_CONFIG
376          + System.currentTimeMillis());
377 
378      String jarPath = dataURL.getFile().substring(0, endIndex);
379      // Strip leading file:
380      int fileColonIndex = jarPath.indexOf(FILE) + FILE.length();
381      jarPath = jarPath.substring(fileColonIndex, jarPath.length());
382      JarUtils.unJar(new File(jarPath), tmpDir, false);
383      // Now get the configpath dir relative to this temp dir
384      String relativePath = dataURL.getFile().substring(endIndex + 1,
385          dataURL.getFile().length());
386      // Add a shutdown hook to delete this temp directory
387      FileUtil.deleteDirectoryOnShutdown(tmpDir);
388      configDir = new File(tmpDir, relativePath);
389    } catch (IOException e) {
390      e.printStackTrace();
391    }
392    return configDir;
393  }
394  
395  /**
396   * Look up the global testconfig path.
397   * This path is specified in either an 
398   * @return
399   */
400  public static String getGlobalTestConfig() {    
401    // First Check System Property
402    String config = System.getProperty(ATG_DUST_TESTCONFIG);
403    // If NULL check environment variable
404    if (config == null)
405      config = System.getenv(ATG_DUST_TESTCONFIG_ENV);
406    // If that's null, there is no global test config specified
407    return config;
408  }
409 
410  /**
411   * This method starts nucleus with a config path calculated from the
412   * specified list of Dynamo modules ("DAS", "DPS", "DSS",
413   * "Publishing.base", etc). Additionally adds a directory calculated relative
414   * to the location of pClassRelativeTo's package name from the classloader.
415   * The added config directory is calculated as (in psuedocode):
416   * <code>
417   *   (pClassRelativeTo's package location) + "/data/" +  (pClassRelativeTo's simpleClassName) + "/config"
418   * </code>
419   * and is only added if the directory exists. <P>
420   *
421   * You must specify a <code>pInitialService</code> parameter, which
422   * will be the initial service started by Nucleus (rather than the
423   * normally Initial component, which would do a full Nucleus component
424   * start-up). <P>
425   *
426   * This method also creates a temporary server directory, which is deleted
427   * when shutdownNucleus in invoked on the returned directory. <P>
428   *
429   * Note: If you need to start up a complete ATG instance, you should
430   * use DUST rather than a unit test. <P>
431   *
432   * Note: You may also wish to use a {@see
433   * atg.nucleus.ConfigCreationFilter}. You can either set a value for
434   * Nucleus.CREATION_FILTER_CLASS_PROPERTY_NAME
435   * ("atg.nucleus.Nucleus.creationFilterClass") as a DynamoEnv or System
436   * property, or set the creationFilter property in Nucleus.properties in
437   * your configuration. This allows on to block creation of referenced
438   * components without having to make additional properties file changes.
439   *
440   * Note 3: Nucleus's classReplacementMap can also be useful for replacing
441   * a component instance with a subclass.
442   *
443   * @param pModules the list of modules to use to calculate the
444   *  Nucleus configuration path.
445   * @param pClassRelativeTo the class whose name and package
446   *  will be used for the {packageName}/config/{ClassName}/data directory 
447   * @param pInitialService the nucleus path of the Nucleus component
448   *  to start-up. This is a required property to prevent accidental
449   *  full start-up.
450   * @return the started Nucleus instance that should later be shut down
451   *   with the shutdownNucleus method.
452   * @exception ServletException if an error occurs
453   */
454  public static Nucleus startNucleusWithModules(
455    String[] pModules, Class pClassRelativeTo, 
456    String pInitialService) throws ServletException {
457    return startNucleusWithModules(
458      new NucleusStartupOptions(
459        pModules, pClassRelativeTo,
460        pClassRelativeTo.getSimpleName() + "/config",
461        pInitialService));
462  }
463  
464 
465  /**
466   * This method starts nucleus with a config path calculated from the
467   * specified list of Dynamo modules ("DAS", "DPS", "DSS",
468   * "Publishing.base", etc). Additionally adds a directory calculated relative
469   * to the location of pClassRelativeTo's package name from the classloader.
470   * The added config directory is calculated as (in psuedocode):
471   * <code>
472   *   (pClassRelativeTo's package location) + "/data/" +  (pBaseConfigDirectory or "config")
473   * </code>
474   * and is only added if the directory exists. <P>
475   *
476   * You must specify a <code>pInitialService</code> parameter, which
477   * will be the initial service started by Nucleus (rather than the
478   * normally Initial component, which would do a full Nucleus component
479   * start-up). <P>
480   *
481   * This method also creates a temporary server directory, which is deleted
482   * when shutdownNucleus in invoked on the returned directory. <P>
483   *
484   * Note: If you need to start up a complete ATG instance, you should
485   * use DUST rather than a unit test. <P>
486   *
487   * Note: You may also wish to use a {@see
488   * atg.nucleus.ConfigCreationFilter}. You can either set a value for
489   * Nucleus.CREATION_FILTER_CLASS_PROPERTY_NAME
490   * ("atg.nucleus.Nucleus.creationFilterClass") as a DynamoEnv or System
491   * property, or set the creationFilter property in Nucleus.properties in
492   * your configuration. This allows on to block creation of referenced
493   * components without having to make additional properties file changes.
494   *
495   * Note 3: Nucleus's classReplacementMap can also be useful for replacing
496   * a component instance with a subclass.
497   *
498   * @param pModules the list of modules to use to calculate the
499   *  Nucleus configuration path.
500   * @param pClassRelativeTo the class whose package the config/data
501   *  (or pBaseConfigDirectory/data) should be relative in.
502   * @param pBaseConfigDirectory the base configuration directory. If
503   *   this parameter is non-null, the relative configuration subdirectory will be
504   *  ("data/" + pBaseConfigDirectory) rather than "data/config".
505   * @param pInitialService the nucleus path of the Nucleus component
506   *  to start-up. This is a required property to prevent accidental
507   *  full start-up.
508   * @return the started Nucleus instance that should later be shut down
509   *   with the shutdownNucleus method.
510   * @exception ServletException if an error occurs
511   */
512  public static Nucleus startNucleusWithModules(
513    String[] pModules, Class pClassRelativeTo, String pBaseConfigDirectory,
514    String pInitialService) throws ServletException {
515    return startNucleusWithModules(
516      new NucleusStartupOptions(pModules, pClassRelativeTo, pBaseConfigDirectory,
517                                pInitialService));
518  }
519 
520  
521  /**
522   * This method starts nucleus with a config path calculated from the
523   * specified list of Dynamo modules ("DAS", "DPS", "DSS",
524   * "Publishing.base", etc). Additionally adds a directory calculated relative
525   * to the location of pClassRelativeTo's package name from the classloader.
526   * The added config directory is calculated as (in psuedocode):
527   * <code>
528   *   (pClassRelativeTo's package location) + "/data/" +  (pBaseConfigDirectory or "config")
529   * </code>
530   * and is only added if the directory exists. <P>
531   *
532   * You must specify a <code>pInitialService</code> parameter, which
533   * will be the initial service started by Nucleus (rather than the
534   * normally Initial component, which would do a full Nucleus component
535   * start-up). <P>
536   *
537   * This method also creates a temporary server directory, which is deleted
538   * when shutdownNucleus in invoked on the returned directory. <P>
539   *
540   * Note: If you need to start up a complete ATG instance, you should
541   * use DUST rather than a unit test. <P>
542   *
543   * Note: You may also wish to use a {@see
544   * atg.nucleus.ConfigCreationFilter}. You can either set a value for
545   * Nucleus.CREATION_FILTER_CLASS_PROPERTY_NAME
546   * ("atg.nucleus.Nucleus.creationFilterClass") as a DynamoEnv or System
547   * property, or set the creationFilter property in Nucleus.properties in
548   * your configuration. This allows on to block creation of referenced
549   * components without having to make additional properties file changes.
550   *
551   * Note 3: Nucleus's classReplacementMap can also be useful for replacing
552   * a component instance with a subclass.
553   *
554   * See NucleusStartupOptions for the effects of individual properties
555   * of pOptions.
556   *
557   * 
558   * @param pOptions the startup Options for Nucleus.
559   *
560   * @return the started Nucleus instance that should later be shut down
561   *   with the shutdownNucleus method.
562   * @exception ServletException if an error occurs
563   */
564  public static Nucleus startNucleusWithModules(
565    NucleusStartupOptions pOptions) throws ServletException {
566 
567    if (pOptions.getInitialService() == null) {
568      throw new IllegalArgumentException("Initial service must be specified.");
569    }
570 
571    // now let's try to find dynamo home...
572    String dynamoRootStr = findDynamoRoot();
573 
574    if (dynamoRootStr == null) {
575      throw new ServletException("Could not find dynamo root.");
576    }
577 
578    if (!new File(dynamoRootStr).exists()) {
579      throw new ServletException(
580        "Could not find dynamo root at " +
581        dynamoRootStr + " because directory does not exist."); 
582    }
583 
584    if (DynamoEnv.getProperty("atg.dynamo.root") == null) {
585      // make sure root is set as a property
586      DynamoEnv.setProperty("atg.dynamo.root", dynamoRootStr);
587    }
588 
589    if (DynamoEnv.getProperty("atg.dynamo.home") == null) {
590      // make sure home is set as a property
591      DynamoEnv.setProperty("atg.dynamo.home",
592                            dynamoRootStr + File.separator + "home");
593    }
594    
595 
596 
597    File dynamoRootFile = new File(dynamoRootStr);
598    
599    String strModulesString = StringUtils.joinStringsWithQuoting(
600      pOptions.getModules(), File.pathSeparatorChar);
601 
602    DynamoEnv.setProperty("atg.dynamo.modules", strModulesString);
603 
604 
605    // our temporary server directory.
606    File fileServerDir = null;
607 
608    try {
609 
610      AppModuleManager modMgr = new MultiInstallLocalAppModuleManager(
611        dynamoRootStr, dynamoRootFile,
612        strModulesString);
613    
614      AppLauncher launcher = AppLauncher.getLauncher(modMgr, strModulesString);
615 
616      // Start Nucleus
617      String configpath = DynamoServerLauncher.calculateConfigPath(
618        launcher, pOptions.getLiveconfig(),
619        pOptions.getLayersAsString(), false, null);
620 
621      // use the NucleusTestUtils config dir as a base, since it
622      // empties out license checks, etc.
623      File fileBaseConfig = getConfigpath(NucleusTestUtils.class, null, false);
624 
625      if ((fileBaseConfig != null) && fileBaseConfig.exists()) {
626        configpath = configpath + File.pathSeparator +
627          fileBaseConfig.getAbsolutePath();
628      }
629 
630 
631      // add the additional config path as the last arg, if needed
632      File fileTestConfig = getConfigpath(
633        pOptions.getClassRelativeTo(),
634        pOptions.getBaseConfigDirectory(), false);
635      
636      // now add it to the end of our config path
637      if ((fileTestConfig != null) && fileTestConfig.exists()) {
638        configpath = configpath + File.pathSeparator +
639          fileTestConfig.getAbsolutePath();
640      }
641      else if (fileTestConfig != null) {
642        System.err.println("Warning: did not find directory " +
643                           fileTestConfig.getAbsolutePath());
644      }
645 
646      // finally, create a server dir.
647      fileServerDir = createTempServerDir();
648 
649      System.setProperty("atg.dynamo.server.home",
650                         fileServerDir.getAbsolutePath());
651      System.setProperty("atg.dynamo.license.read", "true");
652      System.setProperty("atg.license.read", "true");    
653      NucleusServlet.addNamingFactoriesAndProtocolHandlers();
654 
655      ArrayList<String> listArgs = new ArrayList<String>();
656      listArgs.add(configpath);
657      listArgs.add("-initialService");
658      listArgs.add(pOptions.getInitialService());
659 
660      PropertyEditors.registerEditors();
661      System.out.println("Starting nucleus with arguments: " + listArgs);
662      Nucleus n = Nucleus.startNucleus(listArgs.toArray(new String[0]));
663 
664      // remember our temporary server directory for later deletion
665      sNucleusToTempAtgServerDirectory.put(n, fileServerDir);
666      // clear out the variable, so our finally clause knows not to
667      // delete it
668      fileServerDir = null;
669      
670      return n;
671    } catch (AppLauncherException e) {
672      throw new ServletException(e);
673    }
674    catch (IOException e) {
675      throw new ServletException(e);
676    }
677    finally {
678      if ((fileServerDir != null) && sRemoveTempAtgServerDirectories) {
679        try {
680          // a non-null value means it was created, but not added to our list,
681          // so we should nuke it.
682          FileUtils.deleteDir(fileServerDir.getAbsolutePath());
683        } catch (IOException e) {
684          // we shouldn't rethrow here, since we might block
685          // the exception in the main clause, so we'll do the bad
686          // thing and print the stack trace and swallow the exception
687          e.printStackTrace();
688        }
689      }
690    }
691  }
692 
693  /**
694   * Shutdown the specified Nucleus and try to delete the associated
695   * temporary server directory. Typically used on a Nucleus created
696   * by startNucleusWithModules.
697   * @param pNucleus the nucleus instance to shut down.
698   * @exception ServiceException if an error occurs
699   * @exception IOException if an error occurs (such as a failure
700   *   to remove the temporary server directory).
701   */
702  public static void shutdownNucleus(Nucleus pNucleus) throws ServiceException, IOException {
703    boolean bComplete = false;
704    try {
705      if (pNucleus.isRunning()) {
706        pNucleus.stopService();
707      }
708      bComplete = true;
709    } finally {
710      File fileTempAtgServDirectory =
711        sNucleusToTempAtgServerDirectory.get(pNucleus);
712      
713      // try to delete the temp server directory.
714      if (sRemoveTempAtgServerDirectories &&
715          (fileTempAtgServDirectory != null) &&
716          fileTempAtgServDirectory.exists()) {
717        try {
718          FileUtils.deleteDir(fileTempAtgServDirectory.getAbsolutePath());
719        } catch (IOException e) {
720          if (bComplete) {
721            // only throw if we if we finished our try clause, because
722            // otherwise we might block our initial exception
723            throw e;
724          }
725        } finally {
726          sNucleusToTempAtgServerDirectory.remove(pNucleus);
727        }
728      }
729    }
730  }
731 
732  /**
733   * Create a temporary, empty server directory. This is to satisfy
734   * Dynamo's need to have a server directory, yet not conflict if
735   * multiple tests are running at the same time against the same Dynamo
736   * instance. The directory name is generated by File.createTempFile.
737   * @return the created temporary server directory.
738   * @exception IOException if an error occurs
739   */
740  protected static File createTempServerDir() throws IOException {
741    File fileTemp =  File.createTempFile("tempServer", "dir");
742    fileTemp.delete();
743    if (!fileTemp.mkdir()) {
744      throw new IOException("Unable to create directory " +
745                            fileTemp.getAbsolutePath());
746    }
747    for (String strSubDir : ServerConfig.smConfigFileDirs) {
748      File fileSubDir = new File(fileTemp, strSubDir);
749      if (!fileSubDir.mkdirs()) {
750        throw new IOException("Unable to create directory " +
751                              fileSubDir.getAbsolutePath());
752      }
753    }
754    return fileTemp;
755  }
756 
757 
758  /** A crazily ugly and elaborate method where we try to discover
759   * DYNAMO_ROOT by various means. This is mostly made complicated
760   * by the ROAD DUST environment being so different from devtools.
761   */
762  static String findDynamoRoot() {
763    // now let's try to find dynamo home...
764    String dynamoRootStr = DynamoEnv.getProperty("atg.dynamo.root");
765 
766 
767    if (dynamoRootStr == null) {
768      // let's try to look at an environment variable, just to
769      // see....
770      dynamoRootStr = 
771        CommandProcessor.getProcEnvironmentVar("DYNAMO_ROOT");
772    }
773 
774    if (dynamoRootStr == null) {
775      // try dynamo home
776      String dynamoHomeStr = DynamoEnv.getProperty("atg.dynamo.home");
777      if (StringUtils.isEmpty(dynamoHomeStr)) {
778        dynamoHomeStr = null;
779      }
780      
781      if (dynamoHomeStr == null) {
782        dynamoHomeStr = 
783          CommandProcessor.getProcEnvironmentVar("DYNAMO_HOME");
784 
785        if (StringUtils.isEmpty(dynamoHomeStr)) {
786          dynamoHomeStr = null;
787        }
788 
789        if (dynamoHomeStr != null) {
790          // make sure home is set as a property
791          DynamoEnv.setProperty("atg.dynamo.home", dynamoHomeStr);
792        }
793      }
794 
795      if (dynamoHomeStr != null) {
796        dynamoRootStr = dynamoHomeStr.trim() + File.separator + "..";
797      }
798    }
799 
800    if (dynamoRootStr == null) {
801      // okay, start searching upwards for something that looks like
802      // a dynamo directory, which should be the case for devtools
803      File currentDir = new File(new File(".").getAbsolutePath());
804      String strDynamoHomeLocalConfig = "Dynamo" + File.separator + "home" + File.separator + "localconfig";
805 
806      while (currentDir != null) {
807        File filePotentialHomeLocalconfigDir = new File(currentDir,
808                                                        strDynamoHomeLocalConfig);
809        if (filePotentialHomeLocalconfigDir.exists()) {
810          dynamoRootStr = new File(currentDir, "Dynamo").getAbsolutePath();
811          System.out.println("Found dynamo root via parent directory: " + dynamoRootStr);
812          break;
813        }
814        currentDir = currentDir.getParentFile();
815      }
816    }
817 
818    if (dynamoRootStr == null) {
819      // okay, we are not devtools-ish, so let's try using our ClassLoader
820      // to figure things out.
821 
822      URL urlClass =
823        NucleusTestUtils.class.getClassLoader().getResource("atg/nucleus/Nucleus.class");
824 
825      // okay... this should be jar URL...
826      if ((urlClass != null) && "jar".equals(urlClass.getProtocol())) {
827        String strFile = urlClass.getFile();
828        int separator = strFile.indexOf('!');
829        strFile = strFile.substring(0, separator);
830        
831        File fileCur = null;
832        try {
833          fileCur = urlToFile(new URL(strFile));
834        } catch (MalformedURLException e) {
835          // ignore
836        }
837 
838        if (fileCur != null) {
839          String strSubPath =
840            "DAS/taglib/dspjspTaglib/1.0".replace('/', File.separatorChar);
841          while ((fileCur != null) && fileCur.exists()) {
842            if (new File(fileCur, strSubPath).exists()) {
843              dynamoRootStr = fileCur.getAbsolutePath();
844              System.out.println("Found dynamo root by Nucleus.class location: " +
845                                 dynamoRootStr);
846 
847              
848              break;
849            }
850            fileCur = fileCur.getParentFile();
851          }
852        }
853      }
854    }
855 
856    return dynamoRootStr;
857  }
858 
859  /** Try to convert a file URL to a File object. You'd think this would be
860   *  easier, but no. */
861  static File urlToFile(URL url) {
862    URI uri;
863 
864    if (!"file".equals(url.getProtocol())) {
865      throw new IllegalArgumentException("URL must be a file URL, got " + url);
866    }
867 
868    try {
869      // this is the step that can fail, and so
870      // it should be this step that should be fixed
871      uri = url.toURI();
872    } catch (URISyntaxException e) {
873      // OK if we are here, then obviously the URL did
874      // not comply with RFC 2396. This can only
875      // happen if we have illegal unescaped characters.
876      // If we have one unescaped character, then
877      // the only automated fix we can apply, is to assume
878      // all characters are unescaped.
879      // If we want to construct a URI from unescaped
880      // characters, then we have to use the component
881      // constructors:
882      try {
883        uri = new URI(url.getProtocol(), url.getUserInfo(), url
884                      .getHost(), url.getPort(), url.getPath(), url
885                      .getQuery(), url.getRef());
886      } catch (URISyntaxException e1) {
887        // The URL is broken beyond automatic repair
888        throw new IllegalArgumentException("broken URL: " + url);
889      }
890    }
891    return new File(uri);
892  }
893    
894  /**
895   * This method returns a free port number on the current machine. There is
896   * some chance that the port number could be taken by the time the caller
897   * actually gets around to using it.
898   * 
899   * This method returns -9999 if it's not able to find a port.
900   */
901  public static int findFreePort() {
902    ServerSocket socket = null;
903    int freePort = -9999;
904    try {
905      socket = new ServerSocket(0);
906      freePort = socket.getLocalPort();      
907    } catch (IOException e) {
908      ;
909    } finally {
910      try {
911        socket.close();
912      } catch (IOException e) {
913        ;
914      }
915    }
916    return freePort;
917  }
918 
919  //-------------------------------------------------------
920  /** A class representing NucleusStartupOptions, used by
921   * startNucleusWithModules().
922   */
923  public static class NucleusStartupOptions {
924    
925    /** List of dynamo modules. */
926    private String[] mModules;
927    /** Class whose package data subdir is relative to. */
928    private Class mClassRelativeTo;
929    /** The base config directory, realtive to mClassRelativeTo's package
930     * + "/data". If null, then "config"
931     */
932    private String mBaseConfigDirectory;
933 
934    /** The Nucleus path of the intial service to resolve. */
935    private String mInitialService;
936    
937 
938    /**
939     * This constructor creates NucleusStartupOptions with the
940     * specified list of Dynamo modules ("DAS", "DPS", "DSS",
941     * "Publishing.base", etc).
942     * Additionally sets opts to add a directory calculated relative
943     * to the location of pClassRelativeTo's package name from the classloader.
944     * The added config directory is calculated as (in psuedocode):
945     * <code>
946     *   (pClassRelativeTo's package location) + "/data/" +  (pClassRelativeTo's simpleClassName) + "/config"
947     * </code>
948     * and is only added if the directory exists. <P>
949     *
950     * You must specify a <code>pInitialService</code> parameter, which
951     * will be the initial service started by Nucleus (rather than the
952     * normally Initial component, which would do a full Nucleus component
953     * start-up). <P>
954     *
955     *
956     * @param pModules the list of modules to use to calculate the
957     *  Nucleus configuration path.
958     * @param pClassRelativeTo the class whose name and package
959     *  will be used for the {packageName}/config/{simpleClassName}/data directory 
960     * @param pInitialService the nucleus path of the Nucleus component
961     *  to start-up. This is a required property to prevent accidental
962     *  full start-up.
963     * @return the started Nucleus instance that should later be shut down
964     *   with the shutdownNucleus method.
965     * @exception ServletException if an error occurs
966     */
967    public NucleusStartupOptions(String[] pModules, Class pClassRelativeTo, 
968                                 String pInitialService) {
969 
970      
971      mModules = pModules;
972      mClassRelativeTo = pClassRelativeTo;
973      mInitialService = pInitialService;
974      mBaseConfigDirectory = pClassRelativeTo.getSimpleName() + "/config";
975    }
976 
977 
978    /**
979     * This constructor creates NucleusStartupOptions with the
980     * specified list of Dynamo modules ("DAS", "DPS", "DSS",
981     * "Publishing.base", etc).
982     * Additionally sets opts to add a directory calculated relative
983     * to the location of pClassRelativeTo's package name from the classloader.
984     * The added config directory is calculated as (in psuedocode):
985     * <code>
986     *   (pClassRelativeTo's package location) + "/" + (pConfigDirectory or "data") + "/config"
987     * </code>
988     * and is only added if the directory exists. <P>
989     *
990     * You must specify a <code>pInitialService</code> parameter, which
991     * will be the initial service started by Nucleus (rather than the
992     * normally Initial component, which would do a full Nucleus component
993     * start-up). <P>
994     *
995     *
996     * @param pModules the list of modules to use to calculate the
997     *  Nucleus configuration path.
998     * @param pClassRelativeTo the class whose package the config/data
999     *  (or pBaseConfigDirectory/data) should be relative in.
1000     * @param pBaseConfigDirectory the base configuration directory. If
1001     *   this parameter is non-null, the relative configuration subdirectory will be
1002     *  ("data/" + pBaseConfigDirectory) rather than "data/config".
1003     * @param pInitialService the nucleus path of the Nucleus component
1004     *  to start-up. This is a required property to prevent accidental
1005     *  full start-up.
1006     * @return the started Nucleus instance that should later be shut down
1007     *   with the shutdownNucleus method.
1008     * @exception ServletException if an error occurs
1009     */
1010    public NucleusStartupOptions(String[] pModules, Class pClassRelativeTo,
1011                                 String pBaseConfigDirectory,
1012                                 String pInitialService) {
1013 
1014      mModules = pModules;
1015      mClassRelativeTo = pClassRelativeTo;
1016      mInitialService = pInitialService;
1017      mBaseConfigDirectory = pBaseConfigDirectory;
1018    }
1019    
1020    /** Return the list of modules for starting Nucleus. These modules
1021     * are the modules whose config path will be included. */
1022    public String[] getModules() {
1023      return mModules;
1024    }
1025 
1026    /** Return the "class relative to" property. This is the Class whose
1027     * package the config directory will be relative to. */
1028    public Class getClassRelativeTo() {
1029      return mClassRelativeTo;
1030    }
1031 
1032    /** Gets the initialService. This is the InitialService for Nucleus
1033     * to resolve at start-up. Required. */
1034    public String getInitialService() {
1035      return mInitialService;
1036    }
1037 
1038    //-------------------------------------
1039    // property: baseConfigDirectory
1040 
1041    /** Set the basic config directory. This is the directory that will be
1042     * taked on to the package path of the classRelativeTo class. If this
1043     * property is non-null, the relative configuration subdirectory will
1044     * be ("data/" + baseConfigDirectory). */
1045    public String getBaseConfigDirectory() {
1046      return mBaseConfigDirectory;
1047    }
1048 
1049 
1050    //-------------------------------------
1051    // property: layers
1052 
1053    private String[] mLayers;
1054 
1055    /** Sets the Dynamo layers to run with. */
1056    public void setLayers(String[] pLayers) {
1057      mLayers = pLayers;
1058    }
1059 
1060    /** Returns the Dynamo layers to run with. */
1061    public String[] getLayers() {
1062      return mLayers;
1063    }
1064 
1065    /** Return the layers as a string appropriate for passing to
1066     * DynamoServerLauncher, calculateConfigPath.
1067     *
1068     * @return null if layers is null. Otherwise returns a space delimited
1069     *  list of layers.
1070     */
1071    public String getLayersAsString() {
1072      if (mLayers == null) {
1073        return null;
1074      }
1075      StringBuilder strbuf = new StringBuilder();
1076      for (String strCur : mLayers) {
1077        if (strbuf.length() != 0) {
1078          strbuf.append(" ");
1079        }
1080        strbuf.append(strCur);
1081      }
1082      return strbuf.toString();
1083    }
1084 
1085    //-------------------------------------
1086    // property: liveconfig
1087 
1088    private boolean mLiveconfig;
1089 
1090    /** Sets property liveconfig.
1091     *
1092     * @param pLiveconfig true if Nucleus should be started in liveconfig
1093     *  mode, false otherwise.
1094     */
1095    public void setLiveconfig(boolean pLiveconfig) {
1096      mLiveconfig = pLiveconfig;
1097    }
1098 
1099    /** Returns property liveconfig.
1100     * @return true if liveconfig should be set, false otherwise.
1101     */
1102    public boolean getLiveconfig() {
1103      return mLiveconfig;
1104    }
1105 
1106    /** Modify Nucleus command-line options, as needed. This will
1107     * be invoked just before Nucleus is started as a final
1108     * chance to adjust any command-line options.
1109     */
1110    public void modifyNucleusCommandLineOptions(List<String> listArgs) {
1111      return;
1112    }
1113  } // end inner-class NucleusStartupOptions
1114 
1115}

[all classes][atg.nucleus]
EMMA 2.0.5312 (C) Vladimir Roubtsov