/* * JBoss, Home of Professional Open Source * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors * as indicated by the @authors tag. All rights reserved. * 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. */ package org.jboss.arquillian.container.was.wlp_remote_8_5; import java.io.File; import java.io.IOException; import java.net.URLEncoder; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.HttpVersion; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.fluent.Executor; import org.apache.http.client.fluent.Request; import org.apache.http.entity.ContentType; import org.jboss.arquillian.container.spi.client.container.DeploymentException; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; /** * * This is a wrapper around the WebSphere Liberty JMX Rest API. * * @author <a href="mailto:tayres@gmail.com">Tony Ayres</a> * */ public class WLPRestClient { private static final String className = WLPRestClient.class.getName(); private static Logger log = Logger.getLogger(className); private WLPRemoteContainerConfiguration configuration; private static final String IBMJMX_CONNECTOR_REST = "/IBMJMXConnectorREST"; private static final String FILE_ENDPOINT = IBMJMX_CONNECTOR_REST + "/file/"; private static final String MBEANS_ENDPOINT = IBMJMX_CONNECTOR_REST + "/mbeans/"; private static final String UTF_8 = "UTF-8"; private static final String STARTED = "STARTED"; private final Executor executor; public WLPRestClient(WLPRemoteContainerConfiguration configuration) { this.configuration = configuration; executor = Executor.newInstance().auth(new HttpHost(configuration.getHostName()), configuration.getUsername(), configuration.getPassword()); } /** * Uses the rest api to upload an application binary to the dropins folder * of WLP to allow the server automatically deploy it. * * @param archive * @throws ClientProtocolException * @throws IOException */ public void deploy(File archive) throws ClientProtocolException, IOException { if (log.isLoggable(Level.FINER)) { log.entering(className, "deploy"); } String deployPath = String.format("${wlp.user.dir}/servers/%s/dropins/%s", configuration.getServerName(), archive.getName()); String serverRestEndpoint = String.format("https://%s:%d%s%s", configuration.getHostName(), configuration.getHttpsPort(), FILE_ENDPOINT, URLEncoder.encode(deployPath, UTF_8)); HttpResponse result = executor.execute( Request.Post(serverRestEndpoint).useExpectContinue().version(HttpVersion.HTTP_1_1) .bodyFile(archive, ContentType.DEFAULT_BINARY)).returnResponse(); if (log.isLoggable(Level.FINE)) { log.fine("While deploying file " + archive.getName() + ", server returned response: " + result.getStatusLine().getStatusCode()); } if (!isSuccessful(result)) { throw new ClientProtocolException("Could not deploy application to server, server returned response: " + result); } if (log.isLoggable(Level.FINER)) { log.exiting(className, "deploy"); } } /** * Deletes the specified application from the servers dropins directory. WLP * will detect this and then undeploy it. * * @param String * - applicationName * @throws ClientProtocolException * @throws IOException */ public void undeploy(String applicationName) throws ClientProtocolException, IOException { if (log.isLoggable(Level.FINER)) { log.entering(className, "undeploy"); } String deployPath = String.format("${wlp.user.dir}/servers/%s/dropins/%s", configuration.getServerName(), applicationName); String serverRestEndpoint = String.format("https://%s:%d%s%s", configuration.getHostName(), configuration.getHttpsPort(), FILE_ENDPOINT, URLEncoder.encode(deployPath, UTF_8)); HttpResponse result = executor.execute( Request.Delete(serverRestEndpoint).useExpectContinue().version(HttpVersion.HTTP_1_1)).returnResponse(); if (isSuccessful(result)) { log.fine("File " + applicationName + " was deleted"); } else { throw new ClientProtocolException("Unable to undeploy application " + applicationName + ", server returned response: " + result.getStatusLine()); } if (log.isLoggable(Level.FINER)) { log.exiting(className, "undeploy", result); } } /** * Calls the rest api to determine if the application server is up and * running. * * @return boolean - true if the server is running */ public boolean isServerUp() throws ClientProtocolException, IOException { if (log.isLoggable(Level.FINER)) { log.entering(className, "isServerUp"); } String hostName = String.format("https://%s:%d%s", configuration.getHostName(), configuration.getHttpsPort(), IBMJMX_CONNECTOR_REST); HttpResponse result = executor.execute(Request.Get(hostName)).returnResponse(); if (!isSuccessful(result)) { throw new ClientProtocolException("Could not successfully connect to REST endpoint, server returned response: " + result); } if (log.isLoggable(Level.FINER)) { log.exiting(className, "isServerUp"); } return isSuccessful(result); } /** * Queries the rest api for the servers name. * * @return the name of the running server e.g. defaultServer * @throws IOException * @throws ClientProtocolException */ public String getServerName() throws ClientProtocolException, IOException { if (log.isLoggable(Level.FINER)) { log.entering(className, "getServerName"); } String restEndpoint = String.format("https://%s:%d%sWebSphere:feature=kernel,name=ServerInfo/attributes/Name", configuration.getHostName(), configuration.getHttpsPort(), MBEANS_ENDPOINT); String jsonResponse = executor.execute(Request.Get(restEndpoint)).returnContent().asString(); String serverName = parseJsonResponse(jsonResponse); if (log.isLoggable(Level.FINER)) { log.exiting(className, "isServerUp"); } return serverName; } /** * Checks the application State using the WLP rest api. * * @param applicationName * @return true if the application is in STARTED state * @throws DeploymentException */ public boolean isApplicationStarted(String applicationName) { if (log.isLoggable(Level.FINER)) { log.entering(className, "isApplicationStarted"); } String restEndpoint = String.format( "https://%s:%d%sWebSphere:service=com.ibm.websphere.application.ApplicationMBean,name=%s/attributes/State", configuration.getHostName(), configuration.getHttpsPort(), MBEANS_ENDPOINT, applicationName); String status = ""; try { String jsonResponse = executor.execute(Request.Get(restEndpoint)).returnContent().asString(); status = parseJsonResponse(jsonResponse); } catch (ClientProtocolException e) { // This exception is expected if the application hasn't been // deployed yet as its MBean won't exist. // We expect this and can continue, set status to error. log.finest("Expected error occurred while checking if application " + applicationName + " is already started, app may not have been deployed yet. Ok to continue. " + e); status = "error"; } catch (IOException e) { log.severe("IOException occurred while checking if application " + applicationName + " is already started " + e); status = "error"; } boolean applicationState; if (STARTED.equals(status)) { applicationState = true; } else { applicationState = false; } if (log.isLoggable(Level.FINER)) { log.exiting(className, "isApplicationStarted", applicationState); } return applicationState; } /* * Get a value from a json response. */ private String parseJsonResponse(String jsonString) { ObjectMapper mapper = new ObjectMapper(); String value = ""; try { Map result = mapper.readValue(jsonString.getBytes(), Map.class); value = (String) result.get("value"); } catch (JsonParseException e) { log.severe("Error parsing Json response " + e); } catch (JsonMappingException e) { log.severe("Error mapping Json response " + e); } catch (IOException e) { log.severe("IOException while parsing Json response " + e); } return value; } /** * Tests if a HttpResponse contains a 2xx response code * * @param response * @return true if the response code is a 2xx code. */ private boolean isSuccessful(HttpResponse response) { if (response.getStatusLine().getStatusCode() >= HttpStatus.SC_OK && response.getStatusLine().getStatusCode() <= HttpStatus.SC_NO_CONTENT) { return true; } return false; } }