blob: 01e32466d2cb60d4bbc4be2256e5a0c030858437 [file] [log] [blame]
/*
* 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.modules.javaee.wildfly;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AllPermission;
import java.security.CodeSource;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.enterprise.deploy.shared.factories.DeploymentFactoryManager;
import javax.enterprise.deploy.spi.DeploymentManager;
import javax.enterprise.deploy.spi.exceptions.DeploymentManagerCreationException;
import javax.enterprise.deploy.spi.factories.DeploymentFactory;
import org.netbeans.modules.j2ee.deployment.plugins.api.InstanceProperties;
import org.netbeans.modules.javaee.wildfly.ide.ui.WildflyPluginProperties;
import org.netbeans.modules.javaee.wildfly.ide.ui.WildflyPluginUtils;
import org.netbeans.modules.javaee.wildfly.ide.ui.WildflyPluginUtils.Version;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
/**
*
* @author Petr Hejl
*/
public class WildflyDeploymentFactory implements DeploymentFactory {
public static final String URI_PREFIX = "wildfly-deployer:"; // NOI18N
private static final String DISCONNECTED_URI = URI_PREFIX + "http://localhost:8080&"; // NOI18N
private static final Logger LOGGER = Logger.getLogger(WildflyDeploymentFactory.class.getName());
/**
* Mapping of a instance properties to a deployment factory.
* <i>GuardedBy(WildflyDeploymentFactory.class)</i>
*/
private final Map<InstanceProperties, DeploymentFactory> factoryCache =
new WeakHashMap<InstanceProperties, DeploymentFactory>();
/**
* Mapping of a instance properties to a deployment manager.
* <i>GuardedBy(WildflyDeploymentFactory.class)</i>
*/
private final Map<InstanceProperties, WildflyDeploymentManager> managerCache =
new WeakHashMap<InstanceProperties, WildflyDeploymentManager>();
private final Map<InstanceProperties, WildflyDeploymentFactory.WildFlyClassLoader> classLoaderCache =
new WeakHashMap<InstanceProperties, WildflyDeploymentFactory.WildFlyClassLoader>();
private static WildflyDeploymentFactory instance;
private WildflyDeploymentFactory() {
super();
}
public static synchronized WildflyDeploymentFactory getInstance() {
if (instance == null) {
instance = new WildflyDeploymentFactory();
DeploymentFactoryManager.getInstance().registerDeploymentFactory(instance);
}
return instance;
}
public static class WildFlyClassLoader extends URLClassLoader {
/**
* Patching the xnio code to avoid bug #249135
* @see https://netbeans.org/bugzilla/show_bug.cgi?id=249135
*/
private final boolean patchXnio;
public WildFlyClassLoader(URL[] urls, ClassLoader parent, boolean patchXnio) throws MalformedURLException, RuntimeException {
super(urls, parent);
this.patchXnio = patchXnio;
}
@Override
protected PermissionCollection getPermissions(CodeSource codeSource) {
Permissions p = new Permissions();
p.add(new AllPermission());
return p;
}
@Override
public Enumeration<URL> getResources(String name) throws IOException {
// get rid of annoying warnings
if (name.contains("jndi.properties")) { // NOI18N
return Collections.enumeration(Collections.<URL>emptyList());
}
return super.getResources(name);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// see issue #249135
if (patchXnio && "org.xnio.nio.WorkerThread".equals(name)) { // NOI18N
try {
LOGGER.log(Level.INFO, "Patching the issue #249135");
String path = name.replace('.', '/').concat(".class"); // NOI18N
try (InputStream is = super.getResourceAsStream(path)) {
ClassReader cr = new ClassReader(is);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES) {
@Override
protected String getCommonSuperClass(String string, String string1) {
if ("org/xnio/nio/NioHandle".equals(string) // NOI18N
|| "org/xnio/nio/NioHandle".equals(string1)) { // NOI18N
return "java/lang/Object"; // NOI18N
}
return super.getCommonSuperClass(string, string1);
}
};
ClassNode node = new ClassNode();
cr.accept(node, 0);
for (MethodNode m : (Collection<MethodNode>) node.methods) {
if ("execute".equals(m.name) // NOI18N
&& "(Ljava/lang/Runnable;)V".equals(m.desc)) { // NOI18N
InsnList list = m.instructions;
for (ListIterator it = list.iterator(); it.hasNext(); ) {
AbstractInsnNode n = (AbstractInsnNode) it.next();
if (n instanceof MethodInsnNode) {
MethodInsnNode mn = (MethodInsnNode) n;
if ("org/xnio/nio/Log".equals(mn.owner) // NOI18N
&& "threadExiting".equals(mn.name) // NOI18N
&& "()Ljava/util/concurrent/RejectedExecutionException;".equals(mn.desc)) { // NOI18N
if (it.hasNext()) {
AbstractInsnNode possibleThrow = (AbstractInsnNode) it.next();
if (possibleThrow.getOpcode() == Opcodes.ATHROW) {
it.set(new InsnNode(Opcodes.POP));
break;
}
}
}
}
}
break;
}
}
node.accept(cw);
byte[] newBytecode = cw.toByteArray();
return super.defineClass(name, newBytecode, 0, newBytecode.length);
}
} catch (Exception ex) {
// just fallback to original behavior
LOGGER.log(Level.INFO, null, ex);
}
}
return super.findClass(name);
}
}
public synchronized WildFlyClassLoader getWildFlyClassLoader(InstanceProperties ip) {
WildFlyClassLoader cl = classLoaderCache.get(ip);
if (cl == null) {
DeploymentFactory factory = factoryCache.get(ip);
if (factory != null && factory.getClass().getClassLoader() instanceof WildFlyClassLoader) {
cl = (WildFlyClassLoader) factory.getClass().getClassLoader();
}
if (cl == null) {
cl = createWildFlyClassLoader(ip.getProperty(WildflyPluginProperties.PROPERTY_ROOT_DIR));
}
classLoaderCache.put(ip, cl);
}
return cl;
}
public static WildFlyClassLoader createWildFlyClassLoader(String serverRoot) {
try {
String sep = File.separator;
List<URL> urlList = new ArrayList<>(20);
File org = new File(serverRoot, WildflyPluginUtils.getModulesBase(serverRoot) + "org");
addUrl(urlList, org, "dom4j" + sep + "main", Pattern.compile("dom4j-.*.jar"));
if (urlList.isEmpty()) {
LOGGER.log(Level.INFO, "No dom4j.jar availabale on classpath"); // NOI18N
}
File jboss = new File(org, "jboss");
File wildfly = new File(org, "wildfly");
File as = new File(jboss, "as");
final File jbossModules = new File(serverRoot, "jboss-modules.jar");
if(jbossModules.exists()) {
urlList.add(Utilities.toURI(jbossModules).toURL());
}
final File jbossClient = new File(serverRoot, "bin" + sep + "client" + sep + "jboss-client.jar");
if(jbossClient.exists()) {
urlList.add(Utilities.toURI(jbossClient).toURL());
}
addUrl(urlList, jboss, "dmr" + sep + "main", Pattern.compile("jboss-dmr-.*.jar"));
addUrl(urlList, jboss, "logging" + sep + "main", Pattern.compile("jboss-logging-.*.jar"));
addUrl(urlList, jboss, "marshalling" + sep + "main", Pattern.compile("jboss-marshalling-.*.jar"));
addUrl(urlList, jboss, "marshalling" + sep + "river" + sep + "main", Pattern.compile("jboss-marshalling-river-.*.jar"));
addUrl(urlList, jboss, "remoting" + sep + "main", Pattern.compile("jboss-remoting-.*.jar"));
addUrl(urlList, jboss, "sasl" + sep + "main", Pattern.compile("jboss-sasl-.*.jar"));
addUrl(urlList, jboss, "threads" + sep + "main", Pattern.compile("jboss-threads-.*.jar"));
addUrl(urlList, jboss, "xnio" + sep + "main", Pattern.compile("xnio-api-.*.jar"));
addUrl(urlList, jboss, "xnio" + sep + "nio" + sep + "main", Pattern.compile("xnio-nio-.*.jar"));
addUrl(urlList, as, "controller" + sep + "main", Pattern.compile("wildfly-controller-.*.jar"));
addUrl(urlList, as, "controller" + sep + "main", Pattern.compile("jboss-as-controller-.*.jar"));
addUrl(urlList, as, "controller-client" + sep + "main", Pattern.compile("jboss-as-controller-client-.*.jar"));
addUrl(urlList, as, "controller-client" + sep + "main", Pattern.compile("wildfly-controller-client-.*.jar"));
addUrl(urlList, as, "protocol" + sep + "main", Pattern.compile("wildfly-protocol-.*.jar"));
addUrl(urlList, as, "protocol" + sep + "main", Pattern.compile("jboss-as-protocol-.*.jar"));
//CLI GUI
// addUrl(urlList, jboss, "aesh" + sep + "main", Pattern.compile("aesh-.*.jar"));
// addUrl(urlList, jboss, "staxmapper" + sep + "main", Pattern.compile("staxmapper-.*.jar"));
// addUrl(urlList, wildfly, "security" + sep + "manager" + sep + "main", Pattern.compile("wildfly-security-manager-.*.jar"));
// addUrl(urlList, jboss, "remoting-jmx" + sep + "main", Pattern.compile("remoting-jmx-.*.jar"));
// addUrl(urlList, jboss, "vfs" + sep + "main", Pattern.compile("jboss-vfs-.*.jar"));
// addUrl(urlList, org, "picketbox" + sep + "main", Pattern.compile("picketbox-.*.jar"));
// addUrl(urlList, as, "cli" + sep + "main", Pattern.compile("wildfly-cli-.*.jar"));
File serverPath = new File(serverRoot);
Version version = WildflyPluginUtils.getServerVersion(serverPath);
if (WildflyPluginUtils.WILDFLY_10_0_0.compareToIgnoreUpdate(version) >= 0) {
addUrl(urlList, wildfly, "common" + sep + "main", Pattern.compile("wildfly-common-.*.jar"));
}
boolean shouldPatchXnio = WildflyPluginUtils.WILDFLY_8_0_0.compareToIgnoreUpdate(version) <= 0;
WildFlyClassLoader loader = new WildFlyClassLoader(urlList.toArray(new URL[] {}),
WildflyDeploymentFactory.class.getClassLoader(), shouldPatchXnio);
return loader;
} catch (Exception e) {
LOGGER.log(Level.WARNING, null, e);
}
return null;
}
private static void addUrl(List<URL> result, File root, String path, final Pattern pattern) {
File folder = new File(root, path);
if(folder.exists() && folder.isDirectory()) {
File[] children = folder.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return pattern.matcher(name).matches();
}
});
if (children != null) {
for (File child : children) {
try {
result.add(child.toURI().toURL());
LOGGER.log(Level.INFO, "Adding {0} to the classpath", child.getAbsolutePath());
} catch (MalformedURLException ex) {
LOGGER.log(Level.INFO, null, ex);
}
}
}
}
}
@Override
public boolean handlesURI(String uri) {
if (uri != null && uri.startsWith(URI_PREFIX)) {
return true;
}
return false;
}
@Override
public DeploymentManager getDeploymentManager(String uri, String uname, String passwd) throws DeploymentManagerCreationException {
if (!handlesURI(uri)) {
throw new DeploymentManagerCreationException(NbBundle.getMessage(WildflyDeploymentFactory.class, "MSG_INVALID_URI", uri)); // NOI18N
}
synchronized (WildflyDeploymentFactory.class) {
InstanceProperties ip = InstanceProperties.getInstanceProperties(uri);
if (ip != null) {
WildflyDeploymentManager dm = managerCache.get(ip);
if (dm != null) {
return dm;
}
}
try {
DeploymentFactory df = getFactory(uri);
if (df == null) {
throw new DeploymentManagerCreationException(NbBundle.getMessage(WildflyDeploymentFactory.class, "MSG_ERROR_CREATING_DM", uri)); // NOI18N
}
String jbURI = uri;
try {
int index1 = uri.indexOf('#'); // NOI18N
int index2 = uri.indexOf('&'); // NOI18N
int index = Math.min(index1, index2);
jbURI = uri.substring(0, index); // NOI18N
} catch (Exception e) {
LOGGER.log(Level.INFO, null, e);
}
// see #228619
// The default host where the DM is connecting is based on
// serverHost parameter if it is null it uses InetAddress.getLocalHost()
// which is however based on hostname. If hostname is not mapped
// to localhost (the interface where the JB is running) we get
// an excpetion
if (jbURI.endsWith("as7")) { // NOI18N
jbURI = jbURI + "&serverHost=" // NOI18N
+ (ip != null ? ip.getProperty(WildflyPluginProperties.PROPERTY_HOST) : "localhost"); // NOI18N
}
WildflyDeploymentManager dm = new WildflyDeploymentManager(df, uri, jbURI, uname, passwd);
if (ip != null) {
managerCache.put(ip, dm);
}
return dm;
} catch (NoClassDefFoundError e) {
DeploymentManagerCreationException dmce = new DeploymentManagerCreationException("Classpath is incomplete"); // NOI18N
dmce.initCause(e);
throw dmce;
}
}
}
@Override
public DeploymentManager getDisconnectedDeploymentManager(String uri) throws DeploymentManagerCreationException {
if (!handlesURI(uri)) {
throw new DeploymentManagerCreationException(NbBundle.getMessage(WildflyDeploymentFactory.class, "MSG_INVALID_URI", uri)); // NOI18N
}
try {
InstanceProperties ip = InstanceProperties.getInstanceProperties(uri);
if (ip == null) {
// null ip either means that the instance is not registered, or that this is the disconnected URL
if (!DISCONNECTED_URI.equals(uri)) {
throw new DeploymentManagerCreationException("JBoss instance " + uri + " is not registered in the IDE."); // NOI18N
}
}
if (ip != null) {
String root = ip.getProperty(WildflyPluginProperties.PROPERTY_ROOT_DIR);
if (root == null || !new File(root).isDirectory()) {
throw new DeploymentManagerCreationException("Non existent server root " + root); // NOI18N
}
String server = ip.getProperty(WildflyPluginProperties.PROPERTY_SERVER_DIR);
if (server == null || !new File(server).isDirectory()) {
throw new DeploymentManagerCreationException("Non existent domain root " + server); // NOI18N
}
}
return new WildflyDeploymentManager(null, uri, null, null, null);
} catch (NoClassDefFoundError e) {
DeploymentManagerCreationException dmce = new DeploymentManagerCreationException("Classpath is incomplete"); // NOI18N
dmce.initCause(e);
throw dmce;
}
}
@Override
public String getProductVersion() {
return NbBundle.getMessage (WildflyDeploymentFactory.class, "LBL_JBossFactoryVersion");
}
@Override
public String getDisplayName() {
return NbBundle.getMessage(WildflyDeploymentFactory.class, "WILDFLY_SERVER_NAME"); // NOI18N
}
private DeploymentFactory getFactory(String instanceURL) {
DeploymentFactory jbossFactory = null;
try {
InstanceProperties ip = InstanceProperties.getInstanceProperties(instanceURL);
synchronized (WildflyDeploymentFactory.class) {
if (ip != null) {
jbossFactory = (DeploymentFactory) factoryCache.get(ip);
}
if (jbossFactory == null) {
jbossFactory = this;
if (ip != null) {
factoryCache.put(ip, jbossFactory);
}
}
}
} catch (Exception e) {
LOGGER.log(Level.INFO, null, e);
}
return jbossFactory;
}
}