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