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.geronimo.deployment.util;
018
019 import java.io.File;
020 import java.io.IOException;
021 import java.io.InputStream;
022 import java.util.Collection;
023 import java.util.Collections;
024 import java.util.Enumeration;
025 import java.util.Iterator;
026 import java.util.LinkedList;
027 import java.util.jar.JarEntry;
028 import java.util.jar.JarFile;
029 import java.util.jar.Manifest;
030 import java.util.zip.ZipEntry;
031
032 /**
033 * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
034 */
035 public class NestedJarFile extends JarFile {
036 private JarFile baseJar;
037 private String basePath;
038 private boolean isClosed = false;
039 private boolean manifestLoaded = false;
040 private Manifest manifest;
041 private File tempFile;
042
043 public NestedJarFile(JarFile jarFile, String path) throws IOException {
044 super(DeploymentUtil.DUMMY_JAR_FILE);
045
046 // verify that the jar actually contains that path
047 JarEntry targetEntry = jarFile.getJarEntry(path + "/");
048 if (targetEntry == null) {
049 targetEntry = jarFile.getJarEntry(path);
050 if (targetEntry == null) {
051 throw new IOException("Jar entry does not exist: jarFile=" + jarFile.getName() + ", path=" + path);
052 }
053 }
054
055 if (targetEntry.isDirectory()) {
056 if(targetEntry instanceof UnpackedJarEntry) {
057 //unpacked nested module inside unpacked ear
058 File targetFile = ((UnpackedJarEntry) targetEntry).getFile();
059 baseJar = new UnpackedJarFile(targetFile);
060 basePath = "";
061 } else {
062 baseJar = jarFile;
063 if (!path.endsWith("/")) {
064 path += "/";
065 }
066 basePath = path;
067 }
068 } else {
069 if (targetEntry instanceof UnpackedJarEntry) {
070 // for unpacked jars we don't need to copy the jar file
071 // out to a temp directory, since it is already available
072 // as a raw file
073 File targetFile = ((UnpackedJarEntry) targetEntry).getFile();
074 baseJar = new JarFile(targetFile);
075 basePath = "";
076 } else {
077 tempFile = DeploymentUtil.toFile(jarFile, targetEntry.getName());
078 baseJar = new JarFile(tempFile);
079 basePath = "";
080 }
081 }
082 }
083
084 public boolean isUnpacked() {
085 if (isClosed) {
086 throw new IllegalStateException("NestedJarFile is closed");
087 }
088
089 return ( basePath.length() > 0 ) ||
090 ( ( baseJar != null ) && ( baseJar instanceof UnpackedJarFile ) );
091 }
092
093 public boolean isPacked() {
094 if (isClosed) {
095 throw new IllegalStateException("NestedJarFile is closed");
096 }
097
098 return ( basePath.length() == 0 ) &&
099 ( ( baseJar == null ) || !( baseJar instanceof UnpackedJarFile ) );
100 }
101
102 public JarFile getBaseJar() {
103 if (isClosed) {
104 throw new IllegalStateException("NestedJarFile is closed");
105 }
106 return baseJar;
107 }
108
109 public String getBasePath() {
110 if (isClosed) {
111 throw new IllegalStateException("NestedJarFile is closed");
112 }
113 return basePath;
114 }
115
116 public Manifest getManifest() throws IOException {
117 if (isClosed) {
118 throw new IllegalStateException("NestedJarFile is closed");
119 }
120
121 if (!manifestLoaded) {
122 JarEntry manifestEntry = getBaseEntry("META-INF/MANIFEST.MF");
123
124 if (manifestEntry != null && !manifestEntry.isDirectory()) {
125 InputStream in = null;
126 try {
127 in = baseJar.getInputStream(manifestEntry);
128 manifest = new Manifest(in);
129 } finally {
130 if (in != null) {
131 try {
132 in.close();
133 } catch (IOException e) {
134 // ignore
135 }
136 }
137 }
138 }
139 manifestLoaded = true;
140 }
141 return manifest;
142 }
143
144 public NestedJarEntry getNestedJarEntry(String name) {
145 if (isClosed) {
146 throw new IllegalStateException("NestedJarFile is closed");
147 }
148
149 JarEntry baseEntry = getBaseEntry(name);
150 if (baseEntry == null) {
151 return null;
152 }
153 return new NestedJarEntry(name, baseEntry, getManifestSafe());
154 }
155
156 public JarEntry getJarEntry(String name) {
157 if (isClosed) {
158 throw new IllegalStateException("NestedJarFile is closed");
159 }
160
161 return getNestedJarEntry(name);
162 }
163
164 public ZipEntry getEntry(String name) {
165 if (isClosed) {
166 throw new IllegalStateException("NestedJarFile is closed");
167 }
168
169 return getNestedJarEntry(name);
170 }
171
172 public Enumeration entries() {
173 if (isClosed) {
174 throw new IllegalStateException("NestedJarFile is closed");
175 }
176
177 Collection baseEntries = Collections.list(baseJar.entries());
178 Collection entries = new LinkedList();
179 for (Iterator iterator = baseEntries.iterator(); iterator.hasNext();) {
180 JarEntry baseEntry = (JarEntry) iterator.next();
181 String path = baseEntry.getName();
182 if (path.startsWith(basePath)) {
183 entries.add(new NestedJarEntry(path.substring(basePath.length()), baseEntry, getManifestSafe()));
184 }
185 }
186 return Collections.enumeration(entries);
187 }
188
189 public InputStream getInputStream(ZipEntry zipEntry) throws IOException {
190 if (isClosed) {
191 throw new IllegalStateException("NestedJarFile is closed");
192 }
193
194 JarEntry baseEntry;
195 if (zipEntry instanceof NestedJarEntry) {
196 baseEntry = ((NestedJarEntry)zipEntry).getBaseEntry();
197 } else {
198 baseEntry = getBaseEntry(zipEntry.getName());
199 }
200
201 if (baseEntry == null) {
202 throw new IOException("Entry not found: name=" + baseEntry.getName());
203 } else if (baseEntry.isDirectory()) {
204 return new DeploymentUtil.EmptyInputStream();
205 }
206 return baseJar.getInputStream(baseEntry);
207 }
208
209 public String getName() {
210 return baseJar.getName();
211 }
212
213 /**
214 * Always returns -1.
215 * @return -1
216 */
217 public int size() {
218 if (isClosed) {
219 throw new IllegalStateException("NestedJarFile is closed");
220 }
221 return -1;
222 }
223
224 public void close() throws IOException {
225 if (isClosed) {
226 return;
227 }
228
229 try {
230 try {
231 super.close();
232 } catch(IOException ignored) {
233 }
234 if (baseJar != null && basePath.length() == 0) {
235 // baseJar is created by us. We should be closing it too.
236 baseJar.close();
237 }
238 } finally {
239 isClosed = true;
240 baseJar = null;
241 basePath = null;
242 manifestLoaded = false;
243 manifest = null;
244 if (tempFile != null) {
245 tempFile.delete();
246 tempFile = null;
247 }
248 }
249 }
250
251 protected void finalize() throws IOException {
252 close();
253 }
254
255 private JarEntry getBaseEntry(String name) {
256 return baseJar.getJarEntry(basePath + name);
257 }
258
259 private Manifest getManifestSafe() {
260 Manifest manifest = null;
261 try {
262 manifest = getManifest();
263 } catch (IOException e) {
264 // ignore
265 }
266 return manifest;
267 }
268
269 }