001    /**
002     *
003     * Copyright 2004 The Apache Software Foundation
004     *
005     *  Licensed under the Apache License, Version 2.0 (the "License");
006     *  you may not use this file except in compliance with the License.
007     *  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: 394960 $ $Date: 2006-04-18 08:02:36 -0700 (Tue, 18 Apr 2006) $
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                baseJar = jarFile;
057                if (!path.endsWith("/")) {
058                    path += "/";
059                }
060                basePath = path;
061            } else {
062                if (targetEntry instanceof UnpackedJarEntry) {
063                    // for unpacked jars we don't need to copy the jar file
064                    // out to a temp directory, since it is already available
065                    // as a raw file
066                    File targetFile = ((UnpackedJarEntry) targetEntry).getFile();
067                    baseJar = new JarFile(targetFile);
068                    basePath = "";
069                } else {
070                    tempFile = DeploymentUtil.toFile(jarFile, targetEntry.getName());
071                    baseJar = new JarFile(tempFile);
072                    basePath = "";
073                }
074            }
075        }
076    
077        public boolean isUnpacked() {
078            if (isClosed) {
079                throw new IllegalStateException("NestedJarFile is closed");
080            }
081            return basePath.length() > 0;
082        }
083    
084        public boolean isPacked() {
085            if (isClosed) {
086                throw new IllegalStateException("NestedJarFile is closed");
087            }
088            return basePath.length() == 0;
089        }
090    
091        public JarFile getBaseJar() {
092            if (isClosed) {
093                throw new IllegalStateException("NestedJarFile is closed");
094            }
095            return baseJar;
096        }
097    
098        public String getBasePath() {
099            if (isClosed) {
100                throw new IllegalStateException("NestedJarFile is closed");
101            }
102            return basePath;
103        }
104    
105        public Manifest getManifest() throws IOException {
106            if (isClosed) {
107                throw new IllegalStateException("NestedJarFile is closed");
108            }
109    
110            if (!manifestLoaded) {
111                JarEntry manifestEntry = getBaseEntry("META-INF/MANIFEST.MF");
112    
113                if (manifestEntry != null && !manifestEntry.isDirectory()) {
114                    InputStream in = null;
115                    try {
116                        in = baseJar.getInputStream(manifestEntry);
117                        manifest = new Manifest(in);
118                    } finally {
119                        if (in != null) {
120                            try {
121                                in.close();
122                            } catch (IOException e) {
123                                // ignore
124                            }
125                        }
126                    }
127                }
128                manifestLoaded = true;
129            }
130            return manifest;
131        }
132    
133        public NestedJarEntry getNestedJarEntry(String name) {
134            if (isClosed) {
135                throw new IllegalStateException("NestedJarFile is closed");
136            }
137    
138            JarEntry baseEntry = getBaseEntry(name);
139            if (baseEntry == null) {
140                return null;
141            }
142            return new NestedJarEntry(name, baseEntry, getManifestSafe());
143        }
144    
145        public JarEntry getJarEntry(String name) {
146            if (isClosed) {
147                throw new IllegalStateException("NestedJarFile is closed");
148            }
149    
150            return getNestedJarEntry(name);
151        }
152    
153        public ZipEntry getEntry(String name) {
154            if (isClosed) {
155                throw new IllegalStateException("NestedJarFile is closed");
156            }
157    
158            return getNestedJarEntry(name);
159        }
160    
161        public Enumeration entries() {
162            if (isClosed) {
163                throw new IllegalStateException("NestedJarFile is closed");
164            }
165    
166            Collection baseEntries = Collections.list(baseJar.entries());
167            Collection entries = new LinkedList();
168            for (Iterator iterator = baseEntries.iterator(); iterator.hasNext();) {
169                JarEntry baseEntry = (JarEntry) iterator.next();
170                String path = baseEntry.getName();
171                if (path.startsWith(basePath)) {
172                    entries.add(new NestedJarEntry(path.substring(basePath.length()), baseEntry, getManifestSafe()));
173                }
174            }
175            return Collections.enumeration(entries);
176        }
177    
178        public InputStream getInputStream(ZipEntry zipEntry) throws IOException {
179            if (isClosed) {
180                throw new IllegalStateException("NestedJarFile is closed");
181            }
182    
183            JarEntry baseEntry;
184            if (zipEntry instanceof NestedJarEntry) {
185                baseEntry = ((NestedJarEntry)zipEntry).getBaseEntry();
186            } else {
187                baseEntry = getBaseEntry(zipEntry.getName());
188            }
189    
190            if (baseEntry == null) {
191                throw new IOException("Entry not found: name=" + baseEntry.getName());
192            } else if (baseEntry.isDirectory()) {
193                return new DeploymentUtil.EmptyInputStream();
194            }
195            return baseJar.getInputStream(baseEntry);
196        }
197    
198        public String getName() {
199            return baseJar.getName();
200        }
201    
202        /**
203         * Always returns -1.
204         * @return -1
205         */
206        public int size() {
207            if (isClosed) {
208                throw new IllegalStateException("NestedJarFile is closed");
209            }
210            return -1;
211        }
212    
213        public void close() throws IOException {
214            if (isClosed) {
215                return;
216            }
217    
218            try {
219                if (baseJar != null && isPacked()) {
220                    baseJar.close();
221                }
222            } finally {
223                isClosed = true;
224                baseJar = null;
225                basePath = null;
226                manifestLoaded = false;
227                manifest = null;
228                if (tempFile != null) {
229                    tempFile.delete();
230                    tempFile = null;
231                }
232            }
233        }
234    
235        protected void finalize() throws IOException {
236            close();
237        }
238    
239        private JarEntry getBaseEntry(String name) {
240            return baseJar.getJarEntry(basePath + name);
241        }
242    
243        private Manifest getManifestSafe() {
244            Manifest manifest = null;
245            try {
246                manifest = getManifest();
247            } catch (IOException e) {
248                // ignore
249            }
250            return manifest;
251        }
252    
253    }