Coverage Report - org.kuali.rice.kew.plugin.ZipFilePluginLoader
 
Classes in this File Line Coverage Branch Coverage Complexity
ZipFilePluginLoader
0%
0/72
0%
0/40
3.154
 
 1  
 /*
 2  
  * Copyright 2006-2011 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  * http://www.opensource.org/licenses/ecl2.php
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 
 17  
 package org.kuali.rice.kew.plugin;
 18  
 
 19  
 import org.apache.commons.io.FileUtils;
 20  
 import org.apache.log4j.Logger;
 21  
 import org.kuali.rice.core.api.config.property.Config;
 22  
 import org.kuali.rice.kew.api.WorkflowRuntimeException;
 23  
 
 24  
 import java.io.BufferedInputStream;
 25  
 import java.io.BufferedOutputStream;
 26  
 import java.io.File;
 27  
 import java.io.FileOutputStream;
 28  
 import java.io.InputStream;
 29  
 import java.io.OutputStream;
 30  
 import java.net.MalformedURLException;
 31  
 import java.net.URL;
 32  
 import java.util.Enumeration;
 33  
 import java.util.zip.ZipEntry;
 34  
 import java.util.zip.ZipFile;
 35  
 
 36  
 
 37  
 /**
 38  
  * Loads a plugin from a zip file on the file system.
 39  
  *
 40  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 41  
  */
 42  
 public class ZipFilePluginLoader extends BasePluginLoader {
 43  0
         private static final Logger LOG = Logger.getLogger(ZipFilePluginLoader.class);
 44  
 
 45  
         private final File pluginZipFile;
 46  
     private final File extractionDirectory;
 47  0
     private long zipFileLastModified = -1;
 48  0
     private boolean loadFailed = false;
 49  
 
 50  
     private static String validatePluginZipFile(File pluginZipFile) {
 51  0
         PluginUtils.validatePluginZipFile(pluginZipFile);
 52  0
         String fileName = pluginZipFile.getName();
 53  0
         int indexOf = fileName.lastIndexOf(".zip");
 54  0
         return fileName.substring(0, indexOf);
 55  
     }
 56  
 
 57  
     public ZipFilePluginLoader(File pluginZipFile, File sharedPluginDirectory, ClassLoader parentClassLoader, Config parentConfig) {
 58  0
             super(validatePluginZipFile(pluginZipFile), sharedPluginDirectory, parentClassLoader, parentConfig);
 59  0
             this.pluginZipFile = pluginZipFile;
 60  0
             this.extractionDirectory = determineExtractionDirectory(getSimplePluginName(), pluginZipFile);
 61  0
     }
 62  
     
 63  
         public boolean isModified() {
 64  0
                 long currentZipFileLastModified = pluginZipFile.lastModified();
 65  0
                 if (zipFileLastModified == -1) {
 66  0
                         zipFileLastModified = currentZipFileLastModified;
 67  0
                         return false;
 68  0
                 } else if (currentZipFileLastModified > zipFileLastModified) {
 69  0
                         return !isZipFileStillBeingModified();
 70  
                 }
 71  0
                 return false;
 72  
         }
 73  
 
 74  
         protected boolean isZipFileStillBeingModified() {
 75  0
                 long lastModified = pluginZipFile.lastModified();
 76  0
                 long size = pluginZipFile.length();
 77  
                 // sleep for a fraction of a second and then check again if the values have changed
 78  
                 try {
 79  0
                         Thread.sleep(100);
 80  0
                 } catch (InterruptedException e) {}
 81  0
                 if (lastModified != pluginZipFile.lastModified()) {
 82  0
                         return true;
 83  
                 }
 84  0
                 if (size != pluginZipFile.length()) {
 85  0
                         return true;
 86  
                 }
 87  0
                 return false;
 88  
         }
 89  
 
 90  
         public boolean isRemoved() {
 91  0
                 return pluginZipFile != null && !pluginZipFile.exists();
 92  
         }
 93  
 
 94  
     @Override
 95  
         public Plugin load() throws Exception {
 96  
         try {
 97  0
             updateLastModified();
 98  0
             extractPluginFiles();
 99  0
             Plugin plugin = super.load();
 100  0
             loadFailed = false;
 101  0
             return plugin;
 102  0
         } catch (Exception e) {
 103  0
             loadFailed = true;
 104  0
             throw e;
 105  
         }
 106  
         }
 107  
 
 108  
     protected File determineExtractionDirectory(String pluginName, File pluginZipFile) {
 109  0
                 return new File(pluginZipFile.getParentFile(), pluginName);
 110  
         }
 111  
 
 112  
     /**
 113  
      * Extracts the plugin files if necessary.
 114  
      */
 115  
     protected void extractPluginFiles() throws Exception {
 116  0
             if (isExtractNeeded()) {
 117  
                     // first, delete the current copy of the extracted plugin
 118  0
                     if (extractionDirectory.exists()) {
 119  
                             // TODO how to handle locked files under windows?!?  This will throw an IOException in this case.
 120  0
                             FileUtils.deleteDirectory(extractionDirectory);
 121  
                     }
 122  0
                     if (!extractionDirectory.mkdir()) {
 123  0
                             throw new WorkflowRuntimeException("Could not create the extraction directory for the plugin: " + extractionDirectory.getAbsolutePath());
 124  
                     }
 125  0
                     ZipFile zipFile = new ZipFile(pluginZipFile, ZipFile.OPEN_READ);
 126  0
                     for (Enumeration entries = zipFile.entries(); entries.hasMoreElements();) {
 127  0
                             ZipEntry entry = (ZipEntry)entries.nextElement();
 128  0
                                 File entryFile = new File(extractionDirectory + java.io.File.separator + entry.getName());
 129  0
                             if (entry.isDirectory()) { // if its a directory, create it
 130  0
                                     if (!entryFile.mkdir()) {
 131  0
                                             throw new WorkflowRuntimeException("Failed to create directory: " + entryFile.getAbsolutePath());
 132  
                                     }
 133  
                                     continue;
 134  
                             }
 135  0
                             InputStream is = null;
 136  0
                             OutputStream os = null;
 137  
                             try {
 138  0
                                     is = new BufferedInputStream(zipFile.getInputStream(entry)); // get the input stream
 139  0
                                     os = new BufferedOutputStream(new FileOutputStream(entryFile));
 140  0
                                     while (is.available() > 0) {  // write contents of 'is' to 'fos'
 141  0
                                             os.write(is.read());
 142  
                                     }
 143  
                             } finally {
 144  0
                                     if (os != null) {
 145  0
                                             os.close();
 146  
                                     }
 147  0
                                     if (is != null) {
 148  0
                                             is.close();
 149  
                                     }
 150  
                             }
 151  0
                     }
 152  
             }
 153  0
     }
 154  
     
 155  
     /**
 156  
      * @return the loadFailed
 157  
      */
 158  
     public boolean isLoadFailed() {
 159  0
         return this.loadFailed;
 160  
     }
 161  
 
 162  
     /**
 163  
      * An extract is required if the plugin has been modified or the last modified date of the zip file
 164  
      * is later than the last modified date of the extraction directory.
 165  
      */
 166  
     protected boolean isExtractNeeded() {
 167  0
             return isModified() || pluginZipFile.lastModified() > extractionDirectory.lastModified();
 168  
     }
 169  
 
 170  
     protected void updateLastModified() {
 171  0
             zipFileLastModified = pluginZipFile.lastModified();
 172  0
     }
 173  
 
 174  
         protected PluginClassLoader createPluginClassLoader() throws MalformedURLException {
 175  0
         LOG.info(getLogPrefix() + " Initiating loading of plugin from file system: " + extractionDirectory.getPath());
 176  0
         LOG.info(getLogPrefix() + " Absolute path on file system is: " + extractionDirectory.getAbsolutePath());
 177  
         /* MalformedURLException should technically never be thrown as the URLs are coming from (presumably)
 178  
          * valid File objects
 179  
          */
 180  0
         return new PluginClassLoader(parentClassLoader, sharedPluginDirectory, extractionDirectory);
 181  
     }
 182  
 
 183  
     protected URL getPluginConfigURL() throws PluginException, MalformedURLException {
 184  0
         File pluginConfigFile = new File(extractionDirectory, pluginConfigPath);
 185  0
         if (!pluginConfigFile.exists() || !pluginConfigFile.isFile()) {
 186  0
             throw new PluginException(getLogPrefix() + " Could not locate the plugin config file at path " + pluginConfigFile.getAbsolutePath());
 187  
         }
 188  0
         return pluginConfigFile.toURI().toURL();
 189  
     }
 190  
 }