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 }