1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
38
39
40
41 public class ZipFilePluginLoader extends BasePluginLoader {
42 private static final Logger LOG = Logger.getLogger(ZipFilePluginLoader.class);
43
44 private final File pluginZipFile;
45 private final File extractionDirectory;
46 private long zipFileLastModified = -1;
47 private boolean loadFailed = false;
48
49 private static String validatePluginZipFile(File pluginZipFile) {
50 PluginUtils.validatePluginZipFile(pluginZipFile);
51 String fileName = pluginZipFile.getName();
52 int indexOf = fileName.lastIndexOf(".zip");
53 return fileName.substring(0, indexOf);
54 }
55
56 public ZipFilePluginLoader(File pluginZipFile, File sharedPluginDirectory, ClassLoader parentClassLoader, Config parentConfig) {
57 super(validatePluginZipFile(pluginZipFile), sharedPluginDirectory, parentClassLoader, parentConfig);
58 this.pluginZipFile = pluginZipFile;
59 this.extractionDirectory = determineExtractionDirectory(getSimplePluginName(), pluginZipFile);
60 }
61
62 public boolean isModified() {
63 long currentZipFileLastModified = pluginZipFile.lastModified();
64 if (zipFileLastModified == -1) {
65 zipFileLastModified = currentZipFileLastModified;
66 return false;
67 } else if (currentZipFileLastModified > zipFileLastModified) {
68 return !isZipFileStillBeingModified();
69 }
70 return false;
71 }
72
73 protected boolean isZipFileStillBeingModified() {
74 long lastModified = pluginZipFile.lastModified();
75 long size = pluginZipFile.length();
76
77 try {
78 Thread.sleep(100);
79 } catch (InterruptedException e) {}
80 if (lastModified != pluginZipFile.lastModified()) {
81 return true;
82 }
83 if (size != pluginZipFile.length()) {
84 return true;
85 }
86 return false;
87 }
88
89 public boolean isRemoved() {
90 return pluginZipFile != null && !pluginZipFile.exists();
91 }
92
93 @Override
94 public Plugin load() throws Exception {
95 try {
96 updateLastModified();
97 extractPluginFiles();
98 Plugin plugin = super.load();
99 loadFailed = false;
100 return plugin;
101 } catch (Exception e) {
102 loadFailed = true;
103 throw e;
104 }
105 }
106
107 protected File determineExtractionDirectory(String pluginName, File pluginZipFile) {
108 return new File(pluginZipFile.getParentFile(), pluginName);
109 }
110
111
112
113
114 protected void extractPluginFiles() throws Exception {
115 if (isExtractNeeded()) {
116
117 if (extractionDirectory.exists()) {
118
119 FileUtils.deleteDirectory(extractionDirectory);
120 }
121 if (!extractionDirectory.mkdir()) {
122 throw new WorkflowRuntimeException("Could not create the extraction directory for the plugin: " + extractionDirectory.getAbsolutePath());
123 }
124 ZipFile zipFile = new ZipFile(pluginZipFile, ZipFile.OPEN_READ);
125 for (Enumeration entries = zipFile.entries(); entries.hasMoreElements();) {
126 ZipEntry entry = (ZipEntry)entries.nextElement();
127 File entryFile = new File(extractionDirectory + java.io.File.separator + entry.getName());
128 if (entry.isDirectory()) {
129 if (!entryFile.mkdir()) {
130 throw new WorkflowRuntimeException("Failed to create directory: " + entryFile.getAbsolutePath());
131 }
132 continue;
133 }
134 InputStream is = null;
135 OutputStream os = null;
136 try {
137 is = new BufferedInputStream(zipFile.getInputStream(entry));
138 os = new BufferedOutputStream(new FileOutputStream(entryFile));
139 while (is.available() > 0) {
140 os.write(is.read());
141 }
142 } finally {
143 if (os != null) {
144 os.close();
145 }
146 if (is != null) {
147 is.close();
148 }
149 }
150 }
151 }
152 }
153
154
155
156
157 public boolean isLoadFailed() {
158 return this.loadFailed;
159 }
160
161
162
163
164
165 protected boolean isExtractNeeded() {
166 return isModified() || pluginZipFile.lastModified() > extractionDirectory.lastModified();
167 }
168
169 protected void updateLastModified() {
170 zipFileLastModified = pluginZipFile.lastModified();
171 }
172
173 protected PluginClassLoader createPluginClassLoader() throws MalformedURLException {
174 LOG.info(getLogPrefix() + " Initiating loading of plugin from file system: " + extractionDirectory.getPath());
175 LOG.info(getLogPrefix() + " Absolute path on file system is: " + extractionDirectory.getAbsolutePath());
176
177
178
179 return new PluginClassLoader(parentClassLoader, sharedPluginDirectory, extractionDirectory);
180 }
181
182 protected URL getPluginConfigURL() throws PluginException, MalformedURLException {
183 File pluginConfigFile = new File(extractionDirectory, pluginConfigPath);
184 if (!pluginConfigFile.exists() || !pluginConfigFile.isFile()) {
185 throw new PluginException(getLogPrefix() + " Could not locate the plugin config file at path " + pluginConfigFile.getAbsolutePath());
186 }
187 return pluginConfigFile.toURI().toURL();
188 }
189 }