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:
018: package org.apache.catalina.startup;
019:
020: import java.io.BufferedOutputStream;
021: import java.io.File;
022: import java.io.FileInputStream;
023: import java.io.FileOutputStream;
024: import java.io.InputStream;
025: import java.io.IOException;
026: import java.net.JarURLConnection;
027: import java.net.URL;
028: import java.nio.channels.FileChannel;
029: import java.util.Enumeration;
030: import java.util.jar.JarEntry;
031: import java.util.jar.JarFile;
032:
033: import org.apache.catalina.Host;
034: import org.apache.catalina.util.StringManager;
035: import org.apache.juli.logging.Log;
036: import org.apache.juli.logging.LogFactory;
037:
038: /**
039: * Expand out a WAR in a Host's appBase.
040: *
041: * @author Craig R. McClanahan
042: * @author Remy Maucherat
043: * @author Glenn L. Nielsen
044: * @version $Revision: 467222 $
045: */
046:
047: public class ExpandWar {
048:
049: private static Log log = LogFactory.getLog(ExpandWar.class);
050:
051: /**
052: * The string resources for this package.
053: */
054: protected static final StringManager sm = StringManager
055: .getManager(Constants.Package);
056:
057: /**
058: * Expand the WAR file found at the specified URL into an unpacked
059: * directory structure, and return the absolute pathname to the expanded
060: * directory.
061: *
062: * @param host Host war is being installed for
063: * @param war URL of the web application archive to be expanded
064: * (must start with "jar:")
065: *
066: * @exception IllegalArgumentException if this is not a "jar:" URL
067: * @exception IOException if an input/output error was encountered
068: * during expansion
069: */
070: public static String expand(Host host, URL war) throws IOException {
071:
072: // Calculate the directory name of the expanded directory
073: if (host.getLogger().isDebugEnabled()) {
074: host.getLogger().debug("expand(" + war.toString() + ")");
075: }
076: String pathname = war.toString().replace('\\', '/');
077: if (pathname.endsWith("!/")) {
078: pathname = pathname.substring(0, pathname.length() - 2);
079: }
080: int period = pathname.lastIndexOf('.');
081: if (period >= pathname.length() - 4)
082: pathname = pathname.substring(0, period);
083: int slash = pathname.lastIndexOf('/');
084: if (slash >= 0) {
085: pathname = pathname.substring(slash + 1);
086: }
087: if (host.getLogger().isDebugEnabled()) {
088: host.getLogger().debug(
089: " Proposed directory name: " + pathname);
090: }
091: return expand(host, war, pathname);
092:
093: }
094:
095: /**
096: * Expand the WAR file found at the specified URL into an unpacked
097: * directory structure, and return the absolute pathname to the expanded
098: * directory.
099: *
100: * @param host Host war is being installed for
101: * @param war URL of the web application archive to be expanded
102: * (must start with "jar:")
103: * @param pathname Context path name for web application
104: *
105: * @exception IllegalArgumentException if this is not a "jar:" URL
106: * @exception IOException if an input/output error was encountered
107: * during expansion
108: */
109: public static String expand(Host host, URL war, String pathname)
110: throws IOException {
111:
112: // Make sure that there is no such directory already existing
113: File appBase = new File(host.getAppBase());
114: if (!appBase.isAbsolute()) {
115: appBase = new File(System.getProperty("catalina.base"),
116: host.getAppBase());
117: }
118: if (!appBase.exists() || !appBase.isDirectory()) {
119: throw new IOException(sm.getString("hostConfig.appBase",
120: appBase.getAbsolutePath()));
121: }
122: File docBase = new File(appBase, pathname);
123: if (docBase.exists()) {
124: // War file is already installed
125: return (docBase.getAbsolutePath());
126: }
127:
128: // Create the new document base directory
129: docBase.mkdir();
130:
131: // Expand the WAR into the new document base directory
132: JarURLConnection juc = (JarURLConnection) war.openConnection();
133: juc.setUseCaches(false);
134: JarFile jarFile = null;
135: InputStream input = null;
136: try {
137: jarFile = juc.getJarFile();
138: Enumeration jarEntries = jarFile.entries();
139: while (jarEntries.hasMoreElements()) {
140: JarEntry jarEntry = (JarEntry) jarEntries.nextElement();
141: String name = jarEntry.getName();
142: int last = name.lastIndexOf('/');
143: if (last >= 0) {
144: File parent = new File(docBase, name.substring(0,
145: last));
146: parent.mkdirs();
147: }
148: if (name.endsWith("/")) {
149: continue;
150: }
151: input = jarFile.getInputStream(jarEntry);
152:
153: // Bugzilla 33636
154: File expandedFile = expand(input, docBase, name);
155: long lastModified = jarEntry.getTime();
156: if ((lastModified != -1) && (lastModified != 0)
157: && (expandedFile != null)) {
158: expandedFile.setLastModified(lastModified);
159: }
160:
161: input.close();
162: input = null;
163: }
164: } catch (IOException e) {
165: // If something went wrong, delete expanded dir to keep things
166: // clean
167: deleteDir(docBase);
168: throw e;
169: } finally {
170: if (input != null) {
171: try {
172: input.close();
173: } catch (Throwable t) {
174: ;
175: }
176: input = null;
177: }
178: if (jarFile != null) {
179: try {
180: jarFile.close();
181: } catch (Throwable t) {
182: ;
183: }
184: jarFile = null;
185: }
186: }
187:
188: // Return the absolute path to our new document base directory
189: return (docBase.getAbsolutePath());
190:
191: }
192:
193: /**
194: * Copy the specified file or directory to the destination.
195: *
196: * @param src File object representing the source
197: * @param dest File object representing the destination
198: */
199: public static boolean copy(File src, File dest) {
200:
201: boolean result = true;
202:
203: String files[] = null;
204: if (src.isDirectory()) {
205: files = src.list();
206: result = dest.mkdir();
207: } else {
208: files = new String[1];
209: files[0] = "";
210: }
211: if (files == null) {
212: files = new String[0];
213: }
214: for (int i = 0; (i < files.length) && result; i++) {
215: File fileSrc = new File(src, files[i]);
216: File fileDest = new File(dest, files[i]);
217: if (fileSrc.isDirectory()) {
218: result = copy(fileSrc, fileDest);
219: } else {
220: FileChannel ic = null;
221: FileChannel oc = null;
222: try {
223: ic = (new FileInputStream(fileSrc)).getChannel();
224: oc = (new FileOutputStream(fileDest)).getChannel();
225: ic.transferTo(0, ic.size(), oc);
226: } catch (IOException e) {
227: log.error(sm.getString("expandWar.copy", fileSrc,
228: fileDest), e);
229: result = false;
230: } finally {
231: if (ic != null) {
232: try {
233: ic.close();
234: } catch (IOException e) {
235: }
236: }
237: if (oc != null) {
238: try {
239: oc.close();
240: } catch (IOException e) {
241: }
242: }
243: }
244: }
245: }
246: return result;
247:
248: }
249:
250: /**
251: * Delete the specified directory, including all of its contents and
252: * subdirectories recursively.
253: *
254: * @param dir File object representing the directory to be deleted
255: */
256: public static boolean delete(File dir) {
257: if (dir.isDirectory()) {
258: return deleteDir(dir);
259: } else {
260: return dir.delete();
261: }
262: }
263:
264: /**
265: * Delete the specified directory, including all of its contents and
266: * subdirectories recursively.
267: *
268: * @param dir File object representing the directory to be deleted
269: */
270: public static boolean deleteDir(File dir) {
271:
272: String files[] = dir.list();
273: if (files == null) {
274: files = new String[0];
275: }
276: for (int i = 0; i < files.length; i++) {
277: File file = new File(dir, files[i]);
278: if (file.isDirectory()) {
279: deleteDir(file);
280: } else {
281: file.delete();
282: }
283: }
284: return dir.delete();
285:
286: }
287:
288: /**
289: * Expand the specified input stream into the specified directory, creating
290: * a file named from the specified relative path.
291: *
292: * @param input InputStream to be copied
293: * @param docBase Document base directory into which we are expanding
294: * @param name Relative pathname of the file to be created
295: * @return A handle to the expanded File
296: *
297: * @exception IOException if an input/output error occurs
298: */
299: protected static File expand(InputStream input, File docBase,
300: String name) throws IOException {
301:
302: File file = new File(docBase, name);
303: BufferedOutputStream output = null;
304: try {
305: output = new BufferedOutputStream(
306: new FileOutputStream(file));
307: byte buffer[] = new byte[2048];
308: while (true) {
309: int n = input.read(buffer);
310: if (n <= 0)
311: break;
312: output.write(buffer, 0, n);
313: }
314: } finally {
315: if (output != null) {
316: try {
317: output.close();
318: } catch (IOException e) {
319: // Ignore
320: }
321: }
322: }
323:
324: return file;
325: }
326:
327: }
|