/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source 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, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Sam */ package com.caucho.j2ee.deployserver; import com.caucho.config.ConfigException; import com.caucho.j2ee.deployclient.ProgressObjectImpl; import com.caucho.j2ee.deployclient.TargetImpl; import com.caucho.j2ee.deployclient.TargetModuleIDImpl; import com.caucho.jmx.Jmx; import com.caucho.loader.EnvironmentLocal; import com.caucho.management.server.*; import com.caucho.util.L10N; import com.caucho.vfs.Path; import com.caucho.vfs.Vfs; import com.caucho.vfs.WriteStream; import javax.enterprise.deploy.spi.Target; import javax.enterprise.deploy.spi.TargetModuleID; import javax.enterprise.deploy.spi.exceptions.TargetException; import javax.enterprise.deploy.spi.status.ProgressObject; import javax.management.*; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Set; import java.util.TreeSet; import java.util.logging.Level; import java.util.logging.Logger; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; public class DeploymentService { private static final L10N L = new L10N(DeploymentService.class); private static final Logger log = Logger.getLogger(DeploymentService.class.getName()); private static final EnvironmentLocal<DeploymentService> _local = new EnvironmentLocal<DeploymentService>(); public static DeploymentService getDeploymentService() { synchronized (_local) { DeploymentService deploymentService = _local.get(); if (deploymentService == null) { deploymentService = new DeploymentService(); _local.set(deploymentService); } return deploymentService; } } private DeploymentService() { } public TargetImpl[] getTargets() throws IllegalStateException { MBeanServer mbeanServer = Jmx.getMBeanServer(); ArrayList<String> hosts = new ArrayList<String>(); try { Set<ObjectName> objectNames = mbeanServer.queryNames(new ObjectName("resin:type=EarDeploy,*"), null); for (ObjectName objectName : objectNames) { String host = objectName.getKeyProperty("Host"); if (! hosts.contains(host)) hosts.add(host); } } catch (MalformedObjectNameException e) { if (log.isLoggable(Level.WARNING)) log.log(Level.WARNING, e.toString(), e); } try { Set<ObjectName> objectNames = mbeanServer.queryNames(new ObjectName("resin:type=WebAppDeploy,*"), null); for (ObjectName objectName : objectNames) { String host = objectName.getKeyProperty("Host"); if (! hosts.contains(host)) hosts.add(host); } } catch (MalformedObjectNameException e) { if (log.isLoggable(Level.WARNING)) log.log(Level.WARNING, e.toString(), e); } try { Set<ObjectName> objectNames = mbeanServer.queryNames(new ObjectName("resin:type=ResourceDeploy,*"), null); for (ObjectName objectName : objectNames) { String host = objectName.getKeyProperty("Host"); if (! hosts.contains(host)) hosts.add(host); } } catch (MalformedObjectNameException e) { if (log.isLoggable(Level.WARNING)) log.log(Level.WARNING, e.toString(), e); } TargetImpl[] targets = new TargetImpl[hosts.size()]; for (int i = 0; i < hosts.size(); i++) { String host = hosts.get(i); String description = "Virtual Host: " + host; targets[i] = new TargetImpl(host, description); } return targets; } public TargetModuleID []getAvailableModules(String type) throws TargetException, IllegalStateException { return new TargetModuleID[] {}; } private String getExceptionMessage(Throwable exception) { if (exception == null) return ""; else if (exception instanceof ConfigException) return exception.getMessage(); else return exception.toString(); } public ProgressObject distribute(TargetImpl []hostTargets, InputStream archiveIs, DeploymentPlan plan) throws IllegalStateException { ProgressObjectImpl progress; if (hostTargets == null || hostTargets.length != 1) { String msg = L.l("jsr88 can only support single-host deploy"); log.warning(msg); progress = new ProgressObjectImpl(new TargetModuleID[0]); progress.failed(msg); return progress; } String hostName = hostTargets[0].getName(); String name = plan.getName(); String moduleID; ArchiveDeployMXBean mxbean = null; try { if ("ear".equals(plan.getArchiveType())) { moduleID = "resin:name=" + name + ",type=EApp,Host=" + hostName; String jmxName = "resin:type=EarDeploy,Host=" + hostName + ",*"; mxbean = loadArchiveMXBean(jmxName); } else if ("war".equals(plan.getArchiveType())) { moduleID = "resin:name=/" + name + ",type=WebApp,Host=" + hostName; String jmxName = "resin:type=WebAppDeploy,Host=" + hostName + ",*"; mxbean = loadArchiveMXBean(jmxName); } else if ("rar".equals(plan.getArchiveType())) { moduleID = "resin:name=" + name + ",type=Resource,Host=" + hostName; String jmxName = "resin:type=ResourceDeploy,Host=" + hostName + ",*"; mxbean = loadArchiveMXBean(jmxName); } else throw new UnsupportedOperationException(plan.getArchiveType()); } catch (Exception e) { throw new RuntimeException(e); } boolean failed = false; StringBuilder message = new StringBuilder(); Path archivePath = null; Throwable exception = null; TargetImpl childTarget = new TargetImpl(moduleID, ""); TargetModuleIDImpl childModuleID = new TargetModuleIDImpl(childTarget, moduleID); if (mxbean != null) { try { Path deployPath = Vfs.lookup(mxbean.getArchivePath(name)); deployPath.getParent().mkdirs(); if (archivePath == null) { createArchive(deployPath, plan, archiveIs); archivePath = deployPath; } else { WriteStream deployStream = deployPath.openWrite(); try { deployStream.writeFile(archivePath); } finally { deployStream.close(); } } mxbean.update(); exception = mxbean.getConfigException(name); } catch (Exception e) { if (log.isLoggable(Level.INFO)) log.log(Level.INFO, e.toString(), e); exception = e; } if (exception != null) { failed = true; describe(message, childModuleID, false, getExceptionMessage(exception)); /* if (mxbean != null) { try { mxbean.undeploy(moduleID); } catch (Throwable t) { log.log(Level.FINE, t.toString(), t); } } */ } else { if ("ear".equals(plan.getArchiveType())) { try { EAppMXBean eApp = (EAppMXBean) Jmx.find(moduleID); if (eApp != null) childTarget.setClientRefs(eApp.getClientRefs()); } catch (Exception e) { log.log(Level.FINEST, e.toString(), e); } } describe(message, childModuleID, true); } } else { failed = true; log.warning(L.l("jsr88 cannot deploy '{0}', can't find deployment resource (e.g. web-app-deploy, ear-deploy).", moduleID)); } TargetModuleID []targetModuleIDs = new TargetModuleID[] { childModuleID }; progress = new ProgressObjectImpl(targetModuleIDs); if (failed) progress.failed(message.toString()); else progress.completed(message.toString()); return progress; } private ArchiveDeployMXBean loadArchiveMXBean(String pattern) { try { ObjectName objectName = null; for (ObjectName subName : Jmx.getMBeanServer().queryNames(new ObjectName(pattern), null)) { if (objectName == null) objectName = subName; } if (objectName != null) return (ArchiveDeployMXBean) Jmx.find(objectName); } catch (Exception e) { log.log(Level.FINE, e.toString(), e); } return null; } private ArchiveDeployMXBean getMXBean(ObjectName targetName) throws MalformedObjectNameException { String type = targetName.getKeyProperty("type"); String host = targetName.getKeyProperty("Host"); if (type.equals("EApp")) { String archiveName = "resin:type=EarDeploy,Host=" + host + ",*"; return loadArchiveMXBean(archiveName); } else return null; } private void createArchive(Path archivePath, DeploymentPlan plan, InputStream archiveIs) throws IOException { if (log.isLoggable(Level.FINER)) log.log(Level.FINER, L.l("jsr88 deploying archive {0}", archivePath)); Path originalPath = archivePath; String earFileName = archivePath.getTail(); if (earFileName.endsWith(".ear")) { // Uses ".__caucho_ear" to avoid premature expansion. String s = earFileName; s = s.substring(0, s.length() - 3) + "__caucho_ear"; archivePath = archivePath.getParent().lookup(s); if (log.isLoggable(Level.FINER)) log.log(Level.FINER, L.l("jsr88 creating temp archive {0}", archivePath)); } else { earFileName = null; } WriteStream archiveStream = null; ZipInputStream zipInputStream = null; ZipOutputStream zipOutputStream = null; try { archiveStream = archivePath.openWrite(); zipOutputStream = new ZipOutputStream(archiveStream); zipInputStream = new ZipInputStream(archiveIs); ZipEntry zipEntry = zipInputStream.getNextEntry(); TreeSet<String> entryNames = new TreeSet<String>(); int copyCount = 0; while (zipEntry != null) { if (log.isLoggable(Level.FINEST)) log.log(Level.FINEST, L.l("jsr88 copying entry {0}", zipEntry)); entryNames.add(zipEntry.getName()); zipOutputStream.putNextEntry(zipEntry); try { for (int ch = zipInputStream.read(); ch != -1; ch = zipInputStream.read()) zipOutputStream.write(ch); } catch (IOException e) { // XXX: unexpected end of ZLIB input stream. log.log(Level.WARNING, L.l("exception copying entry {0}", zipEntry), e); } zipEntry = zipInputStream.getNextEntry(); copyCount++; } if (log.isLoggable(Level.FINER)) log.log(Level.FINER, L.l("copied {0} entries", copyCount)); if (archiveIs.read() != -1) { if (log.isLoggable(Level.FINE)) log.log(Level.FINE, L.l("unexpected data at end of archive")); while (archiveIs.read() != -1) {} } int fileCount = 0; for (DeploymentPlan.PlanFile file : plan.getFileList()) { String zipEntryName = file.getPath(); if (zipEntryName.startsWith("/")) zipEntryName = zipEntryName.substring(1); if (log.isLoggable(Level.FINEST)) log.log(Level.FINEST, L.l("jsr88 plan file {0} output to {1}", file, zipEntryName)); if (entryNames.contains(zipEntryName)) log.log(Level.WARNING, L.l("plan file {0} overwrites existing file", zipEntryName)); entryNames.add(zipEntryName); zipEntry = new ZipEntry(zipEntryName); zipOutputStream.putNextEntry(zipEntry); file.writeToStream(zipOutputStream); fileCount++; } if (log.isLoggable(Level.FINER)) log.log(Level.FINER, L.l("created {0} entries from plan", fileCount)); zipInputStream.close(); zipInputStream = null; zipOutputStream.close(); zipOutputStream = null; archiveStream.close(); archiveStream = null; } finally { if (zipInputStream != null) { try { zipInputStream.close(); } catch (Exception ex) { log.log(Level.FINER, ex.toString(), ex); } } if (zipOutputStream != null) { try { zipOutputStream.close(); } catch (Exception ex) { log.log(Level.FINER, ex.toString(), ex); } } if (archiveStream != null) { try { archiveStream.close(); } catch (Exception ex) { log.log(Level.FINER, ex.toString(), ex); } } if (earFileName != null) { if (log.isLoggable(Level.FINER)) log.log(Level.FINER, L.l("jsr88 renaming temp archive {0} to {1}", archivePath, originalPath)); // Renames ".__caucho_ear" to ".ear" to allow expansion. archivePath.renameTo(originalPath); } } } public ProgressObject start(TargetModuleID[] ids) { ProgressObjectImpl progress = new ProgressObjectImpl(ids); boolean failed = false; StringBuilder message = new StringBuilder(); for (TargetModuleID targetModuleID : ids) { if (log.isLoggable(Level.FINE)) log.log(Level.FINE, L.l("jsr88 starting {0}", targetModuleID.getModuleID())); Throwable exception = null; DeployControllerMXBean mxbean = null; try { ObjectName objectName = new ObjectName(targetModuleID.getModuleID()); mxbean = (DeployControllerMXBean) Jmx.find(objectName); if (mxbean != null) mxbean.start(); else { log.finer("Jsr88[] " + objectName + " is an unknown module"); failed = true; } } catch (Exception t) { log.log(Level.INFO, t.toString(), t); // XXX: need to handle depending on type exception = t; } /* if (exception == null && mxbean != null) { // XXX: temp for types exception = null; } */ if (exception != null) { failed = true; describe(message, targetModuleID, false, getExceptionMessage(exception)); } else describe(message, targetModuleID, true); } if (failed) progress.failed(L.l("start {0}", message)); else progress.completed(L.l("start {0}", message)); return progress; } public ProgressObject stop(TargetModuleID[] ids) { ProgressObjectImpl progress = new ProgressObjectImpl(ids); boolean failed = false; StringBuilder message = new StringBuilder(); for (TargetModuleID targetModuleID : ids) { if (log.isLoggable(Level.FINE)) log.log(Level.FINE, L.l("jsr88 stopping {0}", targetModuleID.getModuleID())); Throwable exception = null; DeployControllerMXBean mxbean = null; try { ObjectName objectName = new ObjectName(targetModuleID.getModuleID()); mxbean = (DeployControllerMXBean) Jmx.find(objectName); if (mxbean != null) mxbean.stop(); else { log.finer("Jsr88[] " + objectName + " is an unknown module"); failed = true; } } catch (Exception t) { log.log(Level.INFO, t.toString(), t); // XXX: need to handle depending on type exception = t; } /* if (exception == null && mxbean != null) { // XXX: temp for types exception = null; } */ if (exception != null) { failed = true; describe(message, targetModuleID, false, getExceptionMessage(exception)); } else describe(message, targetModuleID, true); } if (failed) progress.failed(L.l("stop {0}", message)); else progress.completed(L.l("stop {0}", message)); return progress; } public ProgressObject undeploy(TargetModuleID []ids) throws IllegalStateException { ProgressObjectImpl progress = new ProgressObjectImpl(ids); boolean failed = false; StringBuilder message = new StringBuilder(); for (TargetModuleID targetModuleID : ids) { if (log.isLoggable(Level.FINE)) log.log(Level.FINE, L.l("undeploying {0}", targetModuleID.getModuleID())); ArchiveDeployMXBean mxbean = null; Throwable exception = null; try { ObjectName objectName = new ObjectName(targetModuleID.getModuleID()); mxbean = getMXBean(objectName); if (mxbean != null) mxbean.undeploy(objectName.getKeyProperty("name")); } catch (Throwable t) { log.log(Level.INFO, t.toString(), t); exception = t; } if (exception != null) { failed = true; describe(message, targetModuleID, false, getExceptionMessage(exception)); } else describe(message, targetModuleID, true); } if (failed) progress.failed(L.l("undeploy {0}", message)); else progress.completed(L.l("undeploy {0}", message)); return progress; } private void describe(StringBuilder builder, TargetModuleID targetModuleID, boolean success) { describe(builder, targetModuleID, success, null); } private void describe(StringBuilder builder, TargetModuleID targetModuleID, boolean success, String message) { if (builder.length() > 0) builder.append(", "); if (success) builder.append(L.l("successful for target {0} module {1}", targetModuleID.getTarget().getName(), targetModuleID.getModuleID())); else { builder.append(L.l("failed for target {0} module {1}", targetModuleID.getTarget().getName(), targetModuleID.getModuleID())); } if (message != null) { builder.append(" '"); builder.append(message); builder.append("'"); } } }