/* * JBoss, Home of Professional Open Source * Copyright 2011, Red Hat Middleware LLC, and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of 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. */ /** * * @author <a href="http://community.jboss.org/people/LightGuard">Jason Porter</a> */ package org.jboss.arquillian.container.glassfish.remote_3_1; import java.io.File; import java.io.StringReader; import javax.ws.rs.core.MediaType; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.WebResource; import com.sun.jersey.multipart.FormDataMultiPart; import com.sun.jersey.multipart.file.FileDataBodyPart; import org.jboss.arquillian.spi.client.container.DeployableContainer; import org.jboss.arquillian.spi.client.container.DeploymentException; import org.jboss.arquillian.spi.client.container.LifecycleException; import org.jboss.arquillian.spi.client.protocol.ProtocolDescription; import org.jboss.arquillian.spi.client.protocol.metadata.HTTPContext; import org.jboss.arquillian.spi.client.protocol.metadata.ProtocolMetaData; import org.jboss.arquillian.spi.client.protocol.metadata.Servlet; import org.jboss.shrinkwrap.api.Archive; import org.jboss.shrinkwrap.api.exporter.ZipExporter; import org.jboss.shrinkwrap.descriptor.api.Descriptor; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; /** * Glassfish v3.1 remote container using REST deployment. * * @author <a href="http://community.jboss.org/people/LightGuard">Jason Porter</a> */ @SuppressWarnings({"HardcodedFileSeparator"}) public class GlassFishRestDeployableContainer implements DeployableContainer<GlassFishRestConfiguration> { private static final String APPLICATION = "/applications/application"; private static final String LIST_SUB_COMPONENTS = "/applications/application/list-sub-components?id="; private static final String SUCCESS = "SUCCESS"; private String adminBaseUrl; private String applicationBaseUrl; private String deploymentName; private GlassFishRestConfiguration configuration; public Class<GlassFishRestConfiguration> getConfigurationClass() { return GlassFishRestConfiguration.class; } public void setup(GlassFishRestConfiguration configuration) { if (configuration == null) { throw new IllegalArgumentException("configuration must not be null"); } this.configuration = configuration; final StringBuilder adminUrlBuilder = new StringBuilder(); if (this.configuration.isRemoteServerAdminHttps()) { adminUrlBuilder.append("https://"); } else { adminUrlBuilder.append("http://"); } adminUrlBuilder.append(this.configuration.getRemoteServerAddress()).append(":") .append(this.configuration.getRemoteServerAdminPort()).append("/management/domain"); this.adminBaseUrl = adminUrlBuilder.toString(); final StringBuilder applicationUrlBuilder = new StringBuilder(); if (this.configuration.isRemoteServerHttps()) { applicationUrlBuilder.append("https://"); } else { applicationUrlBuilder.append("http://"); } applicationUrlBuilder.append(this.configuration.getRemoteServerAddress()).append(":") .append(this.configuration.getRemoteServerHttpPort()).append("/"); this.applicationBaseUrl = applicationUrlBuilder.toString(); } public void start() throws LifecycleException { final String xmlResponse = prepareClient().get(String.class); try { if (!isCallSuccessful(xmlResponse)) { throw new LifecycleException("Server is not running"); } } catch (XPathExpressionException e) { throw new LifecycleException("Error verifying the sever is running", e); } } public void stop() throws LifecycleException { //To change body of implemented methods use File | Settings | File Templates. } public ProtocolDescription getDefaultProtocol() { return new ProtocolDescription("Servlet 3.0"); } public ProtocolMetaData deploy(Archive<?> archive) throws DeploymentException { if (archive == null) { throw new IllegalArgumentException("archive must not be null"); } final String archiveName = archive.getName(); try { // Export to a file so we can send it over the wire final File archiveFile = new File(new File(System.getProperty("java.io.tmpdir")), archiveName); archive.as(ZipExporter.class).exportZip(archiveFile, true); // Build up the POST form to send to Glassfish final FormDataMultiPart form = new FormDataMultiPart(); form.getBodyParts().add(new FileDataBodyPart("id", archiveFile)); form.field("contextroot", archiveName.substring(0, archiveName.lastIndexOf(".")), MediaType.TEXT_PLAIN_TYPE); deploymentName = archiveName.substring(0, archiveName.lastIndexOf(".")); form.field("name", deploymentName, MediaType.TEXT_PLAIN_TYPE); final String xmlResponse = prepareClient(APPLICATION).type(MediaType.MULTIPART_FORM_DATA_TYPE) .post(String.class, form); try { if (!isCallSuccessful(xmlResponse)) { throw new DeploymentException(getMessage(xmlResponse)); } } catch (XPathExpressionException e) { throw new DeploymentException("Error finding exit code or message", e); } // Call has been successful, now we need another call to get the list of servlets final String subComponentsResponse = prepareClient(LIST_SUB_COMPONENTS + this.deploymentName).get(String.class); return this.parseForProtocolMetaData(subComponentsResponse); } catch (XPathExpressionException e) { throw new DeploymentException("Error in creating / deploying archive", e); } } public void undeploy(Archive<?> archive) throws DeploymentException { final String xmlResponse = prepareClient(APPLICATION + "/" + this.deploymentName).delete(String.class); try { if (!isCallSuccessful(xmlResponse)) { throw new DeploymentException(getMessage(xmlResponse)); } } catch (XPathExpressionException e) { throw new DeploymentException("Error finding exit code or message", e); } } public void deploy(Descriptor descriptor) throws DeploymentException { throw new UnsupportedOperationException("Not implemented"); } public void undeploy(Descriptor descriptor) throws DeploymentException { throw new UnsupportedOperationException("Not implemented"); } /** * Basic REST call preparation * * @return the resource builder to execute */ private WebResource.Builder prepareClient() { return prepareClient(""); } /** * Basic REST call preparation, with the additional resource url appended * * @param additionalResourceUrl url portion past the base to use * @return the resource builder to execute */ private WebResource.Builder prepareClient(String additionalResourceUrl) { final Client client = Client.create(); return client.resource(this.adminBaseUrl + additionalResourceUrl).accept(MediaType.APPLICATION_XML_TYPE); } /** * Looks for a successful exit code given the response of the call * * @param xmlResponse XML response from the REST call * @return true if call was successful, false otherwise * @throws XPathExpressionException if the xpath query could not be executed */ private boolean isCallSuccessful(String xmlResponse) throws XPathExpressionException { final XPath xpath = XPathFactory.newInstance().newXPath(); final String exitCode = xpath.evaluate("/map/entry[@key = 'exit_code']/@value", new InputSource(new StringReader(xmlResponse))); return !(exitCode == null || !SUCCESS.equals(exitCode)); } /** * Finds the message from the response. * * @param xmlResponse XML response from the REST call * @return true if call was successful, false otherwise * @throws XPathExpressionException if the xpath query could not be executed */ private String getMessage(String xmlResponse) throws XPathExpressionException { final XPath xpath = XPathFactory.newInstance().newXPath(); return xpath.evaluate("/map/entry[@key = 'message']/@value", new InputSource(new StringReader(xmlResponse))); } private ProtocolMetaData parseForProtocolMetaData(String xmlResponse) throws XPathExpressionException { final ProtocolMetaData protocolMetaData = new ProtocolMetaData(); final HTTPContext httpContext = new HTTPContext(this.configuration.getRemoteServerAddress(), this.configuration.getRemoteServerHttpPort()); final XPath xpath = XPathFactory.newInstance().newXPath(); NodeList servlets = (NodeList) xpath.evaluate("/map/entry[@key = 'properties']/map/entry[@value = 'Servlet']", new InputSource(new StringReader(xmlResponse)), XPathConstants.NODESET); for (int i = 0; i < servlets.getLength(); i++) { httpContext.add(new Servlet(servlets.item(i).getAttributes().getNamedItem("key").getNodeValue(), this.deploymentName)); } protocolMetaData.addContext(httpContext); return protocolMetaData; } }