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    }