001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Sam
028: */
029:
030: package com.caucho.j2ee.deployserver;
031:
032: import com.caucho.config.ConfigException;
033: import com.caucho.j2ee.deployclient.ProgressObjectImpl;
034: import com.caucho.j2ee.deployclient.TargetImpl;
035: import com.caucho.j2ee.deployclient.TargetModuleIDImpl;
036: import com.caucho.jmx.Jmx;
037: import com.caucho.loader.EnvironmentLocal;
038: import com.caucho.management.server.*;
039: import com.caucho.util.L10N;
040: import com.caucho.vfs.Path;
041: import com.caucho.vfs.Vfs;
042: import com.caucho.vfs.WriteStream;
043:
044: import javax.enterprise.deploy.spi.Target;
045: import javax.enterprise.deploy.spi.TargetModuleID;
046: import javax.enterprise.deploy.spi.exceptions.TargetException;
047: import javax.enterprise.deploy.spi.status.ProgressObject;
048: import javax.management.*;
049: import java.io.IOException;
050: import java.io.InputStream;
051: import java.util.ArrayList;
052: import java.util.Set;
053: import java.util.TreeSet;
054: import java.util.logging.Level;
055: import java.util.logging.Logger;
056: import java.util.zip.ZipEntry;
057: import java.util.zip.ZipInputStream;
058: import java.util.zip.ZipOutputStream;
059:
060: public class DeploymentService {
061: private static final L10N L = new L10N(DeploymentService.class);
062: private static final Logger log = Logger
063: .getLogger(DeploymentService.class.getName());
064:
065: private static final EnvironmentLocal<DeploymentService> _local = new EnvironmentLocal<DeploymentService>();
066:
067: public static DeploymentService getDeploymentService() {
068: synchronized (_local) {
069: DeploymentService deploymentService = _local.get();
070:
071: if (deploymentService == null) {
072: deploymentService = new DeploymentService();
073: _local.set(deploymentService);
074: }
075:
076: return deploymentService;
077: }
078: }
079:
080: private DeploymentService() {
081: }
082:
083: public TargetImpl[] getTargets() throws IllegalStateException {
084: MBeanServer mbeanServer = Jmx.getMBeanServer();
085: ArrayList<String> hosts = new ArrayList<String>();
086:
087: try {
088: Set<ObjectName> objectNames = mbeanServer.queryNames(
089: new ObjectName("resin:type=EarDeploy,*"), null);
090:
091: for (ObjectName objectName : objectNames) {
092: String host = objectName.getKeyProperty("Host");
093: if (!hosts.contains(host))
094: hosts.add(host);
095: }
096: } catch (MalformedObjectNameException e) {
097: if (log.isLoggable(Level.WARNING))
098: log.log(Level.WARNING, e.toString(), e);
099: }
100:
101: try {
102: Set<ObjectName> objectNames = mbeanServer.queryNames(
103: new ObjectName("resin:type=WebAppDeploy,*"), null);
104:
105: for (ObjectName objectName : objectNames) {
106: String host = objectName.getKeyProperty("Host");
107: if (!hosts.contains(host))
108: hosts.add(host);
109: }
110: } catch (MalformedObjectNameException e) {
111: if (log.isLoggable(Level.WARNING))
112: log.log(Level.WARNING, e.toString(), e);
113:
114: }
115:
116: try {
117: Set<ObjectName> objectNames = mbeanServer
118: .queryNames(new ObjectName(
119: "resin:type=ResourceDeploy,*"), null);
120:
121: for (ObjectName objectName : objectNames) {
122: String host = objectName.getKeyProperty("Host");
123: if (!hosts.contains(host))
124: hosts.add(host);
125: }
126: } catch (MalformedObjectNameException e) {
127: if (log.isLoggable(Level.WARNING))
128: log.log(Level.WARNING, e.toString(), e);
129: }
130:
131: TargetImpl[] targets = new TargetImpl[hosts.size()];
132: for (int i = 0; i < hosts.size(); i++) {
133: String host = hosts.get(i);
134: String description = "Virtual Host: " + host;
135:
136: targets[i] = new TargetImpl(host, description);
137: }
138:
139: return targets;
140: }
141:
142: public TargetModuleID[] getAvailableModules(String type)
143: throws TargetException, IllegalStateException {
144: return new TargetModuleID[] {};
145: }
146:
147: private String getExceptionMessage(Throwable exception) {
148: if (exception == null)
149: return "";
150: else if (exception instanceof ConfigException)
151: return exception.getMessage();
152: else
153: return exception.toString();
154: }
155:
156: public ProgressObject distribute(TargetImpl[] hostTargets,
157: InputStream archiveIs, DeploymentPlan plan)
158: throws IllegalStateException {
159: ProgressObjectImpl progress;
160:
161: if (hostTargets == null || hostTargets.length != 1) {
162: String msg = L
163: .l("jsr88 can only support single-host deploy");
164:
165: log.warning(msg);
166:
167: progress = new ProgressObjectImpl(new TargetModuleID[0]);
168:
169: progress.failed(msg);
170:
171: return progress;
172: }
173:
174: String hostName = hostTargets[0].getName();
175:
176: String name = plan.getName();
177: String moduleID;
178:
179: ArchiveDeployMXBean mxbean = null;
180:
181: try {
182: if ("ear".equals(plan.getArchiveType())) {
183: moduleID = "resin:name=" + name + ",type=EApp,Host="
184: + hostName;
185:
186: String jmxName = "resin:type=EarDeploy,Host="
187: + hostName + ",*";
188: mxbean = loadArchiveMXBean(jmxName);
189: } else if ("war".equals(plan.getArchiveType())) {
190: moduleID = "resin:name=/" + name + ",type=WebApp,Host="
191: + hostName;
192:
193: String jmxName = "resin:type=WebAppDeploy,Host="
194: + hostName + ",*";
195: mxbean = loadArchiveMXBean(jmxName);
196: } else if ("rar".equals(plan.getArchiveType())) {
197: moduleID = "resin:name=" + name
198: + ",type=Resource,Host=" + hostName;
199:
200: String jmxName = "resin:type=ResourceDeploy,Host="
201: + hostName + ",*";
202: mxbean = loadArchiveMXBean(jmxName);
203: } else
204: throw new UnsupportedOperationException(plan
205: .getArchiveType());
206: } catch (Exception e) {
207: throw new RuntimeException(e);
208: }
209:
210: boolean failed = false;
211: StringBuilder message = new StringBuilder();
212:
213: Path archivePath = null;
214:
215: Throwable exception = null;
216:
217: TargetImpl childTarget = new TargetImpl(moduleID, "");
218:
219: TargetModuleIDImpl childModuleID = new TargetModuleIDImpl(
220: childTarget, moduleID);
221:
222: if (mxbean != null) {
223: try {
224: Path deployPath = Vfs.lookup(mxbean
225: .getArchivePath(name));
226:
227: deployPath.getParent().mkdirs();
228:
229: if (archivePath == null) {
230: createArchive(deployPath, plan, archiveIs);
231: archivePath = deployPath;
232: } else {
233: WriteStream deployStream = deployPath.openWrite();
234:
235: try {
236: deployStream.writeFile(archivePath);
237: } finally {
238: deployStream.close();
239: }
240: }
241:
242: mxbean.update();
243:
244: exception = mxbean.getConfigException(name);
245: } catch (Exception e) {
246: if (log.isLoggable(Level.INFO))
247: log.log(Level.INFO, e.toString(), e);
248:
249: exception = e;
250: }
251:
252: if (exception != null) {
253: failed = true;
254: describe(message, childModuleID, false,
255: getExceptionMessage(exception));
256:
257: /*
258: if (mxbean != null) {
259: try {
260: mxbean.undeploy(moduleID);
261: }
262: catch (Throwable t) {
263: log.log(Level.FINE, t.toString(), t);
264: }
265: }
266: */
267: } else {
268: if ("ear".equals(plan.getArchiveType())) {
269: try {
270: EAppMXBean eApp = (EAppMXBean) Jmx
271: .find(moduleID);
272:
273: if (eApp != null)
274: childTarget.setClientRefs(eApp
275: .getClientRefs());
276: } catch (Exception e) {
277: log.log(Level.FINEST, e.toString(), e);
278: }
279: }
280:
281: describe(message, childModuleID, true);
282: }
283: } else {
284: failed = true;
285: log
286: .warning(L
287: .l(
288: "jsr88 cannot deploy '{0}', can't find deployment resource (e.g. web-app-deploy, ear-deploy).",
289: moduleID));
290: }
291:
292: TargetModuleID[] targetModuleIDs = new TargetModuleID[] { childModuleID };
293:
294: progress = new ProgressObjectImpl(targetModuleIDs);
295:
296: if (failed)
297: progress.failed(message.toString());
298: else
299: progress.completed(message.toString());
300:
301: return progress;
302: }
303:
304: private ArchiveDeployMXBean loadArchiveMXBean(String pattern) {
305: try {
306: ObjectName objectName = null;
307:
308: for (ObjectName subName : Jmx.getMBeanServer().queryNames(
309: new ObjectName(pattern), null)) {
310: if (objectName == null)
311: objectName = subName;
312: }
313:
314: if (objectName != null)
315: return (ArchiveDeployMXBean) Jmx.find(objectName);
316: } catch (Exception e) {
317: log.log(Level.FINE, e.toString(), e);
318: }
319:
320: return null;
321: }
322:
323: private ArchiveDeployMXBean getMXBean(ObjectName targetName)
324: throws MalformedObjectNameException {
325: String type = targetName.getKeyProperty("type");
326: String host = targetName.getKeyProperty("Host");
327:
328: if (type.equals("EApp")) {
329: String archiveName = "resin:type=EarDeploy,Host=" + host
330: + ",*";
331:
332: return loadArchiveMXBean(archiveName);
333: } else
334: return null;
335: }
336:
337: private void createArchive(Path archivePath, DeploymentPlan plan,
338: InputStream archiveIs) throws IOException {
339: if (log.isLoggable(Level.FINER))
340: log.log(Level.FINER, L.l("jsr88 deploying archive {0}",
341: archivePath));
342:
343: Path originalPath = archivePath;
344:
345: String earFileName = archivePath.getTail();
346:
347: if (earFileName.endsWith(".ear")) {
348: // Uses ".__caucho_ear" to avoid premature expansion.
349: String s = earFileName;
350: s = s.substring(0, s.length() - 3) + "__caucho_ear";
351: archivePath = archivePath.getParent().lookup(s);
352:
353: if (log.isLoggable(Level.FINER))
354: log
355: .log(Level.FINER, L.l(
356: "jsr88 creating temp archive {0}",
357: archivePath));
358: } else {
359: earFileName = null;
360: }
361:
362: WriteStream archiveStream = null;
363: ZipInputStream zipInputStream = null;
364: ZipOutputStream zipOutputStream = null;
365:
366: try {
367: archiveStream = archivePath.openWrite();
368: zipOutputStream = new ZipOutputStream(archiveStream);
369:
370: zipInputStream = new ZipInputStream(archiveIs);
371:
372: ZipEntry zipEntry = zipInputStream.getNextEntry();
373:
374: TreeSet<String> entryNames = new TreeSet<String>();
375:
376: int copyCount = 0;
377:
378: while (zipEntry != null) {
379: if (log.isLoggable(Level.FINEST))
380: log.log(Level.FINEST, L.l(
381: "jsr88 copying entry {0}", zipEntry));
382:
383: entryNames.add(zipEntry.getName());
384:
385: zipOutputStream.putNextEntry(zipEntry);
386:
387: try {
388: for (int ch = zipInputStream.read(); ch != -1; ch = zipInputStream
389: .read())
390: zipOutputStream.write(ch);
391: } catch (IOException e) {
392: // XXX: unexpected end of ZLIB input stream.
393: log
394: .log(Level.WARNING, L.l(
395: "exception copying entry {0}",
396: zipEntry), e);
397: }
398:
399: zipEntry = zipInputStream.getNextEntry();
400:
401: copyCount++;
402: }
403:
404: if (log.isLoggable(Level.FINER))
405: log.log(Level.FINER, L.l("copied {0} entries",
406: copyCount));
407:
408: if (archiveIs.read() != -1) {
409: if (log.isLoggable(Level.FINE))
410: log.log(Level.FINE, L
411: .l("unexpected data at end of archive"));
412:
413: while (archiveIs.read() != -1) {
414: }
415: }
416:
417: int fileCount = 0;
418:
419: for (DeploymentPlan.PlanFile file : plan.getFileList()) {
420: String zipEntryName = file.getPath();
421: if (zipEntryName.startsWith("/"))
422: zipEntryName = zipEntryName.substring(1);
423:
424: if (log.isLoggable(Level.FINEST))
425: log.log(Level.FINEST, L.l(
426: "jsr88 plan file {0} output to {1}", file,
427: zipEntryName));
428:
429: if (entryNames.contains(zipEntryName))
430: log.log(Level.WARNING, L.l(
431: "plan file {0} overwrites existing file",
432: zipEntryName));
433:
434: entryNames.add(zipEntryName);
435:
436: zipEntry = new ZipEntry(zipEntryName);
437: zipOutputStream.putNextEntry(zipEntry);
438: file.writeToStream(zipOutputStream);
439:
440: fileCount++;
441: }
442:
443: if (log.isLoggable(Level.FINER))
444: log.log(Level.FINER, L.l(
445: "created {0} entries from plan", fileCount));
446:
447: zipInputStream.close();
448: zipInputStream = null;
449:
450: zipOutputStream.close();
451: zipOutputStream = null;
452:
453: archiveStream.close();
454: archiveStream = null;
455: } finally {
456: if (zipInputStream != null) {
457: try {
458: zipInputStream.close();
459: } catch (Exception ex) {
460: log.log(Level.FINER, ex.toString(), ex);
461: }
462: }
463:
464: if (zipOutputStream != null) {
465: try {
466: zipOutputStream.close();
467: } catch (Exception ex) {
468: log.log(Level.FINER, ex.toString(), ex);
469: }
470: }
471:
472: if (archiveStream != null) {
473: try {
474: archiveStream.close();
475: } catch (Exception ex) {
476: log.log(Level.FINER, ex.toString(), ex);
477: }
478: }
479:
480: if (earFileName != null) {
481: if (log.isLoggable(Level.FINER))
482: log.log(Level.FINER, L.l(
483: "jsr88 renaming temp archive {0} to {1}",
484: archivePath, originalPath));
485:
486: // Renames ".__caucho_ear" to ".ear" to allow expansion.
487: archivePath.renameTo(originalPath);
488: }
489: }
490: }
491:
492: public ProgressObject start(TargetModuleID[] ids) {
493: ProgressObjectImpl progress = new ProgressObjectImpl(ids);
494:
495: boolean failed = false;
496: StringBuilder message = new StringBuilder();
497:
498: for (TargetModuleID targetModuleID : ids) {
499: if (log.isLoggable(Level.FINE))
500: log.log(Level.FINE, L.l("jsr88 starting {0}",
501: targetModuleID.getModuleID()));
502:
503: Throwable exception = null;
504: DeployControllerMXBean mxbean = null;
505:
506: try {
507: ObjectName objectName = new ObjectName(targetModuleID
508: .getModuleID());
509: mxbean = (DeployControllerMXBean) Jmx.find(objectName);
510: if (mxbean != null)
511: mxbean.start();
512: else {
513: log.finer("Jsr88[] " + objectName
514: + " is an unknown module");
515: failed = true;
516: }
517: } catch (Exception t) {
518: log.log(Level.INFO, t.toString(), t);
519: // XXX: need to handle depending on type
520: exception = t;
521: }
522:
523: /*
524: if (exception == null && mxbean != null) {
525: // XXX: temp for types
526: exception = null;
527: }
528: */
529:
530: if (exception != null) {
531: failed = true;
532: describe(message, targetModuleID, false,
533: getExceptionMessage(exception));
534: } else
535: describe(message, targetModuleID, true);
536: }
537:
538: if (failed)
539: progress.failed(L.l("start {0}", message));
540: else
541: progress.completed(L.l("start {0}", message));
542:
543: return progress;
544: }
545:
546: public ProgressObject stop(TargetModuleID[] ids) {
547: ProgressObjectImpl progress = new ProgressObjectImpl(ids);
548:
549: boolean failed = false;
550: StringBuilder message = new StringBuilder();
551:
552: for (TargetModuleID targetModuleID : ids) {
553: if (log.isLoggable(Level.FINE))
554: log.log(Level.FINE, L.l("jsr88 stopping {0}",
555: targetModuleID.getModuleID()));
556:
557: Throwable exception = null;
558: DeployControllerMXBean mxbean = null;
559:
560: try {
561: ObjectName objectName = new ObjectName(targetModuleID
562: .getModuleID());
563: mxbean = (DeployControllerMXBean) Jmx.find(objectName);
564: if (mxbean != null)
565: mxbean.stop();
566: else {
567: log.finer("Jsr88[] " + objectName
568: + " is an unknown module");
569: failed = true;
570: }
571: } catch (Exception t) {
572: log.log(Level.INFO, t.toString(), t);
573: // XXX: need to handle depending on type
574: exception = t;
575: }
576:
577: /*
578: if (exception == null && mxbean != null) {
579: // XXX: temp for types
580: exception = null;
581: }
582: */
583:
584: if (exception != null) {
585: failed = true;
586: describe(message, targetModuleID, false,
587: getExceptionMessage(exception));
588: } else
589: describe(message, targetModuleID, true);
590: }
591:
592: if (failed)
593: progress.failed(L.l("stop {0}", message));
594: else
595: progress.completed(L.l("stop {0}", message));
596:
597: return progress;
598: }
599:
600: public ProgressObject undeploy(TargetModuleID[] ids)
601: throws IllegalStateException {
602: ProgressObjectImpl progress = new ProgressObjectImpl(ids);
603:
604: boolean failed = false;
605: StringBuilder message = new StringBuilder();
606:
607: for (TargetModuleID targetModuleID : ids) {
608: if (log.isLoggable(Level.FINE))
609: log.log(Level.FINE, L.l("undeploying {0}",
610: targetModuleID.getModuleID()));
611:
612: ArchiveDeployMXBean mxbean = null;
613: Throwable exception = null;
614:
615: try {
616: ObjectName objectName = new ObjectName(targetModuleID
617: .getModuleID());
618: mxbean = getMXBean(objectName);
619: if (mxbean != null)
620: mxbean.undeploy(objectName.getKeyProperty("name"));
621: } catch (Throwable t) {
622: log.log(Level.INFO, t.toString(), t);
623: exception = t;
624: }
625:
626: if (exception != null) {
627: failed = true;
628: describe(message, targetModuleID, false,
629: getExceptionMessage(exception));
630: } else
631: describe(message, targetModuleID, true);
632: }
633:
634: if (failed)
635: progress.failed(L.l("undeploy {0}", message));
636: else
637: progress.completed(L.l("undeploy {0}", message));
638:
639: return progress;
640: }
641:
642: private void describe(StringBuilder builder,
643: TargetModuleID targetModuleID, boolean success) {
644: describe(builder, targetModuleID, success, null);
645: }
646:
647: private void describe(StringBuilder builder,
648: TargetModuleID targetModuleID, boolean success,
649: String message) {
650: if (builder.length() > 0)
651: builder.append(", ");
652:
653: if (success)
654: builder.append(L.l("successful for target {0} module {1}",
655: targetModuleID.getTarget().getName(),
656: targetModuleID.getModuleID()));
657: else {
658: builder.append(L.l("failed for target {0} module {1}",
659: targetModuleID.getTarget().getName(),
660: targetModuleID.getModuleID()));
661: }
662:
663: if (message != null) {
664: builder.append(" '");
665: builder.append(message);
666: builder.append("'");
667: }
668: }
669: }
|