/* * JBoss, Home of Professional Open Source. * Copyright 2012, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ /** * */ package org.jboss.as.domain.controller.operations.deployment; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ARCHIVE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.BYTES; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CONTENT; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.HASH; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.INPUT_STREAM_INDEX; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.URL; import static org.jboss.as.server.controller.resources.DeploymentAttributes.CONTENT_ARCHIVE; import static org.jboss.as.server.controller.resources.DeploymentAttributes.CONTENT_HASH; import static org.jboss.as.server.controller.resources.DeploymentAttributes.EXPLODED_CONTENT; import static org.jboss.as.server.controller.resources.DeploymentAttributes.REMOVED_PATHS; import static org.jboss.as.server.controller.resources.DeploymentAttributes.TARGET_PATH; import static org.jboss.as.server.controller.resources.DeploymentAttributes.UPDATED_PATHS; import static org.jboss.as.server.deployment.DeploymentHandlerUtil.getContentItem; import static org.jboss.as.server.deployment.DeploymentHandlerUtils.hasValidContentAdditionParameterDefined; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.List; import org.jboss.as.controller.OperationContext; import org.jboss.as.controller.OperationFailedException; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.operations.CompositeOperationAwareTransformer; import org.jboss.as.controller.operations.DomainOperationTransformer; import org.jboss.as.controller.operations.OperationAttachments; import org.jboss.as.controller.registry.Resource; import org.jboss.as.domain.controller.logging.DomainControllerLogger; import org.jboss.as.repository.ContentRepository; import org.jboss.as.repository.ExplodedContent; import org.jboss.as.repository.ExplodedContentException; import org.jboss.as.repository.HostFileRepository; import org.jboss.as.server.controller.resources.DeploymentAttributes; import org.jboss.as.server.deployment.ModelContentReference; import org.jboss.dmr.ModelNode; import static org.jboss.as.server.controller.resources.DeploymentAttributes.DEPLOYMENT_CONTENT_PATH; import static org.jboss.as.server.controller.resources.DeploymentAttributes.EMPTY; import static org.jboss.as.server.controller.resources.DeploymentAttributes.OVERWRITE; /** * Utility method for storing deployment content. * * @author Brian Stansberry (c) 2011 Red Hat Inc. */ class DeploymentUploadUtil { private DeploymentUploadUtil() { } /** * Store the deployment contents and attach a "transformed" slave operation to the operation context. * * @param context the operation context * @param operation the original operation * @param contentRepository the content repository * @return the hash of the uploaded deployment content * @throws IOException * @throws OperationFailedException */ public static byte[] storeContentAndTransformOperation(OperationContext context, ModelNode operation, ContentRepository contentRepository) throws IOException, OperationFailedException { if (!operation.hasDefined(CONTENT)) { throw createFailureException(DomainControllerLogger.ROOT_LOGGER.invalidContentDeclaration()); } final ModelNode content = operation.get(CONTENT).get(0); if (content.hasDefined(HASH)) { // This should be handled as part of the OSH throw createFailureException(DomainControllerLogger.ROOT_LOGGER.invalidContentDeclaration()); } final byte[] hash = storeDeploymentContent(context, operation, contentRepository); // Clear the contents and update with the hash final ModelNode slave = operation.clone(); slave.get(CONTENT).setEmptyList().add().get(HASH).set(hash); // Add the domain op transformer List<DomainOperationTransformer> transformers = context.getAttachment(OperationAttachments.SLAVE_SERVER_OPERATION_TRANSFORMERS); if (transformers == null) { context.attach(OperationAttachments.SLAVE_SERVER_OPERATION_TRANSFORMERS, transformers = new ArrayList<>()); } transformers.add(new CompositeOperationAwareTransformer(slave)); return hash; } public static byte[] storeEmptyContentAndTransformOperation(OperationContext context, ModelNode operation, ContentRepository contentRepository) throws OperationFailedException { final byte[] hash = storeEmptyDeploymentContent(context, contentRepository); // Clear the contents and update with the hash final ModelNode slave = operation.clone(); slave.remove(EMPTY.getName()); ModelNode contentItemNode = new ModelNode(); contentItemNode.get(CONTENT_HASH.getName()).set(hash); contentItemNode.get(CONTENT_ARCHIVE.getName()).set(false); slave.get(CONTENT).setEmptyList().add(contentItemNode); // Add the domain op transformer List<DomainOperationTransformer> transformers = context.getAttachment(OperationAttachments.SLAVE_SERVER_OPERATION_TRANSFORMERS); if (transformers == null) { context.attach(OperationAttachments.SLAVE_SERVER_OPERATION_TRANSFORMERS, transformers = new ArrayList<>()); } transformers.add(new CompositeOperationAwareTransformer(slave)); return hash; } /** * Explode the deployment contents and attach a "transformed" slave operation to the operation context. * * @param context the operation context * @param operation the original operation * @param contentRepository the content repository * @return the hash of the uploaded deployment content * @throws IOException * @throws OperationFailedException */ public static byte[] explodeContentAndTransformOperation(OperationContext context, ModelNode operation, ContentRepository contentRepository) throws OperationFailedException, ExplodedContentException { final Resource deploymentResource = context.readResource(PathAddress.EMPTY_ADDRESS); ModelNode contentItem = getContentItem(deploymentResource); ModelNode explodedPath = DEPLOYMENT_CONTENT_PATH.resolveModelAttribute(context, operation); byte[] oldHash = CONTENT_HASH.resolveModelAttribute(context, contentItem).asBytes(); final byte[] hash; if (explodedPath.isDefined()) { hash = contentRepository.explodeSubContent(oldHash, explodedPath.asString()); } else { hash = contentRepository.explodeContent(oldHash); } // Clear the contents and update with the hash final ModelNode slave = operation.clone(); ModelNode addedContent = new ModelNode().setEmptyObject(); addedContent.get(HASH).set(hash); addedContent.get(TARGET_PATH.getName()).set("./"); slave.get(CONTENT).setEmptyList().add(addedContent); // Add the domain op transformer List<DomainOperationTransformer> transformers = context.getAttachment(OperationAttachments.SLAVE_SERVER_OPERATION_TRANSFORMERS); if (transformers == null) { context.attach(OperationAttachments.SLAVE_SERVER_OPERATION_TRANSFORMERS, transformers = new ArrayList<>()); } transformers.add(new CompositeOperationAwareTransformer(slave)); return hash; } /** * Add contents to the deployment and attach a "transformed" slave operation to the operation context. * * @param context the operation context * @param operation the original operation * @param contentRepository the content repository * @return the hash of the uploaded deployment content * @throws IOException * @throws OperationFailedException */ public static byte[] addContentToExplodedAndTransformOperation(OperationContext context, ModelNode operation, ContentRepository contentRepository) throws OperationFailedException, ExplodedContentException { final Resource deploymentResource = context.readResource(PathAddress.EMPTY_ADDRESS); ModelNode contentItem = getContentItem(deploymentResource); byte[] oldHash = CONTENT_HASH.resolveModelAttribute(context, contentItem).asBytes(); List<ModelNode> contents = EXPLODED_CONTENT.resolveModelAttribute(context, operation).asList(); final List<ExplodedContent> addedFiles = new ArrayList<>(contents.size()); final ModelNode slave = operation.clone(); ModelNode slaveAddedfiles = slave.get(UPDATED_PATHS.getName()).setEmptyList(); for(ModelNode content : contents) { InputStream in; if(hasValidContentAdditionParameterDefined(content)) { in = getInputStream(context, content); } else { in = null; } String path = TARGET_PATH.resolveModelAttribute(context, content).asString(); addedFiles.add(new ExplodedContent(path, in)); slaveAddedfiles.add(path); } final boolean overwrite = OVERWRITE.resolveModelAttribute(context, operation).asBoolean(true); final byte[] hash = contentRepository.addContentToExploded(oldHash, addedFiles, overwrite); // Clear the contents and update with the hash ModelNode addedContent = new ModelNode().setEmptyObject(); addedContent.get(HASH).set(hash); addedContent.get(TARGET_PATH.getName()).set("."); slave.get(CONTENT).setEmptyList().add(addedContent); // Add the domain op transformer List<DomainOperationTransformer> transformers = context.getAttachment(OperationAttachments.SLAVE_SERVER_OPERATION_TRANSFORMERS); if (transformers == null) { context.attach(OperationAttachments.SLAVE_SERVER_OPERATION_TRANSFORMERS, transformers = new ArrayList<>()); } transformers.add(new CompositeOperationAwareTransformer(slave)); return hash; } /** * Remove contents from the deployment and attach a "transformed" slave operation to the operation context. * * @param context the operation context * @param operation the original operation * @param contentRepository the content repository * @return the hash of the uploaded deployment content * @throws IOException * @throws OperationFailedException */ public static byte[] removeContentFromExplodedAndTransformOperation(OperationContext context, ModelNode operation, ContentRepository contentRepository) throws OperationFailedException, ExplodedContentException { final Resource deploymentResource = context.readResource(PathAddress.EMPTY_ADDRESS); ModelNode contentItemNode = getContentItem(deploymentResource); final byte[] oldHash = CONTENT_HASH.resolveModelAttribute(context, contentItemNode).asBytes(); final List<String> paths = REMOVED_PATHS.unwrap(context, operation); final byte[] hash = contentRepository.removeContentFromExploded(oldHash, paths); // Clear the contents and update with the hash final ModelNode slave = operation.clone(); slave.get(CONTENT).setEmptyList().add().get(HASH).set(hash); slave.get(CONTENT).add().get(ARCHIVE).set(false); // Add the domain op transformer List<DomainOperationTransformer> transformers = context.getAttachment(OperationAttachments.SLAVE_SERVER_OPERATION_TRANSFORMERS); if (transformers == null) { context.attach(OperationAttachments.SLAVE_SERVER_OPERATION_TRANSFORMERS, transformers = new ArrayList<>()); } transformers.add(new CompositeOperationAwareTransformer(slave)); return hash; } /** * Synchronize the required files to a slave HC from the master DC if this is required. * @param fileRepository the HostFileRepository of the HC. * @param contentRepository the ContentRepository of the HC. * @param backup inidcates if this is a DC backup HC. * @param oldHash the hash of the deployment to be replaced. * @return true if the content should be pulled by the slave HC - false otherwise. */ public static byte[] synchronizeSlaveHostController(ModelNode operation, final PathAddress address, HostFileRepository fileRepository, ContentRepository contentRepository, boolean backup, byte[] oldHash) { ModelNode operationContentItem = operation.get(DeploymentAttributes.CONTENT_RESOURCE.getName()).get(0); byte[] newHash = operationContentItem.require(CONTENT_HASH.getName()).asBytes(); if (needRemoteContent(fileRepository, contentRepository, backup, oldHash)) { // backup DC needs to pull the content fileRepository.getDeploymentFiles(ModelContentReference.fromModelAddress(address, newHash)); } return newHash; } private static boolean needRemoteContent(HostFileRepository fileRepository, ContentRepository contentRepository, boolean backup, byte[] oldHash) { return fileRepository != null && (backup || (contentRepository != null && contentRepository.hasContent(oldHash))); } private static byte[] storeDeploymentContent(OperationContext context, ModelNode operation, ContentRepository contentRepository) throws IOException, OperationFailedException { try (InputStream in = getContents(context, operation)){ return contentRepository.addContent(in); } } private static byte[] storeEmptyDeploymentContent(OperationContext context, ContentRepository contentRepository) throws OperationFailedException { try { return contentRepository.addContent(null); } catch (IOException e) { throw createFailureException(e.toString()); } } private static InputStream getContents(OperationContext context, ModelNode operation) throws OperationFailedException { if(! operation.hasDefined(CONTENT)) { throw createFailureException(DomainControllerLogger.ROOT_LOGGER.invalidContentDeclaration()); } return getInputStream(context, operation.require(CONTENT).get(0)); } private static InputStream getInputStream(OperationContext context, ModelNode content) throws OperationFailedException { InputStream in = null; String message = ""; if (content.hasDefined(INPUT_STREAM_INDEX)) { int streamIndex = content.get(INPUT_STREAM_INDEX).asInt(); if (streamIndex > context.getAttachmentStreamCount() - 1) { message = DomainControllerLogger.ROOT_LOGGER.invalidValue(INPUT_STREAM_INDEX, streamIndex, (context.getAttachmentStreamCount() - 1)); throw createFailureException(message); } message = DomainControllerLogger.ROOT_LOGGER.nullStream(streamIndex); in = context.getAttachmentStream(streamIndex); } else if (content.hasDefined(BYTES)) { in = new ByteArrayInputStream(content.get(BYTES).asBytes()); message = DomainControllerLogger.ROOT_LOGGER.invalidByteStream(); } else if (content.hasDefined(URL)) { final String urlSpec = content.get(URL).asString(); try { message = DomainControllerLogger.ROOT_LOGGER.invalidUrlStream(); in = new URL(urlSpec).openStream(); } catch (MalformedURLException e) { throw createFailureException(message); } catch (IOException e) { throw createFailureException(message); } } if (in == null) { throw createFailureException(message); } return in; } private static OperationFailedException createFailureException(String msg) { return new OperationFailedException(msg); } }