001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.servicemix.jbi.framework;
018:
019: import java.io.*;
020: import java.net.MalformedURLException;
021: import java.net.URI;
022: import java.net.URISyntaxException;
023: import java.net.URL;
024: import java.util.ArrayList;
025: import java.util.Date;
026: import java.util.HashMap;
027: import java.util.HashSet;
028: import java.util.List;
029: import java.util.Map;
030: import java.util.Set;
031: import java.util.Timer;
032: import java.util.TimerTask;
033: import java.util.concurrent.ConcurrentHashMap;
034: import java.util.concurrent.atomic.AtomicBoolean;
035: import java.util.zip.ZipFile;
036:
037: import javax.jbi.JBIException;
038: import javax.jbi.management.DeploymentException;
039: import javax.management.JMException;
040: import javax.management.MBeanAttributeInfo;
041:
042: import org.apache.commons.logging.Log;
043: import org.apache.commons.logging.LogFactory;
044: import org.apache.servicemix.jbi.container.EnvironmentContext;
045: import org.apache.servicemix.jbi.container.JBIContainer;
046: import org.apache.servicemix.jbi.deployment.Component;
047: import org.apache.servicemix.jbi.deployment.Descriptor;
048: import org.apache.servicemix.jbi.deployment.DescriptorFactory;
049: import org.apache.servicemix.jbi.deployment.ServiceAssembly;
050: import org.apache.servicemix.jbi.event.DeploymentEvent;
051: import org.apache.servicemix.jbi.event.DeploymentListener;
052: import org.apache.servicemix.jbi.management.AttributeInfoHelper;
053: import org.apache.servicemix.jbi.management.BaseSystemService;
054: import org.apache.servicemix.jbi.util.FileUtil;
055: import org.apache.servicemix.jbi.util.XmlPersistenceSupport;
056:
057: /**
058: * Monitors install and deploy directories to auto install/deploy archives
059: *
060: * @version $Revision: 634607 $
061: */
062: public class AutoDeploymentService extends BaseSystemService implements
063: AutoDeploymentServiceMBean {
064:
065: private static final Log LOG = LogFactory
066: .getLog(AutoDeploymentService.class);
067:
068: private static String filePrefix = "file:///";
069: private EnvironmentContext environmentContext;
070: private DeploymentService deploymentService;
071: private InstallationService installationService;
072: private boolean monitorInstallationDirectory = true;
073: private boolean monitorDeploymentDirectory = true;
074: private int monitorInterval = 10;
075: private String extensions = ".zip,.jar";
076: private AtomicBoolean started = new AtomicBoolean(false);
077: private Timer statsTimer;
078: private TimerTask timerTask;
079: private Map<File, ArchiveEntry> pendingComponents = new ConcurrentHashMap<File, ArchiveEntry>();
080: private Map<File, ArchiveEntry> pendingSAs = new ConcurrentHashMap<File, ArchiveEntry>();
081: private Map<String, ArchiveEntry> installFileMap;
082: private Map<String, ArchiveEntry> deployFileMap;
083:
084: /**
085: * @return the extensions
086: */
087: public String getExtensions() {
088: return extensions;
089: }
090:
091: /**
092: * @param extensions
093: * the extensions to set
094: */
095: public void setExtensions(String extensions) {
096: this .extensions = extensions;
097: }
098:
099: /**
100: * @return a description of this
101: */
102: public String getDescription() {
103: return "automatically installs and deploys JBI Archives";
104: }
105:
106: /**
107: * @return Returns the monitorInstallationDirectory.
108: */
109: public boolean isMonitorInstallationDirectory() {
110: return monitorInstallationDirectory;
111: }
112:
113: /**
114: * @param monitorInstallationDirectory
115: * The monitorInstallationDirectory to set.
116: */
117: public void setMonitorInstallationDirectory(
118: boolean monitorInstallationDirectory) {
119: this .monitorInstallationDirectory = monitorInstallationDirectory;
120: }
121:
122: /**
123: * @return Returns the monitorDeploymentDirectory.
124: */
125: public boolean isMonitorDeploymentDirectory() {
126: return monitorDeploymentDirectory;
127: }
128:
129: /**
130: * @param monitorDeploymentDirectory
131: * The monitorDeploymentDirectory to set.
132: */
133: public void setMonitorDeploymentDirectory(
134: boolean monitorDeploymentDirectory) {
135: this .monitorDeploymentDirectory = monitorDeploymentDirectory;
136: }
137:
138: /**
139: * @return Returns the monitorInterval (number in secs)
140: */
141: public int getMonitorInterval() {
142: return monitorInterval;
143: }
144:
145: /**
146: * @param monitorInterval
147: * The monitorInterval to set (in secs)
148: */
149: public void setMonitorInterval(int monitorInterval) {
150: this .monitorInterval = monitorInterval;
151: }
152:
153: public void start() throws javax.jbi.JBIException {
154: super .start();
155: if (started.compareAndSet(false, true)) {
156: scheduleDirectoryTimer();
157: }
158: }
159:
160: /**
161: * Stop the item. This suspends current messaging activities.
162: *
163: * @exception javax.jbi.JBIException
164: * if the item fails to stop.
165: */
166: public void stop() throws javax.jbi.JBIException {
167: if (started.compareAndSet(true, false)) {
168: super .stop();
169: if (timerTask != null) {
170: timerTask.cancel();
171: }
172: }
173: }
174:
175: /**
176: * Initialize the Service
177: *
178: * @param container
179: * @throws JBIException
180: */
181: public void init(JBIContainer container) throws JBIException {
182: super .init(container);
183: this .environmentContext = container.getEnvironmentContext();
184: this .installationService = container.getInstallationService();
185: this .deploymentService = container.getDeploymentService();
186: // clean-up tmp directory
187: if (environmentContext.getTmpDir() != null) {
188: FileUtil.deleteFile(environmentContext.getTmpDir());
189: }
190: initializeFileMaps();
191: }
192:
193: protected Class<AutoDeploymentServiceMBean> getServiceMBean() {
194: return AutoDeploymentServiceMBean.class;
195: }
196:
197: /**
198: * load an archive from an external location
199: *
200: * @param location
201: * @param autoStart
202: * @throws DeploymentException
203: */
204: public ArchiveEntry updateExternalArchive(String location,
205: boolean autoStart) throws DeploymentException {
206: ArchiveEntry entry = new ArchiveEntry();
207: entry.location = location;
208: entry.lastModified = new Date();
209: updateArchive(location, entry, autoStart);
210: return entry;
211: }
212:
213: /**
214: * Update an archive
215: *
216: * @param location
217: * @param autoStart
218: * @throws DeploymentException
219: */
220: public void updateArchive(String location, ArchiveEntry entry,
221: boolean autoStart) throws DeploymentException {
222: // Call listeners
223: try {
224: DeploymentListener[] listeners = (DeploymentListener[]) container
225: .getListeners(DeploymentListener.class);
226: DeploymentEvent event = new DeploymentEvent(new File(
227: location), DeploymentEvent.FILE_CHANGED);
228: for (int i = 0; i < listeners.length; i++) {
229: if (listeners[i].fileChanged(event)) {
230: return;
231: }
232: }
233: } catch (IOException e) {
234: throw failure("deploy",
235: "Error when deploying: " + location, e);
236: }
237: // Standard processing
238: File tmpDir = null;
239: try {
240: tmpDir = AutoDeploymentService.unpackLocation(
241: environmentContext.getTmpDir(), location);
242: } catch (Exception e) {
243: throw failure("deploy", "Unable to unpack archive: "
244: + location, e);
245: }
246: // unpackLocation returns null if no jbi descriptor is found
247: if (tmpDir == null) {
248: throw failure("deploy", "Unable to find jbi descriptor: "
249: + location);
250: }
251: Descriptor root = null;
252: try {
253: root = DescriptorFactory.buildDescriptor(tmpDir);
254: } catch (Exception e) {
255: throw failure("deploy", "Unable to build jbi descriptor: "
256: + location, e);
257: }
258: if (root == null) {
259: throw failure("deploy", "Unable to find jbi descriptor: "
260: + location);
261: }
262: if (root != null) {
263: try {
264: container.getBroker().suspend();
265: if (root.getComponent() != null) {
266: updateComponent(entry, autoStart, tmpDir, root);
267: } else if (root.getSharedLibrary() != null) {
268: updateSharedLibrary(entry, tmpDir, root);
269: } else if (root.getServiceAssembly() != null) {
270: updateServiceAssembly(entry, autoStart, tmpDir,
271: root);
272: }
273: } finally {
274: container.getBroker().resume();
275: }
276: }
277: }
278:
279: protected void updateComponent(ArchiveEntry entry,
280: boolean autoStart, File tmpDir, Descriptor root)
281: throws DeploymentException {
282: Component comp = root.getComponent();
283: String componentName = comp.getIdentification().getName();
284: entry.type = "component";
285: entry.name = componentName;
286: try {
287: if (container.getRegistry().getComponent(componentName) != null) {
288: installationService.loadInstaller(componentName);
289: installationService
290: .unloadInstaller(componentName, true);
291: }
292: // See if shared libraries are installed
293: entry.dependencies = getSharedLibraryNames(comp);
294: if (LOG.isDebugEnabled()) {
295: LOG.debug("Component dependencies: "
296: + entry.dependencies);
297: }
298: String missings = null;
299: boolean canInstall = true;
300: for (String libraryName : entry.dependencies) {
301: if (container.getRegistry().getSharedLibrary(
302: libraryName) == null) {
303: canInstall = false;
304: if (missings != null) {
305: missings += ", " + libraryName;
306: } else {
307: missings = libraryName;
308: }
309: }
310: }
311: if (canInstall) {
312: installationService.install(tmpDir, null, root,
313: autoStart);
314: checkPendingSAs();
315: } else {
316: entry.pending = true;
317: LOG
318: .warn("Shared libraries "
319: + missings
320: + " are not installed yet: the component"
321: + componentName
322: + " installation is suspended and will be resumed once the listed shared libraries are installed");
323: pendingComponents.put(tmpDir, entry);
324: }
325: } catch (Exception e) {
326: String errStr = "Failed to update Component: "
327: + componentName;
328: LOG.error(errStr, e);
329: throw new DeploymentException(errStr, e);
330: }
331: }
332:
333: protected void updateSharedLibrary(ArchiveEntry entry, File tmpDir,
334: Descriptor root) throws DeploymentException {
335: String libraryName = root.getSharedLibrary()
336: .getIdentification().getName();
337: entry.type = "library";
338: entry.name = libraryName;
339: try {
340: if (container.getRegistry().getSharedLibrary(libraryName) != null) {
341: container.getRegistry().unregisterSharedLibrary(
342: libraryName);
343: environmentContext
344: .removeSharedLibraryDirectory(libraryName);
345: }
346: installationService.doInstallSharedLibrary(tmpDir, root
347: .getSharedLibrary());
348: checkPendingComponents();
349: } catch (Exception e) {
350: String errStr = "Failed to update SharedLibrary: "
351: + libraryName;
352: LOG.error(errStr, e);
353: throw new DeploymentException(errStr, e);
354: }
355: }
356:
357: protected void updateServiceAssembly(ArchiveEntry entry,
358: boolean autoStart, File tmpDir, Descriptor root)
359: throws DeploymentException {
360: ServiceAssembly sa = root.getServiceAssembly();
361: String name = sa.getIdentification().getName();
362: entry.type = "assembly";
363: entry.name = name;
364: try {
365: if (deploymentService.isSaDeployed(name)) {
366: deploymentService.shutDown(name);
367: deploymentService.undeploy(name);
368: }
369: // see if components are installed
370: entry.dependencies = getComponentNames(sa);
371: if (LOG.isDebugEnabled()) {
372: LOG.debug("SA dependencies: " + entry.dependencies);
373: }
374: String missings = null;
375: boolean canDeploy = true;
376: for (String componentName : entry.dependencies) {
377: if (container.getComponent(componentName) == null) {
378: canDeploy = false;
379: if (missings != null) {
380: missings += ", " + componentName;
381: } else {
382: missings = componentName;
383: }
384: }
385: }
386: if (canDeploy) {
387: deploymentService.deployServiceAssembly(tmpDir, sa);
388: if (autoStart) {
389: deploymentService.start(name);
390: }
391: } else {
392: // TODO: check that the assembly is not already
393: // pending
394: entry.pending = true;
395: LOG
396: .warn("Components "
397: + missings
398: + " are not installed yet: the service assembly "
399: + name
400: + " deployment is suspended and will be resumed once the listed components are installed");
401: pendingSAs.put(tmpDir, entry);
402: }
403: } catch (Exception e) {
404: String errStr = "Failed to update Service Assembly: "
405: + name;
406: LOG.error(errStr, e);
407: throw new DeploymentException(errStr, e);
408: }
409: }
410:
411: protected DeploymentException failure(String task, String info) {
412: return failure(task, info, null, null);
413: }
414:
415: protected DeploymentException failure(String task, String info,
416: Exception e) {
417: return failure(task, info, e, null);
418: }
419:
420: protected DeploymentException failure(String task, String info,
421: Exception e, List componentResults) {
422: ManagementSupport.Message msg = new ManagementSupport.Message();
423: msg.setTask(task);
424: msg.setResult("FAILED");
425: msg.setType("ERROR");
426: msg.setException(e);
427: msg.setMessage(info);
428: return new DeploymentException(ManagementSupport
429: .createFrameworkMessage(msg, componentResults));
430: }
431:
432: protected Set<String> getComponentNames(ServiceAssembly sa) {
433: Set<String> names = new HashSet<String>();
434: if (sa.getServiceUnits() != null
435: && sa.getServiceUnits().length > 0) {
436: for (int i = 0; i < sa.getServiceUnits().length; i++) {
437: names.add(sa.getServiceUnits()[i].getTarget()
438: .getComponentName());
439: }
440: }
441: return names;
442: }
443:
444: protected Set<String> getSharedLibraryNames(Component comp) {
445: Set<String> names = new HashSet<String>();
446: if (comp.getSharedLibraries() != null
447: && comp.getSharedLibraries().length > 0) {
448: for (int i = 0; i < comp.getSharedLibraries().length; i++) {
449: names.add(comp.getSharedLibraries()[i].getName());
450: }
451: }
452: return names;
453: }
454:
455: /**
456: * Remove an archive location
457: *
458: * @param location
459: * @throws DeploymentException
460: */
461: public void removeArchive(ArchiveEntry entry)
462: throws DeploymentException {
463: // Call listeners
464: try {
465: DeploymentListener[] listeners = (DeploymentListener[]) container
466: .getListeners(DeploymentListener.class);
467: DeploymentEvent event = new DeploymentEvent(new File(
468: entry.location), DeploymentEvent.FILE_REMOVED);
469: for (int i = 0; i < listeners.length; i++) {
470: if (listeners[i].fileRemoved(event)) {
471: return;
472: }
473: }
474: } catch (IOException e) {
475: throw failure("deploy", "Error when deploying: "
476: + entry.location, e);
477: }
478: // Standard processing
479: LOG.info("Attempting to remove archive at: " + entry.location);
480: try {
481: container.getBroker().suspend();
482: if ("component".equals(entry.type)) {
483: LOG.info("Uninstalling component: " + entry.name);
484: // Ensure installer is loaded
485: installationService.loadInstaller(entry.name);
486: // Uninstall and delete component
487: installationService.unloadInstaller(entry.name, true);
488: }
489: if ("library".equals(entry.type)) {
490: LOG.info("Removing shared library: " + entry.name);
491: installationService.uninstallSharedLibrary(entry.name);
492: }
493: if ("assembly".equals(entry.type)) {
494: LOG.info("Undeploying service assembly " + entry.name);
495: try {
496: if (deploymentService.isSaDeployed(entry.name)) {
497: deploymentService.shutDown(entry.name);
498: deploymentService.undeploy(entry.name);
499: }
500: } catch (Exception e) {
501: String errStr = "Failed to update service assembly: "
502: + entry.name;
503: LOG.error(errStr, e);
504: throw new DeploymentException(errStr, e);
505: }
506: }
507: } finally {
508: container.getBroker().resume();
509: }
510: }
511:
512: /**
513: * Called when a component has been installed to see if pending service
514: * assemblies have all component installed.
515: */
516: private void checkPendingSAs() {
517: Set<File> deployedSas = new HashSet<File>();
518: for (Map.Entry<File, ArchiveEntry> me : pendingSAs.entrySet()) {
519: ArchiveEntry entry = me.getValue();
520: boolean canDeploy = true;
521: for (String componentName : entry.dependencies) {
522: if (container.getComponent(componentName) == null) {
523: canDeploy = false;
524: break;
525: }
526: }
527: if (canDeploy) {
528: File tmp = (File) me.getKey();
529: deployedSas.add(tmp);
530: try {
531: Descriptor root = DescriptorFactory
532: .buildDescriptor(tmp);
533: deploymentService.deployServiceAssembly(tmp, root
534: .getServiceAssembly());
535: deploymentService.start(root.getServiceAssembly()
536: .getIdentification().getName());
537: } catch (Exception e) {
538: String errStr = "Failed to update Service Assembly: "
539: + tmp.getName();
540: LOG.error(errStr, e);
541: }
542: }
543: }
544: if (!deployedSas.isEmpty()) {
545: // Remove SA from pending SAs
546: for (File f : deployedSas) {
547: ArchiveEntry entry = pendingSAs.remove(f);
548: entry.pending = false;
549: }
550: // Store new state
551: persistState(environmentContext.getDeploymentDir(),
552: deployFileMap);
553: persistState(environmentContext.getInstallationDir(),
554: installFileMap);
555: }
556: }
557:
558: private void checkPendingComponents() {
559: Set<File> installedComponents = new HashSet<File>();
560: for (Map.Entry<File, ArchiveEntry> me : pendingComponents
561: .entrySet()) {
562: ArchiveEntry entry = me.getValue();
563: boolean canInstall = true;
564: for (String libraryName : entry.dependencies) {
565: if (container.getRegistry().getSharedLibrary(
566: libraryName) == null) {
567: canInstall = false;
568: break;
569: }
570: }
571: if (canInstall) {
572: File tmp = me.getKey();
573: installedComponents.add(tmp);
574: try {
575: Descriptor root = DescriptorFactory
576: .buildDescriptor(tmp);
577: installationService.install(tmp, null, root, true);
578: } catch (Exception e) {
579: String errStr = "Failed to update Component: "
580: + tmp.getName();
581: LOG.error(errStr, e);
582: }
583: }
584: }
585: if (!installedComponents.isEmpty()) {
586: // Remove SA from pending SAs
587: for (File f : installedComponents) {
588: ArchiveEntry entry = pendingComponents.remove(f);
589: entry.pending = false;
590: }
591: // Store new state
592: persistState(environmentContext.getDeploymentDir(),
593: deployFileMap);
594: persistState(environmentContext.getInstallationDir(),
595: installFileMap);
596: // Check for pending SAs
597: checkPendingSAs();
598: }
599: }
600:
601: /**
602: * Get an array of MBeanAttributeInfo
603: *
604: * @return array of AttributeInfos
605: * @throws JMException
606: */
607: public MBeanAttributeInfo[] getAttributeInfos() throws JMException {
608: AttributeInfoHelper helper = new AttributeInfoHelper();
609: helper.addAttribute(getObjectToManage(),
610: "monitorInstallationDirectory",
611: "Periodically monitor the Installation directory");
612: helper.addAttribute(getObjectToManage(), "monitorInterval",
613: "Interval (secs) before monitoring");
614: return AttributeInfoHelper.join(super .getAttributeInfos(),
615: helper.getAttributeInfos());
616: }
617:
618: /**
619: * Unpack a location into a temp file directory. If the location does not
620: * contain a jbi descritor, no unpacking occurs.
621: *
622: * @param location
623: * @return tmp directory (if location contains a jbi descriptor)
624: * @throws DeploymentException
625: */
626: protected static File unpackLocation(File tmpRoot, String location)
627: throws DeploymentException {
628: File tmpDir = null;
629: File file = null;
630: try {
631: if (location.startsWith(filePrefix)) {
632: URI uri = new URI(location);
633: file = new File(uri);
634: } else {
635: file = new File(location);
636: }
637: if (file.isDirectory()) {
638: if (LOG.isDebugEnabled()) {
639: LOG
640: .debug("Deploying an exploded jar/zip, we will create a temporary jar for it.");
641: }
642: // If we have a directory then we should move it over
643: File newFile = new File(tmpRoot.getAbsolutePath()
644: + "/exploded.jar");
645: newFile.delete();
646: FileUtil.zipDir(file.getAbsolutePath(), newFile
647: .getAbsolutePath());
648: file = newFile;
649: if (LOG.isDebugEnabled()) {
650: LOG.debug("Deployment will now work from "
651: + file.getAbsolutePath());
652: }
653: }
654: if (!file.exists()) {
655: // assume it's a URL
656: try {
657: URL url = new URL(location);
658: String fileName = url.getFile();
659: if (fileName == null) {
660: throw new DeploymentException("Location: "
661: + location + " is not an archive");
662: }
663: file = FileUtil.unpackArchive(url, tmpRoot);
664: } catch (MalformedURLException e) {
665: throw new DeploymentException(e);
666: }
667: }
668: if (FileUtil.archiveContainsEntry(file,
669: DescriptorFactory.DESCRIPTOR_FILE)) {
670: tmpDir = FileUtil.createUniqueDirectory(tmpRoot, file
671: .getName());
672: FileUtil.unpackArchive(file, tmpDir);
673: if (LOG.isDebugEnabled()) {
674: LOG.debug("Unpacked archive " + location + " to "
675: + tmpDir);
676: }
677: }
678: } catch (IOException e) {
679: throw new DeploymentException(e);
680: } catch (URISyntaxException ex) {
681: throw new DeploymentException(ex);
682: }
683: return tmpDir;
684: }
685:
686: private void scheduleDirectoryTimer() {
687: if (!container.isEmbedded()
688: && (isMonitorInstallationDirectory() || isMonitorDeploymentDirectory())) {
689: if (statsTimer == null) {
690: statsTimer = new Timer(true);
691: }
692: if (timerTask != null) {
693: timerTask.cancel();
694: }
695: timerTask = new TimerTask() {
696: public void run() {
697: if (isMonitorInstallationDirectory()) {
698: monitorDirectory(environmentContext
699: .getInstallationDir(), installFileMap);
700: }
701: if (isMonitorDeploymentDirectory()) {
702: monitorDirectory(environmentContext
703: .getDeploymentDir(), deployFileMap);
704: }
705: }
706: };
707: long interval = monitorInterval * 1000;
708: statsTimer.scheduleAtFixedRate(timerTask, 0, interval);
709: }
710: }
711:
712: private void monitorDirectory(final File root,
713: final Map<String, ArchiveEntry> fileMap) {
714: /*
715: * if (log.isTraceEnabled()) { if (root != null) log.trace("Monitoring
716: * directory " + root.getAbsolutePath() + " for new or modified
717: * archives"); else log.trace("No directory to monitor for new or
718: * modified archives for " + ((fileMap==installFileMap) ? "Installation" :
719: * "Deployment") + "."); }
720: */
721: List<String> tmpList = new ArrayList<String>();
722: if (root != null && root.exists() && root.isDirectory()) {
723: File[] files = root.listFiles();
724: if (files != null) {
725: for (int i = 0; i < files.length; i++) {
726: final File file = files[i];
727: tmpList.add(file.getName());
728: if (isAllowedExtension(file.getName())
729: && isAvailable(file)) {
730: ArchiveEntry lastEntry = fileMap.get(file
731: .getName());
732: if (lastEntry == null
733: || file.lastModified() > lastEntry.lastModified
734: .getTime()) {
735: try {
736: final ArchiveEntry entry = new ArchiveEntry();
737: entry.location = file.getName();
738: entry.lastModified = new Date(file
739: .lastModified());
740: fileMap.put(file.getName(), entry);
741: LOG
742: .info("Directory: "
743: + root.getName()
744: + ": Archive changed: processing "
745: + file.getName()
746: + " ...");
747: updateArchive(file.getAbsolutePath(),
748: entry, true);
749: LOG
750: .info("Directory: "
751: + root.getName()
752: + ": Finished installation of archive: "
753: + file.getName());
754: } catch (Exception e) {
755: LOG.warn("Directory: " + root.getName()
756: + ": Automatic install of "
757: + file + " failed", e);
758: } finally {
759: persistState(root, fileMap);
760: }
761: }
762: }
763: }
764: }
765: // now remove any locations no longer here
766: Map<String, ArchiveEntry> map = new HashMap<String, ArchiveEntry>(
767: fileMap);
768: for (String location : map.keySet()) {
769: if (!tmpList.contains(location)) {
770: ArchiveEntry entry = fileMap.remove(location);
771: try {
772: LOG.info("Location " + location
773: + " no longer exists - removing ...");
774: removeArchive(entry);
775: } catch (DeploymentException e) {
776: LOG.error("Failed to removeArchive: "
777: + location, e);
778: }
779: }
780: }
781: if (!map.equals(fileMap)) {
782: persistState(root, fileMap);
783: }
784: }
785: }
786:
787: private boolean isAvailable(File file) {
788: // First check to see if the file is still growing
789: long targetLength = file.length();
790: try {
791: Thread.sleep(100);
792: } catch (InterruptedException e) {
793: //Do nothing
794: }
795: long target2Length = file.length();
796:
797: if (targetLength != target2Length) {
798: LOG
799: .warn("File is still being copied, deployment deferred to next cycle: "
800: + file.getName());
801: return false;
802: }
803:
804: // If file size is consistent, do a foolproof check of the zip file
805: try {
806: ZipFile zip = new ZipFile(file);
807: zip.size();
808: zip.close();
809: } catch (IOException e) {
810: LOG
811: .warn("Unable to open deployment file, deployment deferred to next cycle: "
812: + file.getName());
813: return false;
814: }
815:
816: return true;
817: }
818:
819: private boolean isAllowedExtension(String file) {
820: String[] ext = this .extensions.split(",");
821: for (int i = 0; i < ext.length; i++) {
822: if (file.endsWith(ext[i])) {
823: return true;
824: }
825: }
826: return false;
827: }
828:
829: private void persistState(File root, Map<String, ArchiveEntry> map) {
830: try {
831: File file = new File(environmentContext.getJbiRootDir(),
832: root.getName() + ".xml");
833: XmlPersistenceSupport.write(file, map);
834: } catch (IOException e) {
835: LOG.error("Failed to persist file state to: " + root, e);
836: }
837: }
838:
839: @SuppressWarnings("unchecked")
840: private Map<String, ArchiveEntry> readState(File root) {
841: Map<String, ArchiveEntry> result = new HashMap<String, ArchiveEntry>();
842: try {
843: File file = new File(environmentContext.getJbiRootDir(),
844: root.getName() + ".xml");
845: if (file.exists()) {
846: result = (Map<String, ArchiveEntry>) XmlPersistenceSupport
847: .read(file);
848: } else {
849: LOG
850: .debug("State file doesn't exist: "
851: + file.getPath());
852: }
853: } catch (Exception e) {
854: LOG.error("Failed to read file state from: " + root, e);
855: }
856: return result;
857: }
858:
859: private void initializeFileMaps() {
860: if (isMonitorInstallationDirectory() && !container.isEmbedded()) {
861: try {
862: installFileMap = readState(environmentContext
863: .getInstallationDir());
864: removePendingEntries(installFileMap);
865: } catch (Exception e) {
866: LOG.error("Failed to read installed state", e);
867: }
868: }
869: if (isMonitorDeploymentDirectory() && !container.isEmbedded()) {
870: try {
871: deployFileMap = readState(environmentContext
872: .getDeploymentDir());
873: removePendingEntries(deployFileMap);
874: } catch (Exception e) {
875: LOG.error("Failed to read deployed state", e);
876: }
877: }
878: }
879:
880: private void removePendingEntries(Map<String, ArchiveEntry> map) {
881: Set<String> pendings = new HashSet<String>();
882: for (Map.Entry<String, ArchiveEntry> e : map.entrySet()) {
883: if (e.getValue().pending) {
884: pendings.add(e.getKey());
885: }
886: }
887: for (String s : pendings) {
888: map.remove(s);
889: }
890: }
891:
892: public static class ArchiveEntry {
893:
894: private String location;
895: private Date lastModified;
896: private String type;
897: private String name;
898: private boolean pending;
899: private transient Set<String> dependencies;
900:
901: public String getLocation() {
902: return location;
903: }
904:
905: public void setLocation(String location) {
906: this .location = location;
907: }
908:
909: public Date getLastModified() {
910: return lastModified;
911: }
912:
913: public void setLastModified(Date lastModified) {
914: this .lastModified = lastModified;
915: }
916:
917: public String getType() {
918: return type;
919: }
920:
921: public void setType(String type) {
922: this .type = type;
923: }
924:
925: public String getName() {
926: return name;
927: }
928:
929: public void setName(String name) {
930: this .name = name;
931: }
932:
933: public boolean isPending() {
934: return pending;
935: }
936:
937: public void setPending(boolean pending) {
938: this .pending = pending;
939: }
940:
941: public Set<String> getDependencies() {
942: return dependencies;
943: }
944:
945: public void setDependencies(Set<String> dependencies) {
946: this.dependencies = dependencies;
947: }
948: }
949: }
|