001 package railo.runtime.config; 002 003 import java.io.ByteArrayInputStream; 004 import java.io.ByteArrayOutputStream; 005 import java.io.IOException; 006 import java.io.InputStream; 007 import java.util.ArrayList; 008 import java.util.List; 009 import java.util.jar.Attributes; 010 import java.util.jar.Manifest; 011 import java.util.zip.ZipEntry; 012 import java.util.zip.ZipException; 013 import java.util.zip.ZipFile; 014 import java.util.zip.ZipInputStream; 015 016 import railo.print; 017 import railo.commons.io.IOUtil; 018 import railo.commons.io.SystemUtil; 019 import railo.commons.io.compress.ZipUtil; 020 import railo.commons.io.log.LogAndSource; 021 import railo.commons.io.res.Resource; 022 import railo.commons.io.res.filter.ExtensionResourceFilter; 023 import railo.commons.io.res.filter.ResourceFilter; 024 import railo.commons.io.res.util.FileWrapper; 025 import railo.commons.io.res.util.ResourceUtil; 026 import railo.commons.lang.ExceptionUtil; 027 import railo.commons.lang.StringUtil; 028 import railo.commons.lang.SystemOut; 029 import railo.runtime.Info; 030 import railo.runtime.config.Config; 031 import railo.runtime.extension.RHExtension; 032 import railo.runtime.op.Caster; 033 import railo.runtime.op.Decision; 034 import railo.runtime.type.util.ListUtil; 035 036 public class DeployHandler { 037 038 039 040 private static final ResourceFilter ALL_EXT = new ExtensionResourceFilter(new String[]{".re",".ra",".ras"}); 041 //private static final ResourceFilter ARCHIVE_EXT = new ExtensionResourceFilter(new String[]{".ra",".ras"}); 042 043 044 public static void deploy(Config config){ 045 synchronized (config) { 046 Resource dir = getDeployDirectory(config); 047 int ma = Info.getMajorVersion(); 048 int mi = Info.getMinorVersion(); 049 if(!dir.exists()) { 050 if(ma>4 || ma==4 && mi>1) {// FUTURE remove the if contition 051 dir.mkdirs(); 052 } 053 return; 054 } 055 056 Resource[] children = dir.listResources(ALL_EXT); 057 Resource child; 058 String ext; 059 for(int i=0;i<children.length;i++){ 060 child=children[i]; 061 try { 062 // Railo archives 063 ext=ResourceUtil.getExtension(child, null); 064 if("ra".equalsIgnoreCase(ext) || "ras".equalsIgnoreCase(ext)) { 065 deployArchive(config,child); 066 } 067 068 // Railo Extensions 069 else if("re".equalsIgnoreCase(ext)) 070 deployExtension(config, child); 071 } 072 catch (ZipException e) { 073 SystemOut.printDate(config.getErrWriter(),ExceptionUtil.getStacktrace(e, true)); 074 } 075 catch (IOException e) { 076 SystemOut.printDate(config.getErrWriter(),ExceptionUtil.getStacktrace(e, true)); 077 } 078 } 079 } 080 } 081 082 public static Resource getDeployDirectory(Config config) { 083 return config.getConfigDir().getRealResource("deploy"); 084 } 085 086 private static void deployArchive(Config config,Resource archive) throws ZipException, IOException { 087 LogAndSource log = ((ConfigImpl)config).getDeployLogger(); 088 String type=null,virtual=null,name=null; 089 boolean readOnly,topLevel,hidden,physicalFirst; 090 short inspect; 091 InputStream is = null; 092 ZipFile file=null; 093 try { 094 file=new ZipFile(FileWrapper.toFile(archive)); 095 ZipEntry entry = file.getEntry("META-INF/MANIFEST.MF"); 096 097 // no manifest 098 if(entry==null) { 099 log.error("archive","cannot deploy Railo Archive ["+archive+"], file is to old, the file does not have a MANIFEST."); 100 moveToFailedFolder(archive); 101 return; 102 } 103 104 is = file.getInputStream(entry); 105 Manifest manifest = new Manifest(is); 106 Attributes attr = manifest.getMainAttributes(); 107 108 //id = unwrap(attr.getValue("mapping-id")); 109 type = unwrap(attr.getValue("mapping-type")); 110 virtual = unwrap(attr.getValue("mapping-virtual-path")); 111 name = ListUtil.trim(virtual, "/"); 112 readOnly = Caster.toBooleanValue(unwrap(attr.getValue("mapping-readonly")),false); 113 topLevel = Caster.toBooleanValue(unwrap(attr.getValue("mapping-top-level")),false); 114 inspect = ConfigWebUtil.inspectTemplate(unwrap(attr.getValue("mapping-inspect")), ConfigImpl.INSPECT_UNDEFINED); 115 if(inspect==ConfigImpl.INSPECT_UNDEFINED) { 116 Boolean trusted = Caster.toBoolean(unwrap(attr.getValue("mapping-trusted")),null); 117 if(trusted!=null) { 118 if(trusted.booleanValue()) inspect=ConfigImpl.INSPECT_NEVER; 119 else inspect=ConfigImpl.INSPECT_ALWAYS; 120 } 121 } 122 hidden = Caster.toBooleanValue(unwrap(attr.getValue("mapping-hidden")),false); 123 physicalFirst = Caster.toBooleanValue(unwrap(attr.getValue("mapping-physical-first")),false); 124 } 125 finally{ 126 IOUtil.closeEL(is); 127 ZipUtil.close(file); 128 } 129 Resource trgDir = config.getConfigDir().getRealResource("archives").getRealResource(type).getRealResource(name); 130 Resource trgFile = trgDir.getRealResource(archive.getName()); 131 trgDir.mkdirs(); 132 133 // delete existing files 134 135 try { 136 ResourceUtil.deleteContent(trgDir, null); 137 ResourceUtil.moveTo(archive, trgFile); 138 139 log.info("archive","add "+type+" mapping ["+virtual+"] with archive ["+trgFile.getAbsolutePath()+"]"); 140 if("regular".equalsIgnoreCase(type)) 141 ConfigWebAdmin.updateMapping((ConfigImpl)config,virtual, null, trgFile.getAbsolutePath(), "archive", inspect, topLevel); 142 else if("cfc".equalsIgnoreCase(type)) 143 ConfigWebAdmin.updateComponentMapping((ConfigImpl)config,virtual, null, trgFile.getAbsolutePath(), "archive", inspect); 144 else if("ct".equalsIgnoreCase(type)) 145 ConfigWebAdmin.updateCustomTagMapping((ConfigImpl)config,virtual, null, trgFile.getAbsolutePath(), "archive", inspect); 146 147 148 } 149 catch (Throwable t) { 150 moveToFailedFolder(archive); 151 log.error("archive",ExceptionUtil.getStacktrace(t, true)); 152 } 153 } 154 155 156 private static void deployExtension(Config config, Resource ext) { 157 ConfigImpl ci = (ConfigImpl)config; 158 boolean isWeb=config instanceof ConfigWeb; 159 String type=isWeb?"web":"server"; 160 LogAndSource log = ((ConfigImpl)config).getDeployLogger(); 161 162 // Manifest 163 Manifest manifest = null; 164 ZipInputStream zis=null; 165 try { 166 zis = new ZipInputStream( IOUtil.toBufferedInputStream(ext.getInputStream()) ) ; 167 ZipEntry entry; 168 String name; 169 while ( ( entry = zis.getNextEntry()) != null ) { 170 name=entry.getName(); 171 if(!entry.isDirectory() && name.equalsIgnoreCase("META-INF/MANIFEST.MF")) { 172 manifest = toManifest(config,zis,null); 173 } 174 zis.closeEntry() ; 175 } 176 } 177 catch(Throwable t){ 178 log.error("extension", ExceptionUtil.getStacktrace(t, true)); 179 moveToFailedFolder(ext); 180 return; 181 } 182 finally { 183 IOUtil.closeEL(zis); 184 } 185 186 int minCoreVersion=0; 187 double minLoaderVersion=0; 188 String strMinCoreVersion="",strMinLoaderVersion="",version=null,name=null,id=null; 189 190 if(manifest!=null) { 191 Attributes attr = manifest.getMainAttributes(); 192 // version 193 version=unwrap(attr.getValue("version")); 194 195 196 // id 197 id=unwrap(attr.getValue("id")); 198 199 // name 200 name=unwrap(attr.getValue("name")); 201 202 // core version 203 strMinCoreVersion=unwrap(attr.getValue("railo-core-version")); 204 minCoreVersion=Info.toIntVersion(strMinCoreVersion,minCoreVersion); 205 206 // loader version 207 strMinLoaderVersion=unwrap(attr.getValue("railo-loader-version")); 208 minLoaderVersion=Caster.toDoubleValue(strMinLoaderVersion,minLoaderVersion); 209 210 } 211 if(StringUtil.isEmpty(name,true)) { 212 name=ext.getName(); 213 int index=name.lastIndexOf('.'); 214 name=name.substring(0,index-1); 215 } 216 name=name.trim(); 217 218 219 // check core version 220 if(minCoreVersion>Info.getVersionAsInt()) { 221 log.error("extension", "cannot deploy Railo Extension ["+ext+"], Railo Version must be at least ["+strMinCoreVersion+"]."); 222 moveToFailedFolder(ext); 223 return; 224 } 225 226 // check loader version 227 if(minLoaderVersion>SystemUtil.getLoaderVersion()) { 228 log.error("extension", "cannot deploy Railo Extension ["+ext+"], Railo Loader Version must be at least ["+strMinLoaderVersion+"], update the railo.jar first."); 229 moveToFailedFolder(ext); 230 return; 231 } 232 // check id 233 if(!Decision.isUUId(id)) { 234 log.error("extension", "cannot deploy Railo Extension ["+ext+"], this Extension has no valid id ["+id+"],id must be a valid UUID."); 235 moveToFailedFolder(ext); 236 return; 237 } 238 239 240 241 242 Resource trgFile=null; 243 try{ 244 ConfigWebAdmin.removeRHExtension(ci,id); 245 246 Resource trgDir = config.getConfigDir().getRealResource("extensions").getRealResource(type).getRealResource(name); 247 trgFile = trgDir.getRealResource(ext.getName()); 248 trgDir.mkdirs(); 249 ResourceUtil.moveTo(ext, trgFile); 250 } 251 catch(Throwable t){ 252 log.error("extension", ExceptionUtil.getStacktrace(t, true)); 253 moveToFailedFolder(ext); 254 return; 255 } 256 257 try { 258 zis = new ZipInputStream( IOUtil.toBufferedInputStream(trgFile.getInputStream()) ) ; 259 ZipEntry entry; 260 String path; 261 String fileName; 262 List<String> jars=new ArrayList<String>(), flds=new ArrayList<String>(), tlds=new ArrayList<String>(), contexts=new ArrayList<String>(), applications=new ArrayList<String>(); 263 while ( ( entry = zis.getNextEntry()) != null ) { 264 path=entry.getName(); 265 fileName=fileName(entry); 266 // jars 267 if(!entry.isDirectory() && (startsWith(path,type,"jars") || startsWith(path,type,"jar") || startsWith(path,type,"lib") || startsWith(path,type,"libs")) && StringUtil.endsWithIgnoreCase(path, ".jar")) { 268 log.info("extension","deploy jar "+fileName); 269 ConfigWebAdmin.updateJar(config,zis,fileName,false); 270 jars.add(fileName); 271 } 272 273 // flds 274 if(!entry.isDirectory() && startsWith(path,type,"flds") && StringUtil.endsWithIgnoreCase(path, ".fld")) { 275 log.info("extension","deploy fld "+fileName); 276 ConfigWebAdmin.updateFLD(config, zis, fileName,false); 277 flds.add(fileName); 278 } 279 280 // tlds 281 if(!entry.isDirectory() && startsWith(path,type,"tlds") && StringUtil.endsWithIgnoreCase(path, ".tld")) { 282 log.info("extension","deploy tld "+fileName); 283 ConfigWebAdmin.updateTLD(config, zis, fileName,false); 284 tlds.add(fileName); 285 } 286 287 // context 288 String realpath; 289 if(!entry.isDirectory() && startsWith(path,type,"context") && !StringUtil.startsWith(fileName(entry), '.')) { 290 realpath=path.substring(8); 291 //log.info("extension","deploy context "+realpath); 292 log.info("extension","deploy context "+realpath); 293 ConfigWebAdmin.updateContext(ci, zis, realpath,false); 294 contexts.add(realpath); 295 } 296 297 // applications 298 if(!entry.isDirectory() && startsWith(path,type,"applications") && !StringUtil.startsWith(fileName(entry), '.')) { 299 realpath=path.substring(13); 300 //log.info("extension","deploy context "+realpath); 301 log.info("extension","deploy application "+realpath); 302 ConfigWebAdmin.updateApplication(ci, zis, realpath,false); 303 applications.add(realpath); 304 } 305 306 307 zis.closeEntry() ; 308 } 309 310 //installation successfull 311 312 ConfigWebAdmin.updateRHExtension(ci, 313 new RHExtension(id,name,version, 314 jars.toArray(new String[jars.size()]), 315 flds.toArray(new String[flds.size()]), 316 tlds.toArray(new String[tlds.size()]), 317 contexts.toArray(new String[contexts.size()]), 318 applications.toArray(new String[applications.size()]))); 319 320 } 321 catch(Throwable t){ 322 // installation failed 323 324 log.error("extension",ExceptionUtil.getStacktrace(t, true)); 325 moveToFailedFolder(trgFile); 326 return; 327 } 328 finally { 329 IOUtil.closeEL(zis); 330 } 331 } 332 333 private static Manifest toManifest(Config config,InputStream is, Manifest defaultValue) { 334 try { 335 String cs = config.getResourceCharset(); 336 String str = IOUtil.toString(is,cs); 337 if(StringUtil.isEmpty(str,true)) return defaultValue; 338 str=str.trim()+"\n"; 339 return new Manifest(new ByteArrayInputStream(str.getBytes(cs))); 340 } 341 catch (Throwable t) { 342 return defaultValue; 343 } 344 } 345 346 private static boolean startsWith(String path,String type, String name) { 347 return StringUtil.startsWithIgnoreCase(path, name+"/") || StringUtil.startsWithIgnoreCase(path, type+"/"+name+"/"); 348 } 349 350 private static String fileName(ZipEntry entry) { 351 String name = entry.getName(); 352 int index=name.lastIndexOf('/'); 353 if(index==-1) return name; 354 return name.substring(index+1); 355 } 356 357 private static void moveToFailedFolder(Resource archive) { 358 Resource dir = archive.getParentResource().getRealResource("failed-to-deploy"); 359 Resource dst = dir.getRealResource(archive.getName()); 360 dir.mkdirs(); 361 362 try { 363 if(dst.exists()) dst.remove(true); 364 ResourceUtil.moveTo(archive, dst); 365 } 366 catch (Throwable t) {} 367 368 // TODO Auto-generated method stub 369 370 } 371 372 private static String unwrap(String value) { 373 if(value==null) return ""; 374 String res = unwrap(value,'"'); 375 if(res!=null) return res; // was double quote 376 377 return unwrap(value,'\''); // try single quote unwrap, when there is no double quote. 378 } 379 380 private static String unwrap(String value, char del) { 381 value=value.trim(); 382 if(StringUtil.startsWith(value, del) && StringUtil.endsWith(value, del)) { 383 return value.substring(1, value.length()-1); 384 } 385 return value; 386 } 387 388 }