| 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 | */ |
| 14 | package atg.nucleus; |
| 15 | |
| 16 | import java.io.File; |
| 17 | import java.io.FileWriter; |
| 18 | import java.io.IOException; |
| 19 | import java.net.MalformedURLException; |
| 20 | import java.net.ServerSocket; |
| 21 | import java.net.URI; |
| 22 | import java.net.URISyntaxException; |
| 23 | import java.net.URL; |
| 24 | import java.util.ArrayList; |
| 25 | import java.util.Collections; |
| 26 | import java.util.HashMap; |
| 27 | import java.util.Iterator; |
| 28 | import java.util.List; |
| 29 | import java.util.Map; |
| 30 | import java.util.Properties; |
| 31 | |
| 32 | import javax.servlet.ServletException; |
| 33 | |
| 34 | import org.apache.log4j.Logger; |
| 35 | |
| 36 | import atg.applauncher.AppLauncher; |
| 37 | import atg.applauncher.AppLauncherException; |
| 38 | import atg.applauncher.AppModuleManager; |
| 39 | import atg.applauncher.MultiInstallLocalAppModuleManager; |
| 40 | import atg.applauncher.dynamo.DynamoServerLauncher; |
| 41 | import atg.core.io.FileUtils; |
| 42 | import atg.core.util.CommandProcessor; |
| 43 | import atg.core.util.JarUtils; |
| 44 | import atg.core.util.StringUtils; |
| 45 | import atg.nucleus.naming.ComponentName; |
| 46 | import atg.nucleus.servlet.NucleusServlet; |
| 47 | import atg.service.dynamo.ServerConfig; |
| 48 | import 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 | */ |
| 59 | public 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 | } |