-
Notifications
You must be signed in to change notification settings - Fork 35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ARQ-1729 Add ability to deploy application to WAS Liberty server.xml file #16
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,6 +30,13 @@ | |
import javax.management.remote.JMXConnector; | ||
import javax.management.remote.JMXConnectorFactory; | ||
import javax.management.remote.JMXServiceURL; | ||
import javax.xml.parsers.DocumentBuilder; | ||
import javax.xml.parsers.DocumentBuilderFactory; | ||
import javax.xml.transform.OutputKeys; | ||
import javax.xml.transform.Transformer; | ||
import javax.xml.transform.TransformerFactory; | ||
import javax.xml.transform.dom.DOMSource; | ||
import javax.xml.transform.stream.StreamResult; | ||
|
||
import org.jboss.arquillian.container.spi.client.container.DeployableContainer; | ||
import org.jboss.arquillian.container.spi.client.container.DeploymentException; | ||
|
@@ -41,6 +48,10 @@ | |
import org.jboss.shrinkwrap.api.Archive; | ||
import org.jboss.shrinkwrap.api.exporter.ZipExporter; | ||
import org.jboss.shrinkwrap.descriptor.api.Descriptor; | ||
import org.w3c.dom.Document; | ||
import org.w3c.dom.Element; | ||
import org.w3c.dom.Node; | ||
import org.w3c.dom.NodeList; | ||
|
||
import com.sun.tools.attach.VirtualMachine; | ||
import com.sun.tools.attach.VirtualMachineDescriptor; | ||
|
@@ -228,7 +239,7 @@ private String findVirtualMachineIdByName(String serverName) { | |
|
||
return null; | ||
} | ||
|
||
public ProtocolMetaData deploy(final Archive<?> archive) throws DeploymentException | ||
{ | ||
if (log.isLoggable(Level.FINER)) { | ||
|
@@ -237,47 +248,108 @@ public ProtocolMetaData deploy(final Archive<?> archive) throws DeploymentExcept | |
log.finer("Archive provided to deploy method: " + archive.toString(true)); | ||
} | ||
|
||
// Save the archive to disk so it can be loaded by the container. | ||
String dropInDir = getDropInDirectory(); | ||
File exportedArchiveLocation = new File(dropInDir, archive.getName()); | ||
archive.as(ZipExporter.class).exportTo(exportedArchiveLocation, true); | ||
|
||
// Wait until the application is deployed and available | ||
waitForApplicationTargetState(createDeploymentName(archive.getName()), true, containerConfiguration.getAppDeployTimeout()); | ||
|
||
// Return metadata on how to contact the deployed application | ||
ProtocolMetaData metaData = new ProtocolMetaData(); | ||
HTTPContext httpContext = new HTTPContext("localhost", containerConfiguration.getHttpPort()); | ||
httpContext.add(new Servlet("ArquillianServletRunner", createDeploymentName(archive.getName()))); | ||
metaData.addContext(httpContext); | ||
String archiveName = archive.getName(); | ||
String archiveType = createDeploymentType(archiveName); | ||
String deployName = createDeploymentName(archiveName); | ||
|
||
if (log.isLoggable(Level.FINER)) { | ||
log.exiting(className, "deploy"); | ||
try { | ||
// If the deployment is to server.xml, then update server.xml with the application information | ||
if (containerConfiguration.isDeployTypeXML()) { | ||
// Throw error if deployment type is not ear, war, or eba | ||
if (!archiveType.equalsIgnoreCase("ear") && !archiveType.equalsIgnoreCase("war") && !archiveType.equalsIgnoreCase("eba")) | ||
throw new DeploymentException("Invalid archive type: " + archiveType + ". Valid archive types are ear, war, and eba."); | ||
|
||
// Save the archive to disk so it can be loaded by the container. | ||
String appDir = getAppDirectory(); | ||
File exportedArchiveLocation = new File(appDir, archiveName); | ||
archive.as(ZipExporter.class).exportTo(exportedArchiveLocation, true); | ||
|
||
// Read server.xml file into Memory | ||
Document document = readServerXML(); | ||
|
||
// Add the archive as appropriate to the server.xml file | ||
addApplication(document, deployName, archiveName, archiveType); | ||
|
||
// Update server.xml on file system | ||
writeServerXML(document); | ||
} | ||
// Otherwise put the application in the dropins directory | ||
else { | ||
// Save the archive to disk so it can be loaded by the container. | ||
String dropInDir = getDropInDirectory(); | ||
File exportedArchiveLocation = new File(dropInDir, archiveName); | ||
archive.as(ZipExporter.class).exportTo(exportedArchiveLocation, true); | ||
} | ||
|
||
// Wait until the application is deployed and available | ||
waitForApplicationTargetState(deployName, true, containerConfiguration.getAppDeployTimeout()); | ||
|
||
// Return metadata on how to contact the deployed application | ||
ProtocolMetaData metaData = new ProtocolMetaData(); | ||
HTTPContext httpContext = new HTTPContext("localhost", containerConfiguration.getHttpPort()); | ||
httpContext.add(new Servlet("ArquillianServletRunner", deployName)); | ||
metaData.addContext(httpContext); | ||
|
||
if (log.isLoggable(Level.FINER)) { | ||
log.exiting(className, "deploy"); | ||
} | ||
|
||
return metaData; | ||
} catch (Exception e) { | ||
throw new DeploymentException("Exception while deploying application.", e); | ||
} | ||
|
||
return metaData; | ||
} | ||
|
||
public void undeploy(final Archive<?> archive) throws DeploymentException | ||
{ | ||
if (log.isLoggable(Level.FINER)) { | ||
log.entering(className, "undeploy"); | ||
} | ||
|
||
// Remove archive from the dropIn directory, which causes undeploy | ||
String dropInDir = getDropInDirectory(); | ||
File exportedArchiveLocation = new File(dropInDir, archive.getName()); | ||
if (!exportedArchiveLocation.delete()) | ||
throw new DeploymentException("Unable to delete archive from dropIn directory"); | ||
|
||
String archiveName = archive.getName(); | ||
String deployName = createDeploymentName(archiveName); | ||
|
||
try { | ||
//If deploy type is xml, then remove the application from the xml file, which causes undeploy | ||
if (containerConfiguration.isDeployTypeXML()) { | ||
// Read the server.xml file into Memory | ||
Document document = readServerXML(); | ||
|
||
// Wait until the application is undeployed | ||
waitForApplicationTargetState(createDeploymentName(archive.getName()), false, containerConfiguration.getAppUndeployTimeout()); | ||
// Remove the archive from the server.xml file | ||
removeApplication(document); | ||
|
||
if (log.isLoggable(Level.FINER)) { | ||
// Update server.xml on file system | ||
writeServerXML(document); | ||
|
||
// Wait until the application is undeployed | ||
waitForApplicationTargetState(deployName, false, containerConfiguration.getAppUndeployTimeout()); | ||
|
||
// Remove archive from the apps directory | ||
String appDir = getAppDirectory(); | ||
File exportedArchiveLocation = new File(appDir, archiveName); | ||
if (!exportedArchiveLocation.delete()) | ||
throw new DeploymentException("Unable to delete archive from apps directory"); | ||
} | ||
else { | ||
// Remove archive from the dropIn directory, which causes undeploy | ||
String dropInDir = getDropInDirectory(); | ||
File exportedArchiveLocation = new File(dropInDir, archiveName); | ||
if (!exportedArchiveLocation.delete()) | ||
throw new DeploymentException("Unable to delete archive from dropIn directory"); | ||
|
||
// Wait until the application is undeployed | ||
waitForApplicationTargetState(deployName, false, containerConfiguration.getAppUndeployTimeout()); | ||
} | ||
|
||
} catch (Exception e) { | ||
throw new DeploymentException("Exception while undeploying application.", e); | ||
} | ||
|
||
if (log.isLoggable(Level.FINER)) { | ||
log.exiting(className, "undeploy"); | ||
} | ||
} | ||
} | ||
|
||
private String getDropInDirectory() { | ||
String dropInDir = containerConfiguration.getWlpHome() + "/usr/servers/" + | ||
containerConfiguration.getServerName() + "/dropins"; | ||
|
@@ -286,11 +358,91 @@ private String getDropInDirectory() { | |
return dropInDir; | ||
} | ||
|
||
private String getAppDirectory() { | ||
String appDir = containerConfiguration.getWlpHome() + "/usr/servers/" + | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might want to use File.separator instead of /, even tho / will currently work on both win and *nix systems. |
||
containerConfiguration.getServerName() + "/apps"; | ||
if (log.isLoggable(Level.FINER)) | ||
log.finer("appDir: " + appDir); | ||
return appDir; | ||
} | ||
|
||
private String getServerXML() { | ||
String serverXML = containerConfiguration.getWlpHome() + "/usr/servers/" + | ||
containerConfiguration.getServerName() + "/server.xml"; | ||
if (log.isLoggable(Level.FINER)) | ||
log.finer("server.xml: " + serverXML); | ||
return serverXML; | ||
} | ||
|
||
private String createDeploymentName(String archiveName) | ||
{ | ||
return archiveName.substring(0, archiveName.lastIndexOf(".")); | ||
} | ||
|
||
private String createDeploymentType(String archiveName) | ||
{ | ||
return archiveName.substring(archiveName.lastIndexOf(".")+1); | ||
} | ||
|
||
private Document readServerXML() throws DeploymentException { | ||
try { | ||
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); | ||
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); | ||
return documentBuilder.parse(new File(getServerXML())); | ||
} catch (Exception e) { | ||
throw new DeploymentException("Exception while reading server.xml file.", e); | ||
} | ||
} | ||
|
||
private void writeServerXML(Document doc) throws DeploymentException { | ||
try { | ||
TransformerFactory tf = TransformerFactory.newInstance(); | ||
Transformer tr = tf.newTransformer(); | ||
tr.setOutputProperty(OutputKeys.INDENT, "yes"); | ||
DOMSource source = new DOMSource(doc); | ||
StreamResult res = new StreamResult(new File(getServerXML())); | ||
tr.transform(source, res); | ||
} catch (Exception e) { | ||
throw new DeploymentException("Exception wile writing server.xml file.", e); | ||
} | ||
} | ||
|
||
private Element createApplication(Document doc, String deploymentName, String archiveName, String type) { | ||
|
||
//create new Application | ||
Element application = doc.createElement("application"); | ||
application.setAttribute("id", deploymentName); | ||
application.setAttribute("location", archiveName); | ||
application.setAttribute("name", deploymentName); | ||
application.setAttribute("type", type); | ||
|
||
//create shared library | ||
if (containerConfiguration.getSharedLib() != null) { | ||
Element sharedLib = doc.createElement("classloader"); | ||
sharedLib.setAttribute("commonLibraryRef", containerConfiguration.getSharedLib()); | ||
application.appendChild(sharedLib); | ||
} | ||
|
||
return application; | ||
} | ||
|
||
private void addApplication(Document doc, String deployName, String archiveName, String type) { | ||
NodeList rootList = doc.getElementsByTagName("server"); | ||
Node root = rootList.item(0); | ||
root.appendChild(createApplication(doc, deployName, archiveName, type)); | ||
} | ||
|
||
private void removeApplication(Document doc) { | ||
Node server = doc.getElementsByTagName("server").item(0); | ||
NodeList serverlist = server.getChildNodes(); | ||
for (int i=0; serverlist.getLength() > i; i++) { | ||
Node node = serverlist.item(i); | ||
if (node.getNodeName().equals("application")) { | ||
node.getParentNode().removeChild(node); | ||
} | ||
} | ||
} | ||
|
||
private void waitForApplicationTargetState(String applicationName, boolean targetState, int timeout) throws DeploymentException { | ||
if (log.isLoggable(Level.FINER)) { | ||
log.entering(className, "waitForMBeanTargetState"); | ||
|
@@ -311,7 +463,7 @@ private void waitForApplicationTargetState(String applicationName, boolean targe | |
try { | ||
int timeleft = timeout * 1000; | ||
while(mbsc.isRegistered(appMBean) != targetState) { | ||
Thread.sleep(100); | ||
Thread.sleep(1000); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we really want to wait a whole second for each attempt? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm going to backout this particular line and increase the appDeployTimeout instead. This will have the effect of waiting longer, but not sleeping longer. |
||
if (timeleft <= 0) | ||
throw new DeploymentException("Timeout while waiting for ApplicationMBean to reach targetState"); | ||
timeleft -= 100; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,6 +36,8 @@ public class WLPManagedContainerConfiguration implements | |
private int serverStartTimeout = 30; | ||
private int appDeployTimeout = 2; | ||
private int appUndeployTimeout = 2; | ||
private String sharedLib = null; | ||
private String deployType = "dropins"; | ||
|
||
private boolean allowConnectingToRunningServer = Boolean.parseBoolean( | ||
System.getProperty("org.jboss.arquillian.container.was.wlp_managed_8_5.allowConnectingToRunningServer", "false")); | ||
|
@@ -56,6 +58,19 @@ public void validate() throws ConfigurationException { | |
// Validate httpPort | ||
if (httpPort > 65535 || httpPort <= 0) | ||
throw new ConfigurationException("httpPort provided is not valid: " + httpPort); | ||
|
||
// Validate deployType | ||
if (!deployType.equalsIgnoreCase("xml") && !deployType.equalsIgnoreCase("dropins")) | ||
throw new ConfigurationException("deployType provided is not valid: " + deployType + ". deployType should be xml or dropins."); | ||
|
||
//Validate sharedLib | ||
if (!sharedLib.equals(null)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If sharedLib is not set via xml to 'something', this cause NullPointerException There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm going to add a commit to change this to:
|
||
if (!sharedLib.isEmpty()) { | ||
if (!deployType.equalsIgnoreCase("xml")) | ||
throw new ConfigurationException("deployType must be set to xml when sharedLib is not empty"); | ||
} | ||
} | ||
|
||
} | ||
|
||
public String getWlpHome() { | ||
|
@@ -81,7 +96,22 @@ public int getHttpPort() { | |
public void setHttpPort(int httpPort) { | ||
this.httpPort = httpPort; | ||
} | ||
|
||
public void setSharedLib(String sharedLib) { | ||
this.sharedLib = sharedLib; | ||
} | ||
|
||
public String getSharedLib() { | ||
return sharedLib; | ||
} | ||
|
||
public void setDeployType(String deployType) { | ||
this.deployType = deployType; | ||
} | ||
|
||
public String getDeployType() { | ||
return deployType; | ||
} | ||
public boolean isAllowConnectingToRunningServer() { | ||
return allowConnectingToRunningServer; | ||
} | ||
|
@@ -117,5 +147,19 @@ public int getAppUndeployTimeout() { | |
public void setAppUndeployTimeout(int appUndeployTimeout) { | ||
this.appUndeployTimeout = appUndeployTimeout; | ||
} | ||
|
||
public boolean isDeployTypeXML() { | ||
if (deployType.equalsIgnoreCase("xml")) | ||
return true; | ||
else | ||
return false; | ||
} | ||
|
||
public boolean isDeployTypeDropins() { | ||
if (deployType.equalsIgnoreCase("dropins")) | ||
return true; | ||
else | ||
return false; | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there no API in Liberty that would allow you to dynamically resolve contextRoots for the different Servlets within a Deployment?
That would allow the user to use apis like @ArquillianResource(MySerlvet.class) to resolve the correct contextRoot within a multi module deployment.