blob: 79bed954de1f468868a98a2bc3fa34e7be35dbd1 [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.apache.openejb.assembler;
import org.apache.openejb.ClassLoaderUtil;
import org.apache.openejb.NoSuchApplicationException;
import org.apache.openejb.OpenEJBException;
import org.apache.openejb.OpenEJBRuntimeException;
import org.apache.openejb.UndeployException;
import org.apache.openejb.assembler.classic.*;
import org.apache.openejb.config.AppModule;
import org.apache.openejb.config.ConfigurationFactory;
import org.apache.openejb.config.DeploymentLoader;
import org.apache.openejb.config.DeploymentModule;
import org.apache.openejb.config.WebModule;
import org.apache.openejb.config.sys.AdditionalDeployments;
import org.apache.openejb.config.sys.Deployments;
import org.apache.openejb.config.sys.JaxbOpenejb;
import org.apache.openejb.loader.Files;
import org.apache.openejb.loader.IO;
import org.apache.openejb.loader.SystemInstance;
import org.apache.openejb.loader.provisining.ProvisioningResolver;
import org.apache.openejb.util.JavaSecurityManagers;
import org.apache.openejb.util.LogCategory;
import org.apache.openejb.util.Logger;
import javax.ejb.Lock;
import javax.ejb.Remote;
import javax.ejb.Singleton;
import javax.ejb.TransactionManagement;
import javax.enterprise.inject.Alternative;
import javax.validation.ValidationException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import static javax.ejb.LockType.READ;
import static javax.ejb.TransactionManagementType.BEAN;
import static org.apache.openejb.config.ConfigurationFactory.ADDITIONAL_DEPLOYMENTS;
import static org.apache.openejb.loader.ProvisioningUtil.realLocation;
@SuppressWarnings("EjbProhibitedPackageUsageInspection")
@Singleton(name = "openejb/Deployer")
@Lock(READ)
@Remote(Deployer.class)
@TransactionManagement(BEAN)
@Alternative
public class DeployerEjb implements Deployer {
private static final Logger LOGGER = Logger.getInstance(LogCategory.OPENEJB, DeployerEjb.class);
public static final String OPENEJB_DEPLOYER_FORCED_APP_ID_PROP = "openejb.deployer.forced.appId";
public static final String OPENEJB_DEPLOYER_HOST = "openejb.deployer.host";
public static final String OPENEJB_USE_BINARIES = "openejb.deployer.binaries.use";
public static final String OPENEJB_PATH_BINARIES = "openejb.deployer.binaries.path";
public static final String OPENEJB_VALUE_BINARIES = "openejb.deployer.binaries.value";
public static final String OPENEJB_APP_AUTODEPLOY = "openejb.app.autodeploy";
public static final ThreadLocal<Boolean> AUTO_DEPLOY = new ThreadLocal<>();
private static final File uniqueFile;
private static final boolean oldWarDeployer = "old".equalsIgnoreCase(SystemInstance.get().getOptions().get("openejb.deployer.war", "new"));
private static final String OPENEJB_DEPLOYER_SAVE_DEPLOYMENTS = "openejb.deployer.save-deployments";
private static final boolean SAVE_DEPLOYMENTS = SystemInstance.get().getOptions().get(OPENEJB_DEPLOYER_SAVE_DEPLOYMENTS, false);
static {
final String uniqueName = "OpenEJB-" + new BigInteger(128, new SecureRandom()).toString(Character.MAX_RADIX);
final String tempDir = JavaSecurityManagers.getSystemProperty("java.io.tmpdir");
File unique;
try {
unique = new File(tempDir, uniqueName).getCanonicalFile();
if (!unique.createNewFile()) {
throw new IOException("Failed to create file in temp: " + unique);
}
} catch (final IOException e) {
// same trying in work directory
unique = new File(SystemInstance.get().getBase().getDirectory(), "work");
if (unique.exists()) {
try {
unique = new File(unique, uniqueName).getCanonicalFile();
if (!unique.createNewFile()) {
throw new IOException("Failed to create file in work: " + unique);
}
} catch (final IOException e1) {
throw new OpenEJBRuntimeException(e);
}
} else {
throw new OpenEJBRuntimeException("cannot create unique file, please set java.io.tmpdir to a writable folder or create work folder", e);
}
}
uniqueFile = unique;
uniqueFile.deleteOnExit();
}
private final DeploymentLoader deploymentLoader;
private final Assembler assembler;
public DeployerEjb() {
deploymentLoader = new DeploymentLoader();
assembler = (Assembler) SystemInstance.get().getComponent(org.apache.openejb.spi.Assembler.class);
}
@Override
public String getUniqueFile() {
return uniqueFile.getAbsolutePath();
}
@Override
public Collection<AppInfo> getDeployedApps() {
return assembler.getDeployedApplications();
}
@Override
public AppInfo deploy(final String location) throws OpenEJBException {
return deploy(location, null);
}
@Override
public AppInfo deploy(final Properties properties) throws OpenEJBException {
return deploy(null, properties);
}
@Override
public AppInfo deploy(final String inLocation, Properties properties) throws OpenEJBException {
String rawLocation = inLocation;
if (rawLocation == null && properties == null) {
throw new NullPointerException("location and properties are null");
}
if (rawLocation == null) {
rawLocation = properties.getProperty(FILENAME);
}
if (properties == null) {
properties = new Properties();
}
AppModule appModule = null;
final File file;
if ("true".equalsIgnoreCase(properties.getProperty(OPENEJB_USE_BINARIES, "false"))) {
file = copyBinaries(properties);
} else {
file = new File(realLocation(rawLocation).iterator().next());
}
final boolean autoDeploy = Boolean.parseBoolean(properties.getProperty(OPENEJB_APP_AUTODEPLOY, "false"));
final String host = properties.getProperty(OPENEJB_DEPLOYER_HOST, null);
if (WebAppDeployer.Helper.isWebApp(file) && !oldWarDeployer) {
AUTO_DEPLOY.set(autoDeploy);
try {
final AppInfo appInfo = SystemInstance.get().getComponent(WebAppDeployer.class)
.deploy(host, contextRoot(properties, file.getAbsolutePath()), file);
if (appInfo != null) {
saveIfNeeded(properties, file, appInfo);
return appInfo;
}
throw new OpenEJBException("can't deploy " + file.getAbsolutePath());
} finally {
AUTO_DEPLOY.remove();
}
}
AppInfo appInfo = null;
try {
appModule = deploymentLoader.load(file, null);
// Add any alternate deployment descriptors to the modules
final Map<String, DeploymentModule> modules = new TreeMap<>();
for (final DeploymentModule module : appModule.getEjbModules()) {
modules.put(module.getModuleId(), module);
}
for (final DeploymentModule module : appModule.getClientModules()) {
modules.put(module.getModuleId(), module);
}
for (final WebModule module : appModule.getWebModules()) {
final String contextRoot = contextRoot(properties, module.getJarLocation());
if (contextRoot != null) {
module.setContextRoot(contextRoot);
module.setHost(host);
}
modules.put(module.getModuleId(), module);
}
for (final DeploymentModule module : appModule.getConnectorModules()) {
modules.put(module.getModuleId(), module);
}
for (final Map.Entry<Object, Object> entry : properties.entrySet()) {
String name = (String) entry.getKey();
if (name.startsWith(ALT_DD + "/")) {
name = name.substring(ALT_DD.length() + 1);
final DeploymentModule module;
final int slash = name.indexOf('/');
if (slash > 0) {
final String moduleId = name.substring(0, slash);
name = name.substring(slash + 1);
module = modules.get(moduleId);
} else {
module = appModule;
}
if (module != null) {
final String value = (String) entry.getValue();
final File dd = new File(value);
if (dd.canRead()) {
module.getAltDDs().put(name, dd.toURI().toURL());
} else {
module.getAltDDs().put(name, value);
}
}
}
}
final OpenEjbConfiguration configuration = new OpenEjbConfiguration();
configuration.containerSystem = new ContainerSystemInfo();
configuration.facilities = new FacilitiesInfo();
final ConfigurationFactory configurationFactory = new ConfigurationFactory(false, configuration);
appInfo = configurationFactory.configureApplication(appModule);
appInfo.autoDeploy = autoDeploy;
if (properties != null && properties.containsKey(OPENEJB_DEPLOYER_FORCED_APP_ID_PROP)) {
appInfo.appId = properties.getProperty(OPENEJB_DEPLOYER_FORCED_APP_ID_PROP);
}
if (!appInfo.webApps.isEmpty()) {
appInfo.properties.setProperty("tomcat.unpackWar", "false");
}
// create any resources and containers defined in the application itself
if (!appInfo.webApps.isEmpty()) {
appInfo.properties.setProperty("tomcat.unpackWar", "false");
}
final ClassLoader oldCl = Thread.currentThread().getContextClassLoader();
final ClassLoader appClassLoader = assembler.createAppClassLoader(appInfo);
try {
Thread.currentThread().setContextClassLoader(appClassLoader);
for (final ResourceInfo resource : configuration.facilities.resources) {
assembler.createResource(resource);
}
for (final ContainerInfo container : configuration.containerSystem.containers) {
assembler.createContainer(container);
}
} finally {
Thread.currentThread().setContextClassLoader(oldCl);
}
assembler.createApplication(appInfo, appClassLoader);
saveIfNeeded(properties, file, appInfo);
return appInfo;
} catch (final Throwable e) {
// destroy the class loader for the failed application
if (appModule != null) {
ClassLoaderUtil.destroyClassLoader(appModule.getJarLocation());
}
if (null != appInfo) {
ClassLoaderUtil.destroyClassLoader(appInfo.appId, appInfo.path);
}
LOGGER.error("Can't deploy " + inLocation, e);
if (e instanceof ValidationException) {
throw (ValidationException) e;
}
final Throwable ex;
final DeploymentExceptionManager dem = SystemInstance.get().getComponent(DeploymentExceptionManager.class);
if (dem != null) {
if (dem.hasDeploymentFailed()) {
ex = dem.getLastException();
} else {
ex = e;
}
if (appInfo != null) {
dem.clearLastException(appInfo);
}
} else {
ex = e;
}
if (ex instanceof OpenEJBException) {
if (ex.getCause() instanceof ValidationException) {
throw (ValidationException) ex.getCause();
}
throw (OpenEJBException) ex;
}
throw new OpenEJBException(ex);
}
}
private void saveIfNeeded(final Properties properties, final File file, final AppInfo appInfo) {
if ((SAVE_DEPLOYMENTS && null == properties.getProperty(OPENEJB_DEPLOYER_SAVE_DEPLOYMENTS))
|| "true".equalsIgnoreCase(properties.getProperty(OPENEJB_DEPLOYER_SAVE_DEPLOYMENTS, "false"))) {
appInfo.properties.setProperty("save-deployment","true");
saveDeployment(file, true);
}
}
private synchronized File copyBinaries(final Properties props) throws OpenEJBException {
final File dump = ProvisioningResolver.cacheFile(props.getProperty(OPENEJB_PATH_BINARIES, "dump.war"));
if (dump.exists()) {
Files.delete(dump);
final String name = dump.getName();
if (name.endsWith("ar") && name.length() > 4) {
final File exploded = new File(dump.getParentFile(), name.substring(0, name.length() - 4));
if (exploded.exists()) {
Files.delete(exploded);
}
}
}
try {
IO.copy(byte[].class.cast(props.get(OPENEJB_VALUE_BINARIES)), dump);
} catch (final IOException e) {
throw new OpenEJBException(e);
}
return dump;
}
private synchronized void saveDeployment(final File file, final boolean add) {
final Deployments deps = new Deployments();
if (file.isDirectory()) {
deps.setDir(file.getAbsolutePath());
} else {
deps.setFile(file.getAbsolutePath());
}
File config;
try {
config = SystemInstance.get().getBase().getFile(ADDITIONAL_DEPLOYMENTS, false);
} catch (final IOException e) {
config = null;
}
if (config == null || !config.getParentFile().exists()) {
LOGGER.info("Cannot save the added app because the conf folder does not exist, it will not be present on a restart");
return;
}
// dump it
OutputStream os = null;
try {
final AdditionalDeployments additionalDeployments;
if (config.exists() && config.length() > 0) {
final InputStream fis = IO.read(config);
try {
additionalDeployments = JaxbOpenejb.unmarshal(AdditionalDeployments.class, fis);
} finally {
IO.close(fis);
}
} else {
additionalDeployments = new AdditionalDeployments();
}
if (add) {
if (!additionalDeployments.getDeployments().contains(deps)) {
additionalDeployments.getDeployments().add(deps);
}
} else {
final Iterator<Deployments> it = additionalDeployments.getDeployments().iterator();
while (it.hasNext()) {
final Deployments current = it.next();
if (deps.getDir() != null && deps.getDir().equals(current.getDir())) {
it.remove();
break;
} else if (deps.getFile() != null && deps.getFile().equals(current.getFile())) {
it.remove();
break;
} else { // exploded dirs
final String jar = deps.getFile();
if (jar != null && jar.length() > 3) {
final String substring = jar.substring(0, jar.length() - 4);
if (substring.equals(current.getDir()) || substring.equals(current.getFile())) {
it.remove();
break;
}
} else {
final String jarC = current.getFile();
if (jarC != null && jarC.length() > 3) {
final String substring = jarC.substring(0, jarC.length() - 4);
if (substring.equals(deps.getDir()) || substring.equals(deps.getFile())) {
it.remove();
break;
}
}
}
}
}
}
os = IO.write(config);
JaxbOpenejb.marshal(AdditionalDeployments.class, additionalDeployments, os);
} catch (final Exception e) {
LOGGER.error("cannot save the added app, will not be present next time you'll start", e);
} finally {
IO.close(os);
}
}
@Override
public void undeploy(final String moduleId) throws UndeployException, NoSuchApplicationException {
AppInfo appInfo = assembler.getAppInfo(moduleId);
if (appInfo == null) {
appInfo = assembler.getAppInfo(realLocation(moduleId).iterator().next());
if (appInfo == null) {
appInfo = assembler.getAppInfo(new File(moduleId).getAbsolutePath());
if (appInfo == null) {
appInfo = assembler.getAppInfo(new File(realLocation(moduleId).iterator().next()).getAbsolutePath());
}
}
}
if (appInfo != null) {
try {
assembler.destroyApplication(appInfo);
} finally {
if (appInfo.properties.containsKey("save-deployment")) {
saveDeployment(new File(moduleId), false);
}
}
} else {
throw new NoSuchApplicationException(moduleId);
}
}
private String contextRoot(final Properties properties, final String jarPath) {
return properties.getProperty("webapp." + jarPath + ".context-root");
}
@Override
public void reload(final String moduleId) {
for (final AppInfo info : assembler.getDeployedApplications()) {
if (info.path.equals(moduleId)) {
reload(info);
break;
}
}
}
private void reload(final AppInfo info) {
if (info.webAppAlone) {
final WebAppDeployer component = SystemInstance.get().getComponent(WebAppDeployer.class);
if (null != component) {
component.reload(info.path);
return;
}
}
try {
assembler.destroyApplication(info);
assembler.createApplication(info);
} catch (final Exception e) {
throw new OpenEJBRuntimeException(e);
}
}
}