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