org.netbeans.jellytools.modules.j2ee.J2eeTestCase Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.netbeans.jellytools.modules.j2ee;
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
import junit.framework.Test;
import junit.framework.TestCase;
import org.netbeans.jellytools.Bundle;
import org.netbeans.jellytools.JellyTestCase;
import org.netbeans.jellytools.NbDialogOperator;
import org.netbeans.jellytools.ProjectsTabOperator;
import static org.netbeans.jellytools.modules.j2ee.J2eeTestCase.Server.*;
import org.netbeans.jellytools.modules.j2ee.nodes.J2eeServerNode;
import org.netbeans.jellytools.nodes.Node;
import org.netbeans.jemmy.JemmyException;
import org.netbeans.jemmy.operators.JComboBoxOperator;
import org.netbeans.jemmy.operators.JDialogOperator;
import org.netbeans.jemmy.operators.JTreeOperator;
import org.netbeans.junit.NbModuleSuite;
import org.netbeans.junit.NbModuleSuite.Configuration;
/**
* Registers application servers to IDE and adds test cases to suite only
* if requested server is available.
*
* @author Jindrich Sedek
* @author Jiri Skrivanek
*/
public class J2eeTestCase extends JellyTestCase {
private static final String PID_FILE_PREFIX = "J2EE_TEST_CASE_PID_FILE";
private static final String JBOSS_PATH = "org.netbeans.modules.j2ee.jboss4.installRoot";
private static final String GLASSFISH_HOME = "glassfish.home";
private static final String TOMCAT_HOME = "tomcat.home";
private static final String JBOSS_HOME = "jboss.home";
private static final Logger LOG = Logger.getLogger(J2eeTestCase.class.getName());
private static boolean serversLogged = false;
private static List alreadyRegistered = new ArrayList();
private static boolean addServerTestsCalled = false;
/** Used in self test to verify registration. */
static boolean isSelfTest = false;
public J2eeTestCase(String name) {
super(name);
}
@Override
protected void setUp() throws Exception {
super.setUp();
createPid();
}
/**
* Create a temp file starting with J2EE_TEST_CASE_PID_FILE and ending with
* the pid of the test process. The pid is used by hudson to print stacktrace
* before aborting build because of timeout.
*
* @throws java.io.IOException
*/
private void createPid() throws IOException {
String pid = ManagementFactory.getRuntimeMXBean().getName();
pid = pid.substring(0, pid.indexOf('@'));
String tmpDirPath = System.getProperty("java.io.tmpdir");
File tmpDir = new File(tmpDirPath);
for (String file : tmpDir.list()) {
if (file.startsWith(PID_FILE_PREFIX)) {
if (!(new File(tmpDir, file).delete())) {
LOG.log(Level.WARNING, "File ''{0}{1}{2}'' not successfully deleted!", new Object[]{tmpDirPath, File.pathSeparator, file});
}
}
}
if (!(new File(tmpDir, PID_FILE_PREFIX + pid).createNewFile())) {
LOG.log(Level.WARNING, "File ''{0}{1}" + PID_FILE_PREFIX + "{2}'' not successfully created!", new Object[]{tmpDirPath, File.pathSeparator, pid});
}
}
/**
* TODO - check if it is still functional.
*/
private static void registerJBoss() {
String jbossPath = getServerHome(JBOSS);
if (isValidPath(jbossPath)) {
LOG.log(Level.INFO, "Setting server path {0}", jbossPath);
System.setProperty(JBOSS_PATH, jbossPath);
alreadyRegistered.add(JBOSS);
}
}
/** Returns paths to server home set by system property or null if not defined.
* @param server requested server
* @return path to server home or null if not defined
*/
private static String getServerHome(Server server) {
switch (server) {
case JBOSS:
return System.getProperty(JBOSS_HOME);
case GLASSFISH:
return System.getProperty(GLASSFISH_HOME);
case TOMCAT:
return System.getProperty(TOMCAT_HOME);
}
return null;
}
/** Returns true if given path points to existing folder.
* @param path path to validate
* @return true if given path points to existing folder, false otherwise.
*/
private static boolean isValidPath(String path) {
if (path == null) {
return false;
}
File f = new File(path);
if (f.isDirectory()) {
LOG.log(Level.FINE, "{0} - is valid directory", path);
return true;
} else {
if (!f.exists()) {
LOG.log(Level.INFO, "{0} - does not exists!", path);
} else {
LOG.log(Level.INFO, "{0} - exists, but it is not a directory!", path);
}
return false;
}
}
/**
*
* Create all modules suite.
*
* @param server server needed for the suite
* @param clazz class object to create suite for
* @param testNames test names to add into suite
* @return executable Test instance
*/
protected static Test createAllModulesServerSuite(Server server, Class extends TestCase> clazz, String... testNames) {
Configuration result = NbModuleSuite.createConfiguration(clazz);
result = addServerTests(server, result, testNames).enableModules(".*").clusters(".*").honorAutoloadEager(true);
return result.suite();
}
/**
* Add tests into configuration. Tests are added only if it's sure there
* is some server registered in the IDE.
*
* @param conf test configuration
* @param testNames names of added tests
* @return clone of the test configuration
*/
protected static Configuration addServerTests(Configuration conf, String... testNames) {
return addServerTests(ANY, conf, testNames);
}
/**
* Add tests into configuration.
* Tests are added only if there is the server instance registered in the
* IDE.
*
* @param server server that is needed by tests
* @param conf test configuration
* @param testNames names of added tests
* @return clone of the test configuration
*/
protected static Configuration addServerTests(Server server, Configuration conf, String... testNames) {
return addServerTests(server, conf, null, testNames);
}
/**
* Add tests into configuration.
* Tests are added only if there is the server instance registered in the
* IDE.
*
* @param server server that is needed by tests
* @param conf test configuration
* @param clazz tested class
* @param testNames names of added tests
* @return clone of the test configuration
*/
protected static Configuration addServerTests(Server server, Configuration conf, Class extends TestCase> clazz, String... testNames) {
addServerTestsCalled = true;
if (isRegistered(server)) {
LOG.info("adding server tests");
return addTest(conf, clazz, testNames);
} else {
if (server == GLASSFISH || server == ANY) {
registerGlassFish();
if (isRegistered(GLASSFISH)) {
return addTest(conf, clazz, testNames);
}
}
if (server == TOMCAT || server == ANY) {
registerTomcat();
if (isRegistered(TOMCAT)) {
return addTest(conf, clazz, testNames);
}
}
if (server == JBOSS || server == ANY) {
registerJBoss();
if (isRegistered(JBOSS)) {
return addTest(conf, clazz, testNames);
}
}
LOG.info("no server to add tests");
if (!serversLogged) {
serversLogged = true;
LOG.log(Level.INFO, "{0}={1}", new String[]{JBOSS_HOME, getServerHome(JBOSS)});
LOG.log(Level.INFO, "{0}={1}", new String[]{TOMCAT_HOME, getServerHome(TOMCAT)});
LOG.log(Level.INFO, "{0}={1}", new String[]{GLASSFISH_HOME, getServerHome(GLASSFISH)});
}
try {
return conf.addTest("testEmpty");
} catch (IllegalStateException exc) {
//empty configuration
return conf.addTest(J2eeTestCase.class, "testEmpty");
}
}
}
/**
* Returns true
if given server is already registered in the IDE,
* false
otherwise. It works only in suite methods when addServerTests
* method was called at least once. It cannot be used in test cases.
* @param server server to decide about
* @return true
if the server
is registered
*/
protected static boolean isRegistered(Server server) {
if (!addServerTestsCalled) {
throw new IllegalStateException("Cannot call isRegistered here. It is allowed only in suite method after addServerTests is called.");
}
if (server == ANY) {
return !alreadyRegistered.isEmpty();
} else {
return alreadyRegistered.contains(server);
}
}
/**
* Returns J2eeServerNode for given server
*
* @param server
* @return J2eeServerNode for given server
*/
protected J2eeServerNode getServerNode(Server server) {
switch (server) {
case GLASSFISH:
return J2eeServerNode.invoke("GlassFish");
case JBOSS:
return J2eeServerNode.invoke("JBoss");
case TOMCAT:
return J2eeServerNode.invoke("Tomcat");
case ANY:
for (Server serv : Server.values()) {
if (serv == ANY) {
continue;
}
try {
return getServerNode(serv);
} catch (JemmyException e) {
// ignore and continue with next server
}
}
throw new IllegalArgumentException("No server is registred in IDE");
default:
throw new IllegalArgumentException("Unsupported server");
}
}
public static enum Server {
TOMCAT, GLASSFISH, JBOSS, ANY
}
/**
* Empty test is executed while there is missing server and other tests would
* fail because of missing server.
*/
public void testEmpty() {
// nothing to do
}
private static Configuration addTest(Configuration conf, Class extends TestCase> clazz, String... testNames) {
if (clazz == null) {
if ((testNames == null) || (testNames.length == 0)) {
return conf;
} else {
return conf.addTest(testNames);
}
} else {
return conf.addTest(clazz, testNames);
}
}
/**
* Resolve missing server. This method should be called after opening some project.
* If the Missing server dialog appears, it's closed and first server from
* project properties is used to resolve the missing server problem.
*
* @param projectName name of project
*/
protected void resolveServer(String projectName) {
waitScanFinished();
String openProjectTitle = Bundle.getString("org.netbeans.modules.j2ee.common.ui.Bundle", "MSG_Broken_Server_Title");
if (JDialogOperator.findJDialog(openProjectTitle, true, true) != null) {
new NbDialogOperator(openProjectTitle).close();
LOG.info("Resolving server");
// open project properties
ProjectsTabOperator.invoke().getProjectRootNode(projectName).properties();
// "Project Properties"
String projectPropertiesTitle = Bundle.getStringTrimmed("org.netbeans.modules.web.project.ui.customizer.Bundle", "LBL_Customizer_Title");
NbDialogOperator propertiesDialogOper = new NbDialogOperator(projectPropertiesTitle);
// select "Run" category
new Node(new JTreeOperator(propertiesDialogOper), "Run").select();
// set default server
new JComboBoxOperator(propertiesDialogOper).setSelectedIndex(0);
propertiesDialogOper.ok();
// if setting default server, it scans server jars; otherwise it continues immediatelly
waitScanFinished();
}
}
/* Registers a GlassFish instance by creating instance file in cluster config.
*
* It can be called from ant script the following way:
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
private static void registerGlassFish() {
String glassFishHome = getServerHome(GLASSFISH);
if (!isValidPath(glassFishHome) || !isValidPath(glassFishHome + "/glassfish/domains/domain1")) {
LOG.log(Level.WARNING, "Valid GlassFish server not found at path {0}", glassFishHome);
return;
}
LOG.log(Level.INFO, "Registering GlassFish server at {0}", glassFishHome);
if (isSelfTest) {
alreadyRegistered.add(GLASSFISH);
return;
}
try {
int result = registerServer(
GLASSFISH,
"org.netbeans.modules.glassfish.common.registration.AutomaticRegistration",
findCluster("enterprise"),
glassFishHome + "/glassfish");
if (result != 0) {
LOG.log(Level.WARNING, "registerServer in separate JVM failed with status {0}. "
+ "Trying to register server within this JVM "
+ "but it can influence test cases by loading classes too early", result);
Class> regClass = Class.forName("org.netbeans.modules.glassfish.common.registration.AutomaticRegistration", true, getLoader(GLASSFISH));
Method method = regClass.getDeclaredMethod("autoregisterGlassFishInstance", String.class, String.class, String.class);
method.setAccessible(true);
result = (Integer) method.invoke(null, findCluster("enterprise"), glassFishHome + "/glassfish", "");
}
if (result == 0) {
alreadyRegistered.add(GLASSFISH);
}
} catch (Exception ex) {
LOG.log(Level.WARNING, "Registering GlassFish server failed.", ex);
}
}
/* Registers a Tomcat instance by creating instance file in cluster config.
*
* It can be called from ant script the following way:
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
private static void registerTomcat() {
String tomcatHome = getServerHome(TOMCAT);
if (!isValidPath(tomcatHome)) {
LOG.log(Level.WARNING, "Valid Tomcat server not found at path {0}", tomcatHome);
return;
}
LOG.log(Level.INFO, "Registering Tomcat server at {0}", tomcatHome);
if (isSelfTest) {
alreadyRegistered.add(TOMCAT);
return;
}
try {
int result = registerServer(
TOMCAT,
"org.netbeans.modules.tomcat5.registration.AutomaticRegistration",
findCluster("enterprise"),
tomcatHome);
if (result != 0) {
// try to register server within this JVM but it can influence test cases
// by loading classes too early
Class> regClass = Class.forName("org.netbeans.modules.tomcat5.registration.AutomaticRegistration", true, getLoader(TOMCAT));
Method method = regClass.getDeclaredMethod("registerTomcatInstance", String.class, String.class);
method.setAccessible(true);
result = (Integer) method.invoke(null, findCluster("enterprise"), tomcatHome);
}
if (result == 0) {
alreadyRegistered.add(TOMCAT);
}
} catch (Exception ex) {
LOG.log(Level.WARNING, "Registering Tomcat server failed.", ex);
}
}
/** Returns class path for loading classes for automatic registration
* of server instances.
* @param server server to be registered
* @return class path
*/
private static String getClassPath(Server server) {
StringBuilder sb = new StringBuilder();
for(File file : getJars(server)) {
sb.append(file.getAbsolutePath());
sb.append(File.pathSeparatorChar);
}
sb.deleteCharAt(sb.length() - 1);
return sb.toString();
}
/** Returns class loader for loading classes for automatic registration
* of server instances.
* @param server server to be registered
* @return class loader
*/
private static URLClassLoader getLoader(Server server) throws MalformedURLException {
List urls = new ArrayList();
for(File file : getJars(server)) {
urls.add(file.toURI().toURL());
}
URLClassLoader loader = new URLClassLoader(urls.toArray(new URL[urls.size()]));
return loader;
}
/** Returns list of jars needed for loading classes for automatic registration
* of server instances.
* @param server server to be registered
* @return list of jars
*/
private static List getJars(Server server) {
List jars = new ArrayList();
jars.add(new File(findCluster("platform"), "core/core-base.jar"));
jars.add(new File(findCluster("platform"), "core/core.jar"));
jars.add(new File(findCluster("platform"), "lib/boot.jar"));
jars.add(new File(findCluster("platform"), "lib/org-openide-modules.jar"));
jars.add(new File(findCluster("platform"), "core/org-openide-filesystems.jar"));
jars.add(new File(findCluster("platform"), "lib/org-openide-util.jar"));
jars.add(new File(findCluster("platform"), "lib/org-openide-util-base.jar"));
jars.add(new File(findCluster("platform"), "lib/org-openide-util-lookup.jar"));
jars.add(new File(findCluster("platform"), "lib/org-openide-util-ui.jar"));
jars.add(new File(findCluster("enterprise"), "modules/org-netbeans-modules-j2eeapis.jar"));
jars.add(new File(findCluster("enterprise"), "modules/org-netbeans-modules-j2eeserver.jar"));
if (server == GLASSFISH) {
jars.add(new File(findCluster("enterprise"), "modules/org-netbeans-modules-glassfish-common.jar"));
jars.add(new File(findCluster("enterprise"), "modules/org-netbeans-modules-glassfish-tooling.jar"));
} else if (server == TOMCAT) {
jars.add(new File(findCluster("enterprise"), "modules/org-netbeans-modules-tomcat5.jar"));
}
return jars;
}
/**
* Registers server by calling class in separate JVM to not influence test cases.
* @param server server type
* @param mainClass main class
* @param clusterDir cluster dir
* @param serverHome server home
* @return result of execution, 0 if successful
* @throws Exception
*/
private static int registerServer(Server server, String mainClass, String clusterDir, String serverHome) throws Exception {
String jvm = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
List command = new ArrayList();
command.add(jvm);
command.add("-cp");
command.add(getClassPath(server));
command.add(mainClass);
command.add(clusterDir);
command.add(serverHome);
ProcessBuilder processBuilder = new ProcessBuilder(command);
Process process = processBuilder.start();
int result = process.waitFor();
return result;
}
/** Returns absolute path to given cluster or null if not found.
* @param clusterName cluster name
* @return absolute path to enterprise cluster.
*/
private static String findCluster(String clusterName) {
String clusters = System.getProperty("cluster.path.final");
assert clusters != null : "cluster.path.final must be set.";
for (String cluster : tokenizePath(clusters)) {
if (cluster.endsWith(clusterName)) {
return cluster;
}
}
return null;
}
/**
* Split an Ant-style path specification into components.
* Tokenizes on :
and ;
, paying
* attention to DOS-style components such as C:\FOO.
* Also removes any empty components.
* Copied from org.netbeans.spi.project.support.ant.PropertyUtils.
* @param path an Ant-style path (elements arbitrary) using DOS or Unix separators
* @return a tokenization of that path into components
*/
private static String[] tokenizePath(String path) {
List l = new ArrayList();
StringTokenizer tok = new StringTokenizer(path, ":;", true); // NOI18N
char dosHack = '\0';
char lastDelim = '\0';
int delimCount = 0;
while (tok.hasMoreTokens()) {
String s = tok.nextToken();
if (s.length() == 0) {
// Strip empty components.
continue;
}
if (s.length() == 1) {
char c = s.charAt(0);
if (c == ':' || c == ';') {
// Just a delimiter.
lastDelim = c;
delimCount++;
continue;
}
}
if (dosHack != '\0') {
// #50679 - "C:/something" is also accepted as DOS path
if (lastDelim == ':' && delimCount == 1 && (s.charAt(0) == '\\' || s.charAt(0) == '/')) {
// We had a single letter followed by ':' now followed by \something or /something
s = "" + dosHack + ':' + s;
// and use the new token with the drive prefix...
} else {
// Something else, leave alone.
l.add(Character.toString(dosHack));
// and continue with this token too...
}
dosHack = '\0';
}
// Reset count of # of delimiters in a row.
delimCount = 0;
if (s.length() == 1) {
char c = s.charAt(0);
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
// Probably a DOS drive letter. Leave it with the next component.
dosHack = c;
continue;
}
}
l.add(s);
}
if (dosHack != '\0') {
//the dosHack was the last letter in the input string (not followed by the ':')
//so obviously not a drive letter.
//Fix for issue #57304
l.add(Character.toString(dosHack));
}
return l.toArray(new String[l.size()]);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy