001: /*
002: * Copyright 2005-2006 The Kuali Foundation.
003: *
004: *
005: * Licensed under the Educational Community License, Version 1.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.opensource.org/licenses/ecl1.php
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 edu.iu.uis.eden.plugin;
018:
019: import java.io.BufferedInputStream;
020: import java.io.BufferedOutputStream;
021: import java.io.File;
022: import java.io.FileOutputStream;
023: import java.io.InputStream;
024: import java.io.OutputStream;
025: import java.net.MalformedURLException;
026: import java.net.URL;
027: import java.util.Enumeration;
028: import java.util.zip.ZipEntry;
029: import java.util.zip.ZipFile;
030:
031: import org.apache.commons.io.FileUtils;
032: import org.apache.log4j.Logger;
033: import org.kuali.rice.config.Config;
034:
035: import edu.iu.uis.eden.exception.WorkflowRuntimeException;
036:
037: /**
038: * Loads a plugin from a zip file on the file system.
039: *
040: * @author ewestfal
041: */
042: public class ZipFilePluginLoader extends BasePluginLoader {
043: private static final Logger LOG = Logger
044: .getLogger(ZipFilePluginLoader.class);
045:
046: private final File pluginZipFile;
047: private final File extractionDirectory;
048: private long zipFileLastModified = -1;
049:
050: private static String validatePluginZipFile(File pluginZipFile) {
051: PluginUtils.validatePluginZipFile(pluginZipFile);
052: String fileName = pluginZipFile.getName();
053: int indexOf = fileName.lastIndexOf(".zip");
054: return fileName.substring(0, indexOf);
055: }
056:
057: public ZipFilePluginLoader(File pluginZipFile,
058: File sharedPluginDirectory, ClassLoader parentClassLoader,
059: Config parentConfig, boolean institutionalPlugin) {
060: super (validatePluginZipFile(pluginZipFile),
061: sharedPluginDirectory, parentClassLoader, parentConfig,
062: institutionalPlugin);
063: this .pluginZipFile = pluginZipFile;
064: this .extractionDirectory = determineExtractionDirectory(
065: getSimplePluginName(), pluginZipFile);
066: }
067:
068: public boolean isModified() {
069: long currentZipFileLastModified = pluginZipFile.lastModified();
070: if (zipFileLastModified == -1) {
071: zipFileLastModified = currentZipFileLastModified;
072: return false;
073: } else if (currentZipFileLastModified > zipFileLastModified) {
074: return !isZipFileStillBeingModified();
075: }
076: return false;
077: }
078:
079: protected boolean isZipFileStillBeingModified() {
080: long lastModified = pluginZipFile.lastModified();
081: long size = pluginZipFile.length();
082: // sleep for a fraction of a second and then check again if the values have changed
083: try {
084: Thread.sleep(100);
085: } catch (InterruptedException e) {
086: }
087: if (lastModified != pluginZipFile.lastModified()) {
088: return true;
089: }
090: if (size != pluginZipFile.length()) {
091: return true;
092: }
093: return false;
094: }
095:
096: public boolean isRemoved() {
097: return pluginZipFile != null && !pluginZipFile.exists();
098: }
099:
100: @Override
101: public Plugin load() throws Exception {
102: extractPluginFiles();
103: updateLastModified();
104: return super .load();
105: }
106:
107: protected File determineExtractionDirectory(String pluginName,
108: File pluginZipFile) {
109: return new File(pluginZipFile.getParentFile(), pluginName);
110: }
111:
112: /**
113: * Extracts the plugin files if necessary.
114: */
115: protected void extractPluginFiles() throws Exception {
116: if (isExtractNeeded()) {
117: // first, delete the current copy of the extracted plugin
118: if (extractionDirectory.exists()) {
119: // TODO how to handle locked files under windows?!? This will throw an IOException in this case.
120: FileUtils.deleteDirectory(extractionDirectory);
121: }
122: if (!extractionDirectory.mkdir()) {
123: throw new WorkflowRuntimeException(
124: "Could not create the extraction directory for the plugin: "
125: + extractionDirectory.getAbsolutePath());
126: }
127: ZipFile zipFile = new ZipFile(pluginZipFile,
128: ZipFile.OPEN_READ);
129: for (Enumeration entries = zipFile.entries(); entries
130: .hasMoreElements();) {
131: ZipEntry entry = (ZipEntry) entries.nextElement();
132: File entryFile = new File(extractionDirectory
133: + java.io.File.separator + entry.getName());
134: if (entry.isDirectory()) { // if its a directory, create it
135: if (!entryFile.mkdir()) {
136: throw new WorkflowRuntimeException(
137: "Failed to create directory: "
138: + entryFile.getAbsolutePath());
139: }
140: continue;
141: }
142: InputStream is = null;
143: OutputStream os = null;
144: try {
145: is = new BufferedInputStream(zipFile
146: .getInputStream(entry)); // get the input stream
147: os = new BufferedOutputStream(new FileOutputStream(
148: entryFile));
149: while (is.available() > 0) { // write contents of 'is' to 'fos'
150: os.write(is.read());
151: }
152: } finally {
153: if (os != null) {
154: os.close();
155: }
156: if (is != null) {
157: is.close();
158: }
159: }
160: }
161: }
162: }
163:
164: /**
165: * An extract is required if the plugin has been modified or the last modified date of the zip file
166: * is later than the last modified date of the extraction directory.
167: */
168: protected boolean isExtractNeeded() {
169: return isModified()
170: || pluginZipFile.lastModified() > extractionDirectory
171: .lastModified();
172: }
173:
174: protected void updateLastModified() {
175: zipFileLastModified = pluginZipFile.lastModified();
176: }
177:
178: protected PluginClassLoader createPluginClassLoader()
179: throws MalformedURLException {
180: LOG.info(getLogPrefix()
181: + " Initiating loading of plugin from file system: "
182: + extractionDirectory.getPath());
183: LOG.info(getLogPrefix() + " Absolute path on file system is: "
184: + extractionDirectory.getAbsolutePath());
185: /* MalformedURLException should technically never be thrown as the URLs are coming from (presumably)
186: * valid File objects
187: */
188: return new PluginClassLoader(parentClassLoader,
189: sharedPluginDirectory, extractionDirectory);
190: }
191:
192: protected URL getPluginManifestURL() throws PluginException,
193: MalformedURLException {
194: File pluginManifestFile = new File(extractionDirectory,
195: pluginManifestPath);
196: if (!pluginManifestFile.exists()
197: || !pluginManifestFile.isFile()) {
198: throw new PluginException(
199: getLogPrefix()
200: + " Could not locate the plugin manifest file at path "
201: + pluginManifestFile.getAbsolutePath());
202: }
203: return pluginManifestFile.toURL();
204: }
205: }
|