/**
* Copyright 2015-2016 Red Hat, Inc, and individual 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 org.wildfly.swarm.container.runtime;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
import javax.annotation.PreDestroy;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import org.jboss.as.controller.client.ModelControllerClient;
import org.jboss.dmr.ModelNode;
import org.jboss.jandex.Index;
import org.jboss.jandex.Indexer;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ArchivePath;
import org.jboss.shrinkwrap.api.Filters;
import org.jboss.shrinkwrap.api.GenericArchive;
import org.jboss.shrinkwrap.api.Node;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.exporter.ZipExporter;
import org.jboss.shrinkwrap.api.importer.ExplodedImporter;
import org.jboss.shrinkwrap.api.importer.ZipImporter;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.jboss.vfs.TempFileProvider;
import org.wildfly.swarm.bootstrap.env.ApplicationEnvironment;
import org.wildfly.swarm.bootstrap.logging.BootstrapLogger;
import org.wildfly.swarm.bootstrap.performance.Performance;
import org.wildfly.swarm.bootstrap.util.BootstrapProperties;
import org.wildfly.swarm.container.DeploymentException;
import org.wildfly.swarm.container.internal.Deployer;
import org.wildfly.swarm.container.runtime.deployments.DefaultDeploymentCreator;
import org.wildfly.swarm.container.runtime.wildfly.SwarmContentRepository;
import org.wildfly.swarm.internal.DeployerMessages;
import org.wildfly.swarm.internal.FileSystemLayout;
import org.wildfly.swarm.internal.SwarmMessages;
import org.wildfly.swarm.spi.api.ArchiveMetadataProcessor;
import org.wildfly.swarm.spi.api.ArchivePreparer;
import org.wildfly.swarm.spi.api.ArtifactLookup;
import org.wildfly.swarm.spi.api.DependenciesContainer;
import org.wildfly.swarm.spi.api.SwarmProperties;
import org.wildfly.swarm.spi.api.internal.SwarmInternalProperties;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.BLOCKING_TIMEOUT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CONTENT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ENABLED;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.HASH;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OPERATION_HEADERS;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PERSISTENT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RUNTIME_NAME;
/**
* @author Bob McWhirter
* @author Heiko Braun
* @author Ken Finnigan
*/
@ApplicationScoped
public class RuntimeDeployer implements Deployer {
//private static Logger LOG = Logger.getLogger("org.wildfly.swarm.deployer");
private static final String ALL_DEPENDENCIES_ADDED_MARKER = DependenciesContainer.ALL_DEPENDENCIES_MARKER + ".added";
@Override
public void deploy() throws DeploymentException {
Archive<?> deployment = createDefaultDeployment();
if (deployment == null) {
throw DeployerMessages.MESSAGES.unableToCreateDefaultDeployment();
} else {
deploy(deployment);
}
}
@Override
public void deploy(Collection<Path> pathsToDeploy) throws DeploymentException {
if (pathsToDeploy.isEmpty()) {
DeployerMessages.MESSAGES.noDeploymentsSpecified();
return;
}
archives(pathsToDeploy)
.forEach(e -> {
deploy(e);
});
}
protected static Stream<Archive> archives(Collection<Path> paths) {
return paths.stream()
.map(path -> {
String simpleName = path.getFileName().toString();
Archive archive = ShrinkWrap.create(JavaArchive.class, simpleName);
archive.as(ZipImporter.class).importFrom(path.toFile());
return archive;
});
}
public Archive<?> createDefaultDeployment() {
return this.defaultDeploymentCreator.createDefaultDeployment(determineDeploymentType());
}
private String determineDeploymentType() {
if (this.defaultDeploymentType == null) {
this.defaultDeploymentType = determineDeploymentTypeInternal();
System.setProperty(BootstrapProperties.DEFAULT_DEPLOYMENT_TYPE, this.defaultDeploymentType);
}
return this.defaultDeploymentType;
}
private String determineDeploymentTypeInternal() {
String artifact = System.getProperty(BootstrapProperties.APP_PATH);
if (artifact != null) {
int dotLoc = artifact.lastIndexOf('.');
if (dotLoc >= 0) {
return artifact.substring(dotLoc + 1);
}
}
artifact = System.getProperty(BootstrapProperties.APP_ARTIFACT);
if (artifact != null) {
int dotLoc = artifact.lastIndexOf('.');
if (dotLoc >= 0) {
return artifact.substring(dotLoc + 1);
}
}
// fallback to file system
FileSystemLayout fsLayout = FileSystemLayout.create();
return fsLayout.determinePackagingType();
}
public void debug(boolean debug) {
this.debug = debug;
}
@Override
public void deploy(Archive<?> deployment) throws DeploymentException {
try (AutoCloseable deploymentTimer = Performance.time("deployment: " + deployment.getName())) {
// check for "org.wildfly.swarm.allDependencies" flag
// see DependenciesContainer#addAllDependencies()
if (deployment instanceof DependenciesContainer) {
DependenciesContainer<?> depContainer = (DependenciesContainer) deployment;
if (depContainer.hasMarker(DependenciesContainer.ALL_DEPENDENCIES_MARKER)) {
if (!depContainer.hasMarker(ALL_DEPENDENCIES_ADDED_MARKER)) {
ApplicationEnvironment appEnv = ApplicationEnvironment.get();
if (ApplicationEnvironment.Mode.UBERJAR == appEnv.getMode()) {
ArtifactLookup artifactLookup = ArtifactLookup.get();
for (String gav : appEnv.getDependencies()) {
depContainer.addAsLibrary(artifactLookup.artifact(gav));
}
} else {
Set<String> paths = appEnv.resolveDependencies(Collections.emptyList());
for (String path : paths) {
final File pathFile = new File(path);
if (path.endsWith(".jar")) {
depContainer.addAsLibrary(pathFile);
} else if (pathFile.isDirectory()) {
depContainer
.merge(ShrinkWrap.create(GenericArchive.class)
.as(ExplodedImporter.class)
.importDirectory(pathFile)
.as(GenericArchive.class),
"/WEB-INF/classes",
Filters.includeAll());
}
}
}
depContainer.addMarker(ALL_DEPENDENCIES_ADDED_MARKER);
}
}
}
// 1. create a meta data index, but only if we have processors for it
if (!this.archiveMetadataProcessors.isUnsatisfied()) {
Indexer indexer = new Indexer();
Map<ArchivePath, Node> c = deployment.getContent();
try {
for (Map.Entry<ArchivePath, Node> each : c.entrySet()) {
if (each.getKey().get().endsWith(CLASS_SUFFIX)) {
indexer.index(each.getValue().getAsset().openStream());
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
Index index = indexer.complete();
// 2.1 let fractions process the meta data
for (ArchiveMetadataProcessor processor : this.archiveMetadataProcessors) {
processor.processArchive(deployment, index);
}
}
// 2. give fractions a chance to handle the deployment
for (ArchivePreparer preparer : this.archivePreparers) {
preparer.prepareArchive(deployment);
}
if (DeployerMessages.MESSAGES.isDebugEnabled()) {
DeployerMessages.MESSAGES.deploying(deployment.getName());
Map<ArchivePath, Node> ctx = deployment.getContent();
for (Map.Entry<ArchivePath, Node> each : ctx.entrySet()) {
DeployerMessages.MESSAGES.deploymentContent(each.getKey().toString());
}
}
if (BootstrapProperties.flagIsSet(SwarmProperties.EXPORT_DEPLOYMENT)) {
final File out = new File(deployment.getName());
DeployerMessages.MESSAGES.exportingDeployment(out.getAbsolutePath());
deployment.as(ZipExporter.class).exportTo(out, true);
}
byte[] hash = this.contentRepository.addContent(deployment.as(ZipExporter.class).exportAsInputStream());
final ModelNode deploymentAdd = new ModelNode();
deploymentAdd.get(OP).set(ADD);
deploymentAdd.get(OP_ADDR).set("deployment", deployment.getName());
deploymentAdd.get(RUNTIME_NAME).set(deployment.getName());
deploymentAdd.get(ENABLED).set(true);
deploymentAdd.get(PERSISTENT).set(true);
ModelNode content = deploymentAdd.get(CONTENT).add();
content.get(HASH).set(hash);
int deploymentTimeout = Integer.getInteger(SwarmProperties.DEPLOYMENT_TIMEOUT, 300);
final ModelNode opHeaders = new ModelNode();
opHeaders.get(BLOCKING_TIMEOUT).set(deploymentTimeout);
deploymentAdd.get(OPERATION_HEADERS).set(opHeaders);
BootstrapLogger.logger("org.wildfly.swarm.runtime.deployer")
.info("deploying " + deployment.getName());
System.setProperty(SwarmInternalProperties.CURRENT_DEPLOYMENT, deployment.getName());
try {
ModelNode result = client.execute(deploymentAdd);
ModelNode outcome = result.get("outcome");
if (outcome.asString().equals("success")) {
return;
}
ModelNode description = result.get("failure-description");
throw new DeploymentException(deployment, SwarmMessages.MESSAGES.deploymentFailed(description.asString()));
} catch (IOException e) {
throw SwarmMessages.MESSAGES.deploymentFailed(e, deployment);
}
} catch (Exception e) {
throw new DeploymentException(deployment, e);
}
}
@SuppressWarnings("unused")
@PreDestroy
void stop() {
}
private static final String CLASS_SUFFIX = ".class";
private String defaultDeploymentType;
@SuppressWarnings("unused")
@Inject
private ModelControllerClient client;
@SuppressWarnings("unused")
@Inject
private SwarmContentRepository contentRepository;
@SuppressWarnings("unused")
@Inject
private TempFileProvider tempFileProvider;
@SuppressWarnings("unused")
@Inject
private DefaultDeploymentCreator defaultDeploymentCreator;
@SuppressWarnings("unused")
private boolean debug = false;
@SuppressWarnings("unused")
@Inject
private Instance<ArchivePreparer> archivePreparers;
@SuppressWarnings("unused")
@Inject
private Instance<ArchiveMetadataProcessor> archiveMetadataProcessors;
}