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: 520533 $ $Date: 2007-03-20 15:36:30 -0400 (Tue, 20 Mar 2007) $
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 return basePath.length() > 0;
089 }
090
091 public boolean isPacked() {
092 if (isClosed) {
093 throw new IllegalStateException("NestedJarFile is closed");
094 }
095 return basePath.length() == 0;
096 }
097
098 public JarFile getBaseJar() {
099 if (isClosed) {
100 throw new IllegalStateException("NestedJarFile is closed");
101 }
102 return baseJar;
103 }
104
105 public String getBasePath() {
106 if (isClosed) {
107 throw new IllegalStateException("NestedJarFile is closed");
108 }
109 return basePath;
110 }
111
112 public Manifest getManifest() throws IOException {
113 if (isClosed) {
114 throw new IllegalStateException("NestedJarFile is closed");
115 }
116
117 if (!manifestLoaded) {
118 JarEntry manifestEntry = getBaseEntry("META-INF/MANIFEST.MF");
119
120 if (manifestEntry != null && !manifestEntry.isDirectory()) {
121 InputStream in = null;
122 try {
123 in = baseJar.getInputStream(manifestEntry);
124 manifest = new Manifest(in);
125 } finally {
126 if (in != null) {
127 try {
128 in.close();
129 } catch (IOException e) {
130 // ignore
131 }
132 }
133 }
134 }
135 manifestLoaded = true;
136 }
137 return manifest;
138 }
139
140 public NestedJarEntry getNestedJarEntry(String name) {
141 if (isClosed) {
142 throw new IllegalStateException("NestedJarFile is closed");
143 }
144
145 JarEntry baseEntry = getBaseEntry(name);
146 if (baseEntry == null) {
147 return null;
148 }
149 return new NestedJarEntry(name, baseEntry, getManifestSafe());
150 }
151
152 public JarEntry getJarEntry(String name) {
153 if (isClosed) {
154 throw new IllegalStateException("NestedJarFile is closed");
155 }
156
157 return getNestedJarEntry(name);
158 }
159
160 public ZipEntry getEntry(String name) {
161 if (isClosed) {
162 throw new IllegalStateException("NestedJarFile is closed");
163 }
164
165 return getNestedJarEntry(name);
166 }
167
168 public Enumeration entries() {
169 if (isClosed) {
170 throw new IllegalStateException("NestedJarFile is closed");
171 }
172
173 Collection baseEntries = Collections.list(baseJar.entries());
174 Collection entries = new LinkedList();
175 for (Iterator iterator = baseEntries.iterator(); iterator.hasNext();) {
176 JarEntry baseEntry = (JarEntry) iterator.next();
177 String path = baseEntry.getName();
178 if (path.startsWith(basePath)) {
179 entries.add(new NestedJarEntry(path.substring(basePath.length()), baseEntry, getManifestSafe()));
180 }
181 }
182 return Collections.enumeration(entries);
183 }
184
185 public InputStream getInputStream(ZipEntry zipEntry) throws IOException {
186 if (isClosed) {
187 throw new IllegalStateException("NestedJarFile is closed");
188 }
189
190 JarEntry baseEntry;
191 if (zipEntry instanceof NestedJarEntry) {
192 baseEntry = ((NestedJarEntry)zipEntry).getBaseEntry();
193 } else {
194 baseEntry = getBaseEntry(zipEntry.getName());
195 }
196
197 if (baseEntry == null) {
198 throw new IOException("Entry not found: name=" + baseEntry.getName());
199 } else if (baseEntry.isDirectory()) {
200 return new DeploymentUtil.EmptyInputStream();
201 }
202 return baseJar.getInputStream(baseEntry);
203 }
204
205 public String getName() {
206 return baseJar.getName();
207 }
208
209 /**
210 * Always returns -1.
211 * @return -1
212 */
213 public int size() {
214 if (isClosed) {
215 throw new IllegalStateException("NestedJarFile is closed");
216 }
217 return -1;
218 }
219
220 public void close() throws IOException {
221 if (isClosed) {
222 return;
223 }
224
225 try {
226 if (baseJar != null && isPacked()) {
227 baseJar.close();
228 }
229 } finally {
230 isClosed = true;
231 baseJar = null;
232 basePath = null;
233 manifestLoaded = false;
234 manifest = null;
235 if (tempFile != null) {
236 tempFile.delete();
237 tempFile = null;
238 }
239 }
240 }
241
242 protected void finalize() throws IOException {
243 close();
244 }
245
246 private JarEntry getBaseEntry(String name) {
247 return baseJar.getJarEntry(basePath + name);
248 }
249
250 private Manifest getManifestSafe() {
251 Manifest manifest = null;
252 try {
253 manifest = getManifest();
254 } catch (IOException e) {
255 // ignore
256 }
257 return manifest;
258 }
259
260 }