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 }