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    }