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