/* * Copyright 2013 Matt Sicker and Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package atg.tools.dynunit.junit.nucleus; import atg.applauncher.AppLauncher; import atg.applauncher.AppModule; import atg.nucleus.DynamoEnv; import atg.nucleus.Nucleus; import atg.service.email.ContentPart; import atg.service.email.EmailEvent; import atg.service.email.MimeMessageUtils; import atg.service.email.SMTPEmailSender; import atg.servlet.ServletUtil; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.Nullable; import org.w3c.dom.Node; import javax.activation.DataHandler; import javax.activation.FileDataSource; import javax.mail.BodyPart; import javax.mail.Message; import javax.mail.Multipart; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMultipart; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.lang.reflect.Method; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.URL; import java.net.UnknownHostException; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.StringTokenizer; import java.util.jar.Manifest; // TODO: holy shit this file is huge /** * This class is used to hold useful utility methods people may * need when running tests. */ public class TestUtils extends atg.nucleus.GenericService { private static final Logger logger = LogManager.getLogger(); // names of app servers types that may be specified by the // 'atg.dynamo.appserver' system property // Dynamo currently does not distinguish between generic // Tomcat and JBoss, everything is just referred to as 'tomcat' private static final String APP_SERVER_DAS = "das"; private static final String APP_SERVER_BEA = "weblogic"; private static final String APP_SERVER_IBM = "websphere"; private static final String APP_SERVER_TOMCAT = "tomcat"; // names of various vendors that ATG works with private static final String VENDOR_ATG = "ATG"; private static final String VENDOR_BEA = "BEA"; private static final String VENDOR_IBM = "IBM"; private static final String VENDOR_JBOSS = "JBOSS"; // the variable that points to the installation directory for dynamo private static final String ROOT_VAR = "atg.dynamo.root"; private static final String HOME_VAR = "atg.dynamo.home"; private static final String ATG_J2EESERVER_ROOT = "atg.j2eeserver.root"; // these are used to lookup some system settings from the VM private static final String JAVA_VAR = "java.vm.info"; private static final String JAVA_VERSION = "java.vm.version"; private static final String JAVA_BUILD_VERSION = "java.version"; private static final String COMPILER_VAR = "java.compiler"; private static final String OS_VAR = "os.name"; private static final String OS_VERSION_VAR = "os.version"; // the system variable that returns the name of the app server being used private static final String APP_SERVER = "atg.dynamo.appserver"; // the Configuration component used by Dynamo private static final String CONFIGURATION_COMPONENT = "/atg/dynamo/Configuration"; // mailhost used to send email private static final String MAILHOST = "mailsvr.atg.com"; // value returned by several methods, as noted in javadoc, if a // piece of information can not be definitively determined. in // particular, used when reporting about product build and version // information private static final String UNKNOWN_INFO = "unknown"; /** * property to track the DUST version being used. utilized by * ATGXMLFileTestResultReported so we can tag XML result files for * compatibility validation when passed to the XML file logger */ private static int DUST_VERSION = 1; /** * specifies the DUST version being used. utilized by * ATGXMLFileTestResultReporter so XML result files can be tagged * for compatibility validation when passed to the XML file * logger */ public void setDustVersion(int pVersion) { DUST_VERSION = pVersion; } /** * returns the DUST version being used. utilized by * ATGXMLFileTestResultReporter so XML result files can be tagged * for compatibility validation when passed to the XML file * logger */ public int getDustVersion() { return DUST_VERSION; } /** * property to track the DUST user. utilized when results are * logged to the database to correlate the result with a user * account in the test management system. */ private static String DUST_USERNAME = System.getProperty("user.name"); /** * specifies the DUST user. utilized when results are logged to * the database to correlate the result with a user account in the * test management system. */ public void setDustUsername(String pUsername) { DUST_USERNAME = pUsername; } /** * returns the DUST user name. utilized when results are logged to * the database to correlate the result with a user account in the * test management system. */ public String getDustUsername() { if ( DUST_USERNAME == null || DUST_USERNAME.trim().length() == 0 ) { return System.getProperty("user.name"); } else { return DUST_USERNAME; } } /** * property to track which testrun a result is part of. utilized * by TSM to correlate a specifid result with the testrun used to * install and configure the test Dynamo. */ @Nullable private static String TSM_TESTRUN = null; /** * Specifies the TSM testrun this result is part of. utilized by * TSM to correlate a specifid result with the testrun used to * install and configure the test Dynamo. */ public void setTsmTestrun(String pId) { TSM_TESTRUN = pId; } /** * Returns the TSM testrun this result is part of. utilized by TSM * to correlate a specifid result with the testrun used to install * and configure the test Dynamo. */ public String getTsmTestrun() { return TSM_TESTRUN; } /** * property to track the p4 sync time for tests. utilized by TSM * to inform end-users of time at which machine was last synced. * must be specified by test setup before test is run. */ private static String P4SYNCTIME = null; /** * property to track the p4 sync time for tests. utilized by TSM * to inform end-users of time at which machine was last synced. * must be specified by test setup before test is run. */ public void setP4Synctime(String pTime) { P4SYNCTIME = pTime; } /** * property to track the p4 sync time for tests. utilized by TSM * to inform end-users of time at which machine was last synced. * must be specified by test setup before test is run. */ public String setP4Synctime() { return P4SYNCTIME; } /** * Returns the directory in which Dynamo was installed. If the * installation directory was not specified during the DUST * installation, returns null. Should <b>only</b> be used by * System tests. */ private static File DYNAMO_INSTALL_DIR = null; /** * Returns the directory in which Dynamo was installed. If the * installation directory can not be successfully determined returns * null. * <br><b>NOTE:</b> There is no reliable System property (or * other inherent mechanism) to determine the Dynamo installation * directory when running as BigEar, so this value is set during the * DUST build process. The SystemTests.base build step writes the * file * SystemTests/base/config/atg/junit/nucleus/TestUtils.properties * with the proper value. Consequently, this method should only be * used by System tests, not Unit tests. */ public File getDynamoInstallDir() { return DYNAMO_INSTALL_DIR; } /** * Specifies the directory in which Dynamo was installed. */ public void setDynamoInstallDir(File pDir) { DYNAMO_INSTALL_DIR = pDir; } /** * Returns the root directory for this Dynamo. If the root * directory can not be successfully located returns null. Operates * according to this logic: * <ul> * <li>If "home" Dynamo module can be found, return parent directory * containing that module. (This should resolve when running 'BigEar') * <li>Otherwise, return value of System property 'atg.dynamo.root'. * (this could still be null) * </ul> */ public static File getDynamoRootDir() { File root = null; try { root = getDynamoHomeDir().getParentFile(); } catch ( Throwable t ) { logger.catching(t); } if ( root == null ) { try { root = new File(System.getProperty(ROOT_VAR)); } catch ( Throwable t ) { logger.catching(t); } } return root; } /** * Returns Dynamo's "home" module installation directory. If the * directory can not be successfully located returns null. * <br>Logic works like this: * <ul> * <li>If "home" Dynamo module can be found, return directory representing * that module. This should resolve when running 'BigEar' and may * point to a subdirectory of the application server's directory used * to deploy ear files. On DAS it should resolve to * <DYNAMO_INSTALL_DIR>/home. * <li>Otherwise, return value of System property 'atg.dynamo.home'. * (this could be null) * </ul> */ public static File getDynamoHomeDir() { File root = null; try { root = getModuleResourceFile("home", ".").getCanonicalFile(); } catch ( Throwable t ) { logger.catching(t); } if ( root == null ) { try { root = new File(System.getProperty(HOME_VAR)); } catch ( Throwable t ) { logger.catching(t); } } return root; } /** * returns the root install directory for the ATG J2EE Server, or null if * the ATG J2EE Server is not installed. */ public static File getAtgJ2eeServerInstallDir() { try { return new File(System.getProperty(ATG_J2EESERVER_ROOT)); } catch ( Throwable t ) { logger.catching(t); return null; } } /** * Returns the product name of the app server being used. For ATG * we currently assume it's called 'ATGDAS' if it's a separate * product, since there is no definitive way to figure out the * product name from MANIFEST files. For all other app servers it * returns the value of getAppServerType(). */ public static String getAppServerProductName() { if ( getAppServerType().equals(APP_SERVER_DAS) ) { return getAtgJ2eeServerProductName(); } else { return getAppServerType(); } } /** * Returns the name of the ATG J2EE Server product this is installed, or * null if a separate ATG J2EE Server build is not installed. */ public static String getAtgJ2eeServerProductName() { // TODO: Bug 78552 was opened to add a MANIFEST entry containing // the product name so we don't have to hard code this to // 'ATGDAS'. if ( getAtgJ2eeServerInstallDir() != null ) { return "ATGDAS"; } else { return null; } } /** * Returns the version number of the app server being used. For * DAS this version comes from the J2EEServer MANIFEST file, or is * UNKNOWN_INFO if the MANIFEST can't be found. Such may be the * case if a person is using devtools to build their product. For * 3PAS the version number is extracted from their configuration * file (typically some well known XML file). */ public static String getAppServerVersion() { String apptype = getAppServerType(); if ( apptype.equals(APP_SERVER_DAS) ) { return getAtgVersion(getAtgJ2eeServerModule()); } else if ( apptype.equals(APP_SERVER_BEA) ) { return getBeaVersion(); } else if ( apptype.equals(APP_SERVER_IBM) ) { return getWasVersion(); } else if ( apptype.equals(APP_SERVER_TOMCAT) ) { return getJBossVersion(); } else { return UNKNOWN_INFO; } } /** * Returns the build number of the app server being used. For DAS * this version comes from the J2EEServer MANIFEST file, or is * UNKNOWN_INFO if the MANIFEST can't be found. Such may be the * case if a person is using devtools to build their product. For * 3PAS the build number is always UNKNOWN_INFO. */ public static String getAppServerBuildNumber() { String apptype = getAppServerType(); if ( apptype.equals(APP_SERVER_DAS) ) { return getAtgBuildNumber(getAtgJ2eeServerModule()); } else { return UNKNOWN_INFO; } } /** * Returns the patch version of the app server being used. For DAS * this version comes from the J2EEServer MANIFEST file, or is * UNKNOWN_INFO if the MANIFEST can't be found. Such may be the * case if a person is using devtools to build their product. For * 3PAS the patch version is always UNKNOWN_INFO. */ public static String getAppServerPatchVersion() { String apptype = getAppServerType(); if ( apptype.equals(APP_SERVER_DAS) ) { return getAtgPatchVersion(getAtgJ2eeServerModule()); } else { return UNKNOWN_INFO; } } /** * Returns the patch build number of the app server being used. * For DAS this version comes from the J2EEServer MANIFEST file, or * is UNKNOWN_INFO if the MANIFEST can't be found. Such may be the * case if a person is using devtools to build their product. For * 3PAS the patch build number is always UNKNOWN_INFO. */ public static String getAppServerPatchBuildNumber() { String apptype = getAppServerType(); if ( apptype.equals(APP_SERVER_DAS) ) { return getAtgPatchBuildNumber(getAtgJ2eeServerModule()); } else { return UNKNOWN_INFO; } } /** * Returns the vendor name of the App server manufacturer. if a * vendor can not be determined it returns UNKNOWN_INFO. */ public static String getAppServerVendor() { String apptype = getAppServerType(); if ( apptype.equals(APP_SERVER_DAS) ) { return VENDOR_ATG; } else if ( apptype.equals(APP_SERVER_BEA) ) { return VENDOR_BEA; } else if ( apptype.equals(APP_SERVER_IBM) ) { return VENDOR_IBM; } else if ( apptype.equals(APP_SERVER_TOMCAT) ) { return VENDOR_JBOSS; } else { return UNKNOWN_INFO; } } /** * Returns true if the Dynamo product is being used; false if only the ATG * J2EE Server product is running. */ public static boolean isDynamoInstalled() { try { // if j2ee server is not installed then Dynamo must be... return getAtgJ2eeServerInstallDir() == null || (!getAtgJ2eeServerInstallDir().getCanonicalFile() .equals(getDynamoRootDir().getCanonicalFile())); } catch ( IOException ioe ) { // this should never happen, but if it does return false... return false; } } /** * This method returns the name of the Dynamo product that is * installed. Because there is no guaranteed way to determine the * installed product this method makes a best-guess. If the * version is less than 5.5 then the method skips down from DCS, to * DPS, etc. until it finds a product that exists. If the version * 5.5, 5.6, or 5.6.1 then this method just returns 'AppDAP' since * that is the only Dynamo product we have for those versions. * Likewise, if the version is NOT anything between 4 and 5.6, then * we return 'ATG' since that is the only version we have for * copper, etc. If the method can't determine a version it returns * UNKNOWN_INFO */ public static String getDynamoProductName() { AppModule module = getAtgDynamoModule(); if ( module == null ) { return UNKNOWN_INFO; } String version = getAtgVersion(module); if ( version == null ) { return UNKNOWN_INFO; } else if ( version.startsWith("5.5") || version.startsWith("5.6") ) { // this is an AppDAP build of 5.5, 5.6, or 5.6.1 return "AppDAP"; } else if ( !version.startsWith("4") && !version.startsWith("5.0") && !version.startsWith( "5.1" ) ) { // assume this is an ATG build from version 6.x // TODO: Bug 78552 was opened to add a MANIFEST entry containing // the product name so we don't have to guess at it. return "ATG"; } else { return UNKNOWN_INFO; } } /** * Returns information about the ATG Dynamo product being used. * does not include information about the app server that may be in * use. returns null if Dynamo is not running. */ public static String getDynamoProductInfo() { StringBuilder sb = new StringBuilder(); AppModule dynamo = getAtgDynamoModule(); if ( dynamo == null ) { return null; } sb.append(getDynamoProductName()).append(" version ").append(getAtgVersion(dynamo)); String build = getAtgBuildNumber(dynamo); if ( !build.equals(UNKNOWN_INFO) ) { sb.append(" build ").append(build); } String patch_version = getAtgPatchVersion(dynamo); String patch_build = getAtgPatchBuildNumber(dynamo); if ( !(patch_version == null) && !patch_version.equals(UNKNOWN_INFO) ) { sb.append(" with patch ").append(patch_version).append(" build ").append(patch_build); } return sb.toString(); } /** * Returns a summary of information about the App Server product * being used. */ public static String getAppServerProductInfo() { StringBuilder sb = new StringBuilder(); sb.append(getAppServerProductName()).append(" version ").append(getAppServerVersion()); String build = getAppServerBuildNumber(); if ( !build.equals(UNKNOWN_INFO) ) { sb.append(" build ").append(build); } String patch_version = getAppServerPatchVersion(); String patch_build = getAppServerPatchBuildNumber(); if ( !(patch_version == null) && !patch_version.equals(UNKNOWN_INFO) ) { sb.append(" with patch ").append(patch_version).append(" build ").append(patch_build); } return sb.toString(); } /** * returns the java version that Dynamo is using * * @return String version of java Dynamo is using */ public static String getJavaVersion() { return System.getProperty(JAVA_VERSION); } /** * returns the java build version (java.version) that Dynamo is using * * @return String build version of java Dynamo is using */ public static String getJavaBuildVersion() { return System.getProperty(JAVA_BUILD_VERSION); } /** * returns detailed version information about the jdk being used */ public static String getJavaVersionDetails() { return TestUtils.getJavaInfo() + " - " + TestUtils.getJavaVersion(); } /** * returns info about the java build that Dynamo is using * * @return String info about java build Dynamo is using */ public static String getJavaInfo() { return System.getProperty(JAVA_VAR); } /** * returns the type of compiler that Dynamo is using * * @return String compiler Dynamo is using */ public static String getCompilerType() { return System.getProperty(COMPILER_VAR); } /** * returns the type of Operating System that Dynamo is running on */ public static String getOperatingSystemType() { return System.getProperty(OS_VAR) + " version " + System.getProperty(OS_VERSION_VAR); } /** * returns the hostname of the machine that Dynamo is running on */ public static String getHostname() { try { InetAddress address = InetAddress.getLocalHost(); return address.getHostName(); } catch ( UnknownHostException uhe ) { logger.catching(uhe); } return "unknown"; } /** * returns the name of the app server that dynamo is using */ public static String getAppServerType() { if ( ServletUtil.isWebLogic() ) { return APP_SERVER_BEA; } else if ( ServletUtil.isWebSphere() ) { return APP_SERVER_IBM; } else if ( ServletUtil.isDynamoJ2EEServer() ) { return APP_SERVER_DAS; } else if ( isGenericAppServer() ) { return APP_SERVER_TOMCAT; } else { return System.getProperty(APP_SERVER); } } /** * Returns true if Dynamo is running on a 'generic' (aka Tomcat) * j2ee appserver; false otherwise. * ServletUtil.isGenericJ2EEServer() method call does not exist in * early Dynamo versions, so use reflection to invoke it. As of * ATG 7x, isGenericJ2EEServer() really means "are we running on * JBOSS" - I'm not sure whether we intend to differentiate between * JBOSS and other 'generic' Tomcat app servers. */ public static boolean isGenericAppServer() { try { ServletUtil.class.newInstance(); return (Boolean) invokeMethod( dynamoEnv(), "isGenericJ2EEServer", null, null, null ); } catch ( Throwable t ) { logger.catching(t); } return false; } /** * returns the WAS home directory if running on WAS. otherwise, * returns null */ public static String getWasHomeDir() { return System.getProperty("was.install.root"); } /** * returns the WAS version number. if not running against WAS, or * if the {WAS.install.root}/properties/version/BASE.product file * can not be found, or if an error occurs parsing the file then * returns UNKNOWN_INFO. */ public static String getWasVersion() { String version = null; try { // WAS 5 File f = new File(getWasHomeDir(), "properties/version/BASE.product"); // WAS 6 if ( !f.exists() ) { f = new File(getWasHomeDir(), "properties/version/WAS.product"); } String[] children1 = { "version" }; List<Node> nodes1 = XmlUtils.getNodes(f, false, children1); if ( nodes1 != null ) { for ( Node n1 : nodes1 ) { version = XmlUtils.getNodeTextValue(n1); } } } catch ( Throwable e ) { logger.catching(e); } if ( version != null ) { return version; } else { return UNKNOWN_INFO; } } /** * returns the full file path of specified was log if * getWasHomeDir() is not null. otherwise returns null. */ private static File getWasLogFile(String pServerName, String pLogName) { if ( getWasHomeDir() == null ) { return null; } else { return new File( getWasHomeDir(), "logs" + File.separator + pServerName + File.separator + pLogName ); } } private static String mWasSystemOutLogFile = null; /** * Specifies the log file to return when asked for the WAS * 'SystemOut.log' file. if this value is null we attempt to * calculate a default location */ public void setWasSystemOutLogFile(String pFile) { mWasSystemOutLogFile = pFile; } /** * returns the expected location of the WAS 'SystemOut.log' file if * running on WAS. otherwise returns null. */ public static String getWasSystemOutLogFile() { if ( getWasHomeDir() == null ) { return null; } else if ( mWasSystemOutLogFile != null && mWasSystemOutLogFile.trim().length() > 0 ) { return mWasSystemOutLogFile.trim(); } else { File f = getWasLogFile(ServletUtil.getWebsphereServerName(), "SystemOut.log"); if ( f == null || !f.exists() ) { f = getWasLogFile("server1", "SystemOut.log"); } if ( f != null ) { return f.getAbsolutePath(); } } return null; } private static String mWasSystemErrLogFile = null; /** * Specifies the log file to return when asked for the WAS * 'SystemErr.log' file. if this value is null we attempt to * calculate a default location */ public void setWasSystemErrLogFile(String pFile) { mWasSystemErrLogFile = pFile; } /** * returns the expected location of the WAS 'SystemErr.log' file if * running on WAS. otherwise returns null. */ public static String getWasSystemErrLogFile() { if ( getWasHomeDir() == null ) { return null; } else if ( mWasSystemErrLogFile != null && mWasSystemErrLogFile.trim().length() > 0 ) { return mWasSystemErrLogFile.trim(); } else { File f = getWasLogFile(ServletUtil.getWebsphereServerName(), "SystemErr.log"); if ( f == null || !f.exists() ) { f = getWasLogFile("server1", "SystemErr.log"); } if ( f != null ) { return f.getAbsolutePath(); } } return null; } /** * returns the BEA home directory if running on BEA. otherwise, * returns null */ public static String getBeaHomeDir() { String homedir = System.getProperty("bea.home"); if ( homedir != null ) { return homedir; } // sometimes (like on bea 8) 'bea.home' is not specified, so try // to determine bea.home base on another property... String startfile = System.getProperty("java.security.policy"); if ( startfile != null ) { // the policy file is (hopefully) always located at a location like: // /root/to/bea/<weblogic>/server/lib/weblogic.policy // so we basically want to go up four levels from there... homedir = atg.core.io.FileUtils.getParent(startfile); // should now be in /root/to/bea/<weblogic>/server/lib homedir = atg.core.io.FileUtils.getParent(homedir); // should now be in /root/to/bea/<weblogic>/server homedir = atg.core.io.FileUtils.getParent(homedir); // should now be in /root/to/bea/<weblogic> homedir = atg.core.io.FileUtils.getParent(homedir); // should now be in /root/to/bea } return homedir; } private static String mBeaMyServerLogFile = null; /** * Specifies the log file to return when asked for the BEA * 'myserver.log' file. if this value is null then a default value * will be calculated. */ public static void setBeaMyServerLogFile(String pFile) { mBeaMyServerLogFile = pFile; } /** * returns the expected location of the BEA 'myserver.log' file if * running on BEA. otherwise returns null. */ public static String getBeaMyServerLogFile() { if ( getBeaHomeDir() == null ) { return null; } else if ( mBeaMyServerLogFile != null && mBeaMyServerLogFile.trim().length() > 0 ) { return mBeaMyServerLogFile.trim(); } else { // try this default location.... String name = "user_projects" + File.separator + "mydomain" + File.separator + "myserver" + File.separator + "myserver.log"; File log = new File(getBeaHomeDir(), name); if ( log.exists() ) { return log.getAbsolutePath(); } else { // the default didn't work (as we shouldn't always expect it // to) so try this location... 'user.dir' typically points to // the domain dir: /path/to/bea/user_projects/mydomain // 'weblogic.Name' should be like : myserver name = System.getProperty("user.dir") + File.separator + System.getProperty("weblogic.Name") + File.separator + System.getProperty("weblogic.Name") + ".log"; log = new File(name); if ( log.exists() ) { return log.getAbsolutePath(); } } } return null; } /** * returns the BEA version number. if not running against BEA, or if * the {BEA_HOME}/registry.xml file can not be found, or if an error occurs * parsing the file then returns UNKNOWN_INFO. */ public static String getBeaVersion() { String version = null; try { File f = new File(new File(getBeaHomeDir()), "registry.xml"); String[] children = { "host", "product", "release" }; List<Node> nodes = XmlUtils.getNodes(f, false, children); if ( nodes != null ) { // I expect there to only be one <host><product><release> node // so this iteration should really just loop over one node. for ( Node n : nodes ) { version = XmlUtils.getAttribute(n, "level", "0") + "." + XmlUtils.getAttribute(n, "ServicePackLevel", "0") + "." + XmlUtils.getAttribute(n, "PatchLevel", "0"); } } } catch ( Throwable e ) { logger.catching(e); } if ( version != null ) { return version; } else { return UNKNOWN_INFO; } } // ---------------- JBOSS Utility Methods -------------------------- // NOTE: Available JBoss System property names can be found in class // org.jboss.system.server.ServerConfig /** * Returns the JBOSS installation home directory, if Dynamo is * running on JBOSS. Otherwise returns null. */ public static File getJBossHomeDir() { String dir = System.getProperty("jboss.home.dir"); if ( dir == null ) { return null; } else { return new File(dir); } } /** * Returns the JBOSS server home directory, if Dynamo is running on * JBOSS. Otherwise returns null. */ public static File getJBossServerHomeDir() { String dir = System.getProperty("jboss.server.home.dir"); if ( dir == null ) { return null; } else { return new File(dir); } } /** * Returns the JBOSS server name, if Dynamo is running on JBOSS. * Otherwise returns null. */ public static String getJBossServerName() { return System.getProperty("jboss.server.name"); } /** * Returns the path to the JBOSS server log file, if it can be * found. Otherwise returns null */ public static String getJBossServerLog() { try { File log = new File(getJBossServerHomeDir(), "log/server.log"); if ( log.exists() ) { return log.getAbsolutePath(); } } catch ( Throwable t ) { logger.catching(t); } return null; } /** * Returns the version of JBOSS being used, if it can be * determined. Otherwise returns UNKNOWN_INFO. This method * expects to find a 'jar-versions.xml' file in the JBoss home * directory. It searches for the <jar> element whose 'name' * attribute is "jboss.jar", and determines the version based on * the value of the 'implVersion' attribute. */ public static String getJBossVersion() { // XXX: might want to simplify this try { File versionFile = new File(getJBossHomeDir(), "jar-versions.xml"); if ( !versionFile.exists() ) { log( "jar-versions.xml file does not exist; " + "unable to determine version info" ); return UNKNOWN_INFO; } String[] children = { "jar" }; for ( Node node : XmlUtils.getNodes(versionFile, false, children) ) { try { String name = node.getAttributes().getNamedItem("name").getNodeValue(); log("Checking node: " + name); if ( name.equals("jboss.jar") ) { String ver = node.getAttributes() .getNamedItem("implVersion") .getNodeValue() .trim(); log("JBOSS version string: " + ver); // implVersion is typically something like // "4.0.1sp1 (build: CVSTag=JBoss_4_0_1_SP1 date=200502160314)" // so, strip off the build information since we don't care about it int idx = ver.indexOf(" (build:"); if ( idx != -1 ) { ver = ver.substring(0, idx).trim(); } return ver; } } catch ( Throwable ti ) { logger.catching(ti); } } } catch ( Throwable t ) { logger.catching(t); } return UNKNOWN_INFO; } private static atg.service.dynamo.Configuration DYN_CONFIG = null; /** * returns the Configuration component being used by Dynamo */ public static atg.service.dynamo.Configuration getDynamoConfiguration() { if ( DYN_CONFIG == null ) { try { DYN_CONFIG = (atg.service.dynamo.Configuration) Nucleus.getGlobalNucleus() .resolveName( CONFIGURATION_COMPONENT ); } catch ( Throwable t ) { logger.catching(t); } } return DYN_CONFIG; } /** * returns the session limit of the specified license component * * @param pLicense the component name of the license in question * @param pResolve true if Nucleus should attempt to create the license * component if it does not exist * * @return The session limit for the license. 0 if the license does not * resolve. */ public static int getSessionLimit(String pLicense, boolean pResolve) { // FIXME: LicenseImpl is not in ATG 10 return Integer.MAX_VALUE; } // ==================== EMAIL ====================== /** * This method is used to send an email message and allows the user * to specify the return address. * * @return boolean true if the mail was sent successfully; otherwise false. */ public static boolean sendEmailWithReturn(String pAddress, String pMsg, String pSubject, String pBodyEncoding, String pReturnAddress) { try { // create a Message with the given From and Subject Message msg = MimeMessageUtils.createMessage(pReturnAddress, pSubject); // set the To recipient MimeMessageUtils.setRecipient(msg, Message.RecipientType.TO, pAddress); // set the message content: multipart message + attachment if ( pBodyEncoding == null || pBodyEncoding.trim().length() == 0 ) { pBodyEncoding = "text/plain"; } ContentPart[] content = { new ContentPart(pMsg, pBodyEncoding) }; MimeMessageUtils.setContent(msg, content); // create the email event EmailEvent em = new EmailEvent(msg); // now send the event SMTPEmailSender sender = new SMTPEmailSender(); sender.setEmailHandlerHostName(MAILHOST); sender.sendEmailEvent(em); } catch ( Exception e ) { logger.catching(e); return false; } return true; } /** * This method is used to send an email message; returns true if * everything went ok; otherwise, returns false */ public static boolean sendEmail(String pAddress, String pMsg, String pSubject, String pBodyEncoding) { return sendEmailWithReturn(pAddress, pMsg, pSubject, pBodyEncoding, pAddress); } /** * This method is used to send an email message; returns true if * everything went ok; otherwise, returns false */ public static boolean sendEmail(String pAddress, String pMsg, String pSubject) { return sendEmail(pAddress, pMsg, pSubject, "text/plain"); } /** * This method is used to send the same email message to a vector * of recipients */ public static void sendEmails(List<String> pAddresses, String pMsg, String pSubject, String pBodyEncoding) { // make sure addresses are valid if ( pAddresses == null || pAddresses.size() <= 0 ) { return; } // send emails Iterator<String> addresses = pAddresses.iterator(); String address = null; while ( addresses.hasNext() ) { try { address = addresses.next(); if ( address != null && address.trim().length() > 0 ) { sendEmail(address.trim(), pMsg, pSubject, null, null, null, pBodyEncoding); } } catch ( Exception e ) { logger.catching(e); } } } /** * This method is used to send the same email message to a vector * of recipients. It encodes the message body as "text/plain". */ public static void sendEmails(List<String> pAddresses, String pMsg, String pSubject) { sendEmails(pAddresses, pMsg, pSubject, "text/plain"); } /** * This method is used to send an email message that contains * several attachments. This method is specifically designed to * accept a map of java.lang.Strings as content parts instead of * java.io.Files. The key in the Map should be a String * representing the name that you would like to show for the * attached file. The value in the Map should be a String * representing the contents of the attachment. */ public static void sendEmail(String pAddress, String pMsg, String pSubject, Map<String, Object> pTextAttachments, Map<String, Object> pHTMLAttachments, File[] pFiles, String pBodyEncoding) { try { // make sure addresses are valid if ( pAddress == null || pAddress.trim().length() == 0 ) { return; } // create a Message with the given From and Subject Message msg = MimeMessageUtils.createMessage(pAddress, pSubject); // set the To recipient MimeMessageUtils.setRecipient(msg, Message.RecipientType.TO, pAddress); // create the MultiPart used to hold everything Multipart mp = new MimeMultipart(); // set the message content: multipart message + attachment BodyPart guts = new MimeBodyPart(); if ( pBodyEncoding == null || pBodyEncoding.trim().length() == 0 ) { pBodyEncoding = "text/plain"; } guts.setContent(pMsg, pBodyEncoding); mp.addBodyPart(guts); // add the text attachments if ( pTextAttachments != null ) { for ( String key : pTextAttachments.keySet() ) { Object val = pTextAttachments.get(key); if ( val != null ) { MimeBodyPart part = new MimeBodyPart(); part.setContent(val.toString(), "text/plain"); part.setDisposition(MimeBodyPart.ATTACHMENT); part.setDescription(key); part.setFileName(key); mp.addBodyPart(part); } } } // add the html attachments if ( pHTMLAttachments != null ) { for ( String key : pHTMLAttachments.keySet() ) { Object val = pHTMLAttachments.get(key); if ( val != null ) { MimeBodyPart part = new MimeBodyPart(); part.setContent(val.toString(), "text/html"); part.setDisposition(MimeBodyPart.ATTACHMENT); part.setDescription(key); part.setFileName(key); mp.addBodyPart(part); } } } // add the File attachments if ( pFiles != null ) { for ( File pFile : pFiles ) { MimeBodyPart part = new MimeBodyPart(); part.setDataHandler(new DataHandler(new FileDataSource(pFile))); part.setFileName(pFile.getName()); mp.addBodyPart(part); } } msg.setContent(mp); // create the email event EmailEvent em = new EmailEvent(msg); // now send the event SMTPEmailSender sender = new SMTPEmailSender(); sender.setEmailHandlerHostName(MAILHOST); sender.sendEmailEvent(em); } catch ( Exception e ) { logger.catching(e); } } /** * This method is used to send an email message that contains * several attachments. This method is specifically designed to * accept a map of java.lang.Strings as content parts instead of * java.io.Files. The key in the Map should be a String * representing the name that you would like to show for the * attached file. The value in the Map should be a String * representing the contents of the attachment. If you wish to * attach java.io.Files, use the static methods found in * atg.service.email.MimeMessageUtils. */ public static void sendEmail(String pAddress, String pMsg, String pSubject, Map<String, Object> pTextAttachments, Map<String, Object> pHTMLAttachments, String pBodyEncoding) { sendEmail( pAddress, pMsg, pSubject, pTextAttachments, pHTMLAttachments, null, pBodyEncoding ); } /** * This method is used to send an email message that contains * several attachments. This method is specifically designed to * accept a map of java.lang.Strings as content parts instead of * java.io.Files. The key in the Map should be a String * representing the name that you would like to show for the * attached file. The value in the Map should be a String * representing the contents of the attachment. If you wish to * attach java.io.Files, use the static methods found in * atg.service.email.MimeMessageUtils. */ public static void sendEmail(String pAddress, String pMsg, String pSubject, Map<String, Object> pTextAttachments, Map<String, Object> pHTMLAttachments) { sendEmail(pAddress, pMsg, pSubject, pTextAttachments, pHTMLAttachments, "text/plain"); } /** * This method is used to send an email message that contains * several attachments. This method is specifically designed to * accept a map of java.lang.Strings as content parts instead of * java.io.Files. The key in the Map should be a String * representing the name that you would like to show for the * attached file. The value in the Map should be a String * representing the contents of the attachment. */ public static void sendEmails(List<String> pAddresses, String pMsg, String pSubject, Map<String, Object> pTextAttachments, Map<String, Object> pHTMLAttachments, File[] pFiles, String pBodyEncoding) { // make sure addresses are valid if ( pAddresses == null || pAddresses.size() <= 0 ) { return; } // send emails Iterator<String> addresses = pAddresses.iterator(); String address = null; while ( addresses.hasNext() ) { try { address = addresses.next(); if ( address != null && address.trim().length() > 0 ) { sendEmail( address.trim(), pMsg, pSubject, pTextAttachments, pHTMLAttachments, pFiles, pBodyEncoding ); } } catch ( Exception e ) { logger.catching(e); } } } /** * This method is used to send an email message that contains * several attachments to multiple recipients. This method is * specifically designed to accept a map of java.lang.Strings as * content parts instead of java.io.Files. The key in the Map * should be a String representing the name that you would like to * show for the attached file. The value in the Map should be a * String representing the contents of the attachment. */ public static void sendEmails(List<String> pAddresses, String pMsg, String pSubject, Map<String, Object> pTextAttachments, Map<String, Object> pHTMLAttachments, String pBodyEncoding) { sendEmails( pAddresses, pMsg, pSubject, pTextAttachments, pHTMLAttachments, null, pBodyEncoding ); } /** * This method is used to send an email message that contains * several attachments to multiple recipients. The message will * have it's main body part encoded as "text/plain". <br>This * method is specifically designed to accept a map of * java.lang.Strings as content parts instead of java.io.Files. * The key in the Map should be a String representing the name that * you would like to show for the attached file. The value in the * Map should be a String representing the contents of the * attachment. If you wish to attach java.io.Files, use the static * methods found in atg.service.email.MimeMessageUtils. */ public static void sendEmails(List<String> pAddresses, String pMsg, String pSubject, Map<String, Object> pTextAttachments, Map<String, Object> pHTMLAttachments) { sendEmails(pAddresses, pMsg, pSubject, pTextAttachments, pHTMLAttachments, "text/plain"); } // ======================== EXCEPTIONS ===================== /** * this method returns a String representation of an Exception's stacktrace */ public static String getStackTrace(Throwable pException) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(bos); pException.printStackTrace(ps); ps.flush(); return bos.toString(); } // ===================== URL ACCESS ======================== /** * this method returns the contents of the page specified as the * URL. the URL should be a fully qualified request string. for * example, http://rygar.atg.com:8880/some/directory/page.jhtml * <p/> * If the boolean parameter is set to true then this method will * throw an Exception if an error occurs; otherwise it will simply * return the contents of the exception. * * @throws MalformedURLException if URL is malformed & pThrow is true * @throws IOException if error happens while reading and pThrow is true */ public static String accessURL(String pUrl, boolean pThrow) throws IOException { URL url = null; StringBuilder results = new StringBuilder(); BufferedReader in = null; InputStreamReader isr = null; try { url = new URL(pUrl); isr = new InputStreamReader(url.openStream()); in = new BufferedReader(isr); String line = null; while ( (line = in.readLine()) != null ) { results.append(line).append("\n"); } return results.toString(); } catch ( MalformedURLException e ) { if ( pThrow ) { throw logger.throwing(e); } else { results.append( "\nEncountered an unexpected error while trying to retrieve the configuration info." + "\nWhen the url " ) .append(url) .append(" was requested, this error was received: \n") .append(getStackTrace(e)) .append("\n"); } } catch ( IOException ioe ) { if ( pThrow ) { throw logger.throwing(ioe); } else { results.append( "\nEncountered an unexpected error while trying to retrieve the configuration info." + "\nWhen the url " ) .append(url) .append(" was requested, this error was received: \n") .append(getStackTrace(ioe)) .append("\n"); } } finally { if ( in != null ) { try { in.close(); } catch ( Exception e ) { logger.catching(e); } } if ( isr != null ) { try { isr.close(); } catch ( Exception e ) { logger.catching(e); } } } return results.toString(); } /** * this method returns the contents of the page specified as the URL. the * URL should be a fully qualified request string. for example, * http://rygar.atg.com:8880/some/directory/page.jhtml * <p/> * Unlike it's sister method with the boolean parameter, this method will * not throw an exception. */ public static String accessURL(String pUrl) { try { return accessURL(pUrl, false); } catch ( Exception e ) { logger.catching(e); return "\nEncountered an unexpected error while trying to retrieve the configuration info." + "\nWhen the url " + pUrl + " was requested, this error was received: \n" + getStackTrace(e) + "\n"; } } // ==================== File IO ============================ /** * Writes the byte array into the specified file. * * @param pFile the file to write to * @param pBytes the bytes to write * * @throws IOException if an error occurred opening or reading the file. */ public static void writeFileBytes(File pFile, byte[] pBytes) throws IOException { if ( pBytes == null ) { pBytes = new byte[0]; } FileOutputStream fos = null; try { fos = new FileOutputStream(pFile); fos.write(pBytes); } catch ( IOException e ) { throw logger.throwing(e); } finally { try { if ( fos != null ) { fos.close(); } } catch ( IOException exc ) { logger.catching(exc); } } } /** * converts a delimiter separated String of file names into an * array and expands all System property variables in the Strings. * it does not check whether resolved file paths exist. * * @param pFiles delimited string of files to be converted to array. * @param pDelimiter delimiter string used to separated files * * @return String[] array of expanded paths * @throws Exception if files can't be resolved properly */ public static String[] convertFileArray(String pFiles, String pDelimiter) throws Exception { return convertFileArray(pFiles, pDelimiter, null); } /** * converts a delimiter separated String of file names into an array * and expands all variables in the Strings. it does not check whether * resolved file paths exist. * * @param pFiles delimited string of files to be converted to array. * @param pDelimiter delimiter string used to separated files * @param pPrimaryMapping optional primary mapping of key/value pairs to * substitute into file paths whererever the syntax <tt>{...}</tt> * is found. If parameter is null, or mapping not found, then * System.getProperties() is checked. * * @return array of expanded paths * @throws Exception if files can't be resolved properly */ public static String[] convertFileArray(String pFiles, String pDelimiter, Properties pPrimaryMapping) throws Exception { if ( pDelimiter == null ) { pDelimiter = ""; } StringTokenizer st = new StringTokenizer(pFiles, pDelimiter); List<String> files = new LinkedList<String>(); while ( st.hasMoreTokens() ) { files.add(expand(st.nextToken(), pPrimaryMapping)); } return files.toArray(new String[files.size()]); } /** * expands all System property variables specified in the supplied * String using curly braces syntax <tt>{...}</tt> and returns the * resulting String. * * @param pString the string to expand. * * @throws Exception if a System property resolves to null or if * the enclosing braces are not properly matched. */ public static String expand(String pString) throws Exception { return expand(pString, null); } /** * expands all property variables specified in the supplied String * using curly braces syntax <tt>{...}</tt> and returns the * resulting String. Property names inside the curly braces can be * either a simple String referring to a Java System property, such * as "SystemProperty.X", or can be in AppModuleResource format, * such as * "appModuleResource?moduleID=MyModule&resource=my/resource/file". * * @param pString the string to expand. * @param pPrimaryMapping an optional primary key/value mapping to use * for System property substitutions. If param is null, or if * mapping not found, then System.getProperties().getProperty(foo) * is used. * * @return the expanded string. * @throws Exception if a System or AppModuleResource property * resolves to null or if the enclosing braces are not properly * matched. */ public static String expand(String pString, Properties pPrimaryMapping) throws Exception { int idx = pString.indexOf("{"); while ( idx != -1 ) { int end = pString.indexOf("}"); if ( end == -1 ) { throw new Exception("Unclosed braces in String " + pString); } String pname = pString.substring(idx + 1, end); String prop = null; if ( pPrimaryMapping != null ) { prop = pPrimaryMapping.getProperty(pname); } if ( prop == null ) { if ( pname.startsWith("appModuleResource?") ) { prop = resolveAppModuleResourceReference(pname).getPath(); } // atg.dynamo.root and atg.dynamo.home are resolved specially // because of BigEar else if ( pname.equals(ROOT_VAR) ) { prop = getDynamoRootDir().getPath(); } else if ( pname.equals(HOME_VAR) ) { prop = getDynamoHomeDir().getPath(); } else { prop = System.getProperty(pname); } } if ( prop == null ) { throw new Exception( "System property '" + pString.substring(idx + 1, end) + "' is null. String " + pString + " can not be resolved." ); } pString = pString.substring(0, idx) + prop + pString.substring(end + 1); idx = pString.indexOf("{"); } return pString; } // ===================== atg dynamo info =================================== /** * product module corresponding to ATG Dynamo */ private static String ATGDYNAMO_PRODUCT_MODULE = "DPS"; /** * specifies the name of the ATG Dynamo product module that will be * loaded if Dynamo is being used. */ public static void setAtgDynamoProductModule(String pModule) { ATGDYNAMO_PRODUCT_MODULE = pModule; } /** * returns the name of the ATG Dynamo product module that will be * loaded if Dynamo is being used. */ public static String getAtgDynamoProductModule() { return ATGDYNAMO_PRODUCT_MODULE; } /** * returns an AppModule corresponding to the ATG Dynamo if that * product is loaded. If it isn't loaded then returns null. */ public static AppModule getAtgDynamoModule() { // get all modules that were started with dynamo for ( Object o : getAppLauncher().getModules() ) { AppModule module = (AppModule) o; if ( module.getName().equals(getAtgDynamoProductModule()) ) { return module; } } return null; } // ==================== atg j2ee server info ============================== /** * product module corresponding to ATG's J2EE Server */ public static String ATGJ2EESERVER_PRODUCT_MODULE = "J2EEServer"; /** * specifies the name of the ATG J2EE Server product module that * will be loaded if ATG's J2EE Server is being used. */ public static void setAtgJ2eeServerProductModule(String pModule) { ATGJ2EESERVER_PRODUCT_MODULE = pModule; } /** * returns the name of the ATG J2EE Server product module that will * be loaded if ATG's J2EE Server is being used. */ public static String getAtgJ2eeServerProductModule() { return ATGJ2EESERVER_PRODUCT_MODULE; } /** * returns an AppModule corresponding to the ATG J2EE Server if * that product is loaded. If it isn't loaded then returns null. */ public static AppModule getAtgJ2eeServerModule() { // get all modules that were started with dynamo for ( Object o : getAppLauncher().getModules() ) { AppModule module = (AppModule) o; if ( module.getName().equals(getAtgJ2eeServerProductModule()) ) { return module; } } return null; } // ==================== application info ============================ /** * possible application product modules that may be installed */ private static String[] APPLICATION_PRODUCT_MODULES = { "ACA", "ABTest", "DCS-SO", "CAF" }; /** * specifies the names of possible application product modules that * may be installed in Dyanmo. used to help report on which * application modules are running. */ public void setApplicationProductModules(String[] pModules) { APPLICATION_PRODUCT_MODULES = pModules; } /** * returns the names of possible application product modules that * may be installed in Dyanmo. used to help report on which * application modules are running. NOTE: This method should not * be called. It is only provided so we can specify application * modules in a .properties file. Java classes should call method * getApplicationModules(). */ public String[] getApplicationProductModules() { return APPLICATION_PRODUCT_MODULES; } /** * returns an array of AppModule items corresponding to the * currently running application products. */ public static AppModule[] getApplicationModules() { List<AppModule> apps = new LinkedList<AppModule>(); // get all modules that were started with dynamo for ( Object o : getAppLauncher().getModules() ) { AppModule module = (AppModule) o; for ( String APPLICATION_PRODUCT_MODULE : APPLICATION_PRODUCT_MODULES ) { // in order to work around bug 80207, we allow a colon ":" in // the specified module names. if a colon exists, the name // before the colon is the name of the module that would be // started if the application is running. the name after the // colon is the module containing the MANIFEST.MF file with // build info. if there is no colon, assume the two modules // are the same. int idx = APPLICATION_PRODUCT_MODULE.indexOf(":"); if ( idx == -1 ) { // no colon... if ( (APPLICATION_PRODUCT_MODULE).equals(module.getName()) ) { apps.add(module); } } else { if ( APPLICATION_PRODUCT_MODULE.substring(0, idx).equals(module.getName()) ) { // NOTE: getAppLauncher().getModule(...) will return a // module as long as it exists; the module does not need // to be running. try { AppModule mod = getAppLauncher().getModule( APPLICATION_PRODUCT_MODULE.substring(idx + 1) ); logger.info("Mod: {}", mod); if ( mod != null ) { apps.add(mod); } else { throw new Exception( APPLICATION_PRODUCT_MODULE.substring( idx + 1 ) + " not found." ); } } catch ( Exception ale ) { logger.catching(ale); logger.warn( "Cannot resolve module '{}'.", APPLICATION_PRODUCT_MODULE.substring(idx + 1) ); } } } } } return apps.toArray(new AppModule[apps.size()]); } // =========== generic AppModule info retrieval methods ==================== private static AppLauncher mAppLauncher = null; /** * Returns the AppLauncher used to load this class. */ private static AppLauncher getAppLauncher() { if ( mAppLauncher == null ) { mAppLauncher = AppLauncher.getAppLauncher(TestUtils.class); } return mAppLauncher; } /** * Retrieves a File resource from a Dynamo Module. Note that the * module does not need to be started, it simply has to be * installed in the Dynamo. Returned file is <u>not</u> verified * to exist. * * @param pModuleName the name of the Dynamo module to look in. e.g. * "SystemTests.JSPTest" * @param pResourceURI the URI of the File to get from the module. e.g. "mite.xml" * * @return the requested file. */ public static File getModuleResourceFile(String pModuleName, String pResourceURI) { return getAppLauncher().getAppModuleManager().getResourceFile(pModuleName, pResourceURI); } /** * Resolves an appModuleResource reference by parsing the string * into its constituent ModuleID and ResourceURI. * * @param pReference The AppModuleResource reference to resolve. Expected to be of * format: * <br><tt>appModuleResource?moduleID=<i>moduleID</i>&resourceURI=<i>some/URI</i></tt> * * @return the referenced module resource. * @throws IllegalArgumentException if the specified reference does not have the proper * structure. */ public static File resolveAppModuleResourceReference(String pReference) { // there's probably a standard utility method in Dynamo to do this // resolution, but i can't find it... String moduleID = null; String resourceURI = null; String ref = pReference; try { int idx = ref.indexOf("moduleID="); // locate moduleID delimiter if ( idx == -1 ) { throw new Exception(); } ref = ref.substring(idx + 9); // strip up to and including 'moduleID=' idx = ref.indexOf("&resourceURI="); // get index of resourceURI delimiter moduleID = ref.substring(0, idx); // extract moduleID resourceURI = ref.substring(idx + 13); // extract resourceURI } catch ( Throwable t ) { logger.catching(t); throw new IllegalArgumentException( "Can not resolve appModuleReference. " + "Illegal reference syntax: " + pReference ); } return getModuleResourceFile(moduleID, resourceURI); } /** * Retrieves a piece of information from the MANIFEST of the * supplied AppModule. Returns null if the specified information * can't be found. */ public static String getManifestInfo(AppModule pModule, String pEntry) { return getManifestInfo(pModule.getManifest(), pEntry); } /** * Logs a message using log4j. * * @param pMessage Log message to output. */ public static void log(String pMessage) { logger.info(pMessage); } /** * Retrieves a piece of information from the specified Manifest file. * Returns null if the specified information can't be found. */ public static String getManifestInfo(Manifest pManifest, String pEntry) { // if manifest or entry key is null return null... if ( pManifest == null || pEntry == null ) { return null; } if ( pManifest.getMainAttributes() == null ) { return null; } else { return pManifest.getMainAttributes().getValue(pEntry); } } /** * Returns the ATG product version ("ATG-Version") of the specified module. * Returns UNKNOWN_INFO if the product version can't be determined. */ public static String getAtgVersion(AppModule pModule) { String version = getManifestInfo(pModule, "ATG-Version"); if ( version != null ) { return version; } else { return UNKNOWN_INFO; } } /** * Returns the ATG product build number ("ATG-Build") of the * specified module. Returns UNKNOWN_INFO if the build number * can't be determined. */ public static String getAtgBuildNumber(AppModule pModule) { String build = getManifestInfo(pModule, "ATG-Build"); if ( build != null ) { return build; } else { return UNKNOWN_INFO; } } /** * Returns the ATG patch version ("ATG-Patch-Version") of the * specified module. Returns null if the module's version can be * determined, but a patch version can't. Returns UNKNOWN_INFO if * neither the product or patch version can be determined. */ public static String getAtgPatchVersion(AppModule pModule) { String version = getManifestInfo(pModule, "ATG-Patch-Version"); if ( version != null ) { return version; } else if ( getAtgVersion(pModule).equals(UNKNOWN_INFO) ) { return UNKNOWN_INFO; } else { return null; } } /** * Returns the ATG patch build number ("ATG-Patch-Build") of the * specified module. Returns null if the module's build number can * be determined, but a patch build number can't. Returns * UNKNOWN_INFO if neither the product or patch build number can be * determined. */ public static String getAtgPatchBuildNumber(AppModule pModule) { String build = getManifestInfo(pModule, "ATG-Patch-Build"); if ( build != null ) { return build; } else if ( getAtgBuildNumber(pModule).equals(UNKNOWN_INFO) ) { return UNKNOWN_INFO; } else { return null; } } /** * Returns the ATG full product version ("ATG-Version-Full") of the * specified module. Returns UNKNOWN_INFO if the full product * version can't be determined. */ public static String getAtgFullVersion(AppModule pModule) { String version = getManifestInfo(pModule, "ATG-Version-Full"); if ( version != null ) { return version; } else { return UNKNOWN_INFO; } } // ==================== Dynamo Environment Information ===================== // some methods called on DynamoEnv are not available in older // versions of the class, so use reflection to maintain backward // compatibility. private static DynamoEnv mDynamoEnv = null; private static DynamoEnv dynamoEnv() { if ( mDynamoEnv == null ) { try { mDynamoEnv = DynamoEnv.class.newInstance(); } catch ( Throwable t ) { logger.catching(t); } } return mDynamoEnv; } /** * Returns true if Dynamo is running as BigEar; otherwise returns false. */ public static boolean isBigEar() { Boolean isBigEar = (Boolean) invokeMethod( dynamoEnv(), "isBigEar", null, null, Boolean.FALSE ); return isBigEar; } /** * Returns true if Dynamo is running as BigEar in standalone mode; * otherwise returns false. */ public static boolean isBigEarStandalone() { Boolean isStandalone = (Boolean) invokeMethod( dynamoEnv(), "getStandaloneMode", null, null, Boolean.FALSE ); return isStandalone; } /** * Returns true is Dynamo is running with liveconfig enabled; * otherwise returns false. */ public static boolean isLiveconfig() { // 'isLiveconfig' is a new method in Koko (pr 88105). try it, but // if that doesn't work try the 'getProperty' method that was used // for ATG 7.0. finally, if that doesn't work just examine System // properties as a last check. Boolean isliveconfig = (Boolean) invokeMethod( dynamoEnv(), "isLiveconfig", null, null, null ); if ( isliveconfig == null ) { // that method didn't work, so try this method - which should // work in ATG 7 String[] args = { "atg.dynamo.liveconfig" }; String propval = (String) invokeMethod( dynamoEnv(), "getProperty", new Class[] { String.class }, args, null ); if ( propval != null ) { isliveconfig = "on".equalsIgnoreCase(propval); } else { isliveconfig = "on".equalsIgnoreCase( System.getProperty( "atg.dynamo.liveconfig" ) ); } } return isliveconfig != null && isliveconfig; } public static Object invokeMethod(Object pObj, String pMethodName, Class<?>[] pSignature, Object[] pParams, Object pDefault) { Object returnval = null; try { Method meth = pObj.getClass().getMethod(pMethodName, pSignature); returnval = meth.invoke(pObj, pParams); //if ( isLoggingDebug() ) logDebug("Method '" + pMethodName + "' //invoked - return value: " + returnval); } catch ( Throwable t ) { logger.catching(t); //if ( isLoggingDebug() ) logDebug("Method '" + pMethodName + "' //could not be invoked.", t); returnval = pDefault; } return returnval; } } // end of class