001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.commons.io.res.type.compress; 020 021import java.io.IOException; 022import java.io.InputStream; 023import java.util.HashMap; 024import java.util.Map; 025import java.util.zip.GZIPOutputStream; 026import java.util.zip.ZipOutputStream; 027 028import lucee.commons.digest.MD5; 029import lucee.commons.io.CompressUtil; 030import lucee.commons.io.IOUtil; 031import lucee.commons.io.SystemUtil; 032import lucee.commons.io.res.Resource; 033import lucee.commons.io.res.ResourceProvider; 034import lucee.commons.io.res.type.ram.RamResourceProviderOld; 035import lucee.commons.io.res.util.ResourceUtil; 036import lucee.commons.lang.ExceptionUtil; 037import lucee.runtime.config.Config; 038import lucee.runtime.config.ConfigImpl; 039import lucee.runtime.engine.ThreadLocalPageContext; 040import lucee.runtime.op.Caster; 041 042import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; 043 044public final class Compress { 045 046 public static final int FORMAT_ZIP = CompressUtil.FORMAT_ZIP; 047 public static final int FORMAT_TAR = CompressUtil.FORMAT_TAR; 048 public static final int FORMAT_TGZ = CompressUtil.FORMAT_TGZ; 049 public static final int FORMAT_TBZ2 = CompressUtil.FORMAT_TBZ2; 050 051 052 //private final static Map files=new WeakHashMap(); 053 054 private final Resource ffile; 055 //private ResourceProvider ramProvider; 056 private long syn=-1; 057 private Resource root; 058 private Synchronizer synchronizer; 059 private long lastMod=-1; 060 private long lastCheck=-1; 061 062 private int format; 063 private int mode; 064 private boolean caseSensitive; 065 private Resource temp; 066 067 /** 068 * private Constructor of the class, will be invoked be getInstance 069 * @param file 070 * @param format 071 * @param caseSensitive 072 */ 073 public Compress(Resource file, int format, boolean caseSensitive) { 074 this.ffile=file; 075 this.format=format; 076 this.mode=ffile.getMode(); 077 if(mode==0) mode=0777; 078 load(this.caseSensitive=caseSensitive); 079 } 080 081 /** 082 * return zip instance matching the zipfile, singelton isnatce only 1 zip for one file 083 * @param zipFile 084 * @param format 085 * @param caseSensitive 086 * @return 087 */ 088 /*public static Compress getInstance(Resource zipFile, int format, boolean caseSensitive) { 089 Compress compress=(Compress) files.get(zipFile.getPath()); 090 if(compress==null) { 091 compress=new Compress(zipFile,format,caseSensitive); 092 files.put(zipFile.getPath(), compress); 093 } 094 return compress;ConfigImpl 095 }*/ 096 public static Compress getInstance(Resource zipFile, int format, boolean caseSensitive) { 097 ConfigImpl config=(ConfigImpl) ThreadLocalPageContext.getConfig(); 098 return config.getCompressInstance(zipFile, format, caseSensitive); 099 } 100 101 102 private synchronized void load(boolean caseSensitive) { 103 long actLastMod = ffile.lastModified(); 104 lastMod=actLastMod; 105 lastCheck=System.currentTimeMillis(); 106 Map args = new HashMap(); 107 args.put("case-sensitive", Caster.toBoolean(caseSensitive)); 108 //ramProvider = new RamResourceProviderOld().init("ram",args); 109 //root=ramProvider.getResource("/"); 110 111 if(temp==null){ 112 String cid=""; 113 try { 114 Config config = ThreadLocalPageContext.getConfig(); 115 if(config!=null){ 116 cid=config.getId(); 117 temp = config.getTempDirectory(); 118 } 119 if(temp==null)temp=SystemUtil.getTempDirectory(); 120 121 temp=temp.getRealResource("compress"); 122 temp=temp.getRealResource(MD5.getDigestAsString(cid+"-"+ffile.getAbsolutePath())); 123 if(!temp.exists())temp.createDirectory(true); 124 125 } 126 catch(Throwable t){ 127 ExceptionUtil.rethrowIfNecessary(t); 128 } 129 } 130 131 132 if(temp!=null) { 133 String name=Caster.toString(actLastMod)+":"+Caster.toString(ffile.length()); 134 name=MD5.getDigestAsString(name,name); 135 root=temp.getRealResource(name); 136 if(actLastMod>0 && root.exists()) return; 137 138 139 ResourceUtil.removeChildrenEL(temp); 140 //if(root!=null)ResourceUtil.removeChildrenEL(root); 141 //String name=CreateUUID.invoke(); 142 //root=temp.getRealResource(name); 143 root.mkdirs(); 144 } 145 else { 146 ResourceProvider ramProvider = new RamResourceProviderOld().init("ram",args); 147 root=ramProvider.getResource("/"); 148 } 149 _load(); 150 } 151 152 153 154 private void _load() { 155 if(ffile.exists()) { 156 try { 157 CompressUtil.extract(format, ffile, root); 158 } catch (IOException e) {} 159 } 160 else { 161 try { 162 ffile.createFile(false); 163 } 164 catch (IOException e) {} 165 lastMod=ffile.lastModified(); 166 } 167 } 168 169 public Resource getRamProviderResource(String path) { 170 long t=System.currentTimeMillis(); 171 if(t>lastCheck+2000){ 172 173 lastCheck=t; 174 t=ffile.lastModified(); 175 if((lastMod-t)>10 || (t-lastMod)>10 || root==null || !root.exists()){ 176 lastMod=t; 177 load(caseSensitive); 178 } 179 } 180 return root.getRealResource(path);//ramProvider.getResource(path); 181 } 182 183 /** 184 * @return the zipFile 185 */ 186 public Resource getCompressFile() { 187 return ffile; 188 } 189 190 public synchronized void synchronize(boolean async) { 191 if(!async) { 192 doSynchronize(); 193 return; 194 } 195 syn=System.currentTimeMillis(); 196 if(synchronizer==null || !synchronizer.isRunning()) { 197 synchronizer=new Synchronizer(this,100); 198 synchronizer.start(); 199 } 200 } 201 202 private void doSynchronize() { 203 try { 204 CompressUtil.compress(format, root.listResources(), ffile, 777); 205 //ramProvider=null; 206 } 207 catch (IOException e) {} 208 } 209 210 class Synchronizer extends Thread { 211 private Compress zip; 212 private int interval; 213 private boolean running=true; 214 215 public Synchronizer(Compress zip, int interval) { 216 this.zip=zip; 217 this.interval=interval; 218 } 219 220 public void run() { 221 if(FORMAT_TAR==format) runTar(ffile); 222 if(FORMAT_TGZ==format) runTGZ(ffile); 223 else runZip(ffile); 224 225 } 226 227 private void runTGZ(Resource res) { 228 GZIPOutputStream gos=null; 229 InputStream tmpis=null; 230 Resource tmp = SystemUtil.getTempDirectory().getRealResource(System.currentTimeMillis()+"_.tgz"); 231 try { 232 gos=new GZIPOutputStream(res.getOutputStream()); 233 // wait for sync 234 while(true) { 235 sleepEL(); 236 if(zip.syn+interval<=System.currentTimeMillis()) break; 237 } 238 // sync 239 tmpis = tmp.getInputStream(); 240 CompressUtil.compressTar(root.listResources(), tmp, -1); 241 CompressUtil.compressGZip(tmpis, gos); 242 } 243 catch (IOException e) {} 244 finally { 245 IOUtil.closeEL(gos); 246 IOUtil.closeEL(tmpis); 247 tmp.delete(); 248 running=false; 249 } 250 } 251 private void runTar(Resource res) { 252 TarArchiveOutputStream tos=null; 253 try { 254 tos=new TarArchiveOutputStream(res.getOutputStream()); 255 tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU); 256 // wait for sync 257 while(true) { 258 sleepEL(); 259 if(zip.syn+interval<=System.currentTimeMillis()) break; 260 } 261 // sync 262 CompressUtil.compressTar(root.listResources(), tos, -1); 263 } 264 catch (IOException e) {} 265 finally { 266 IOUtil.closeEL(tos); 267 running=false; 268 } 269 } 270 271 private void runZip(Resource res) { 272 ZipOutputStream zos=null; 273 try { 274 zos=new ZipOutputStream(res.getOutputStream()); 275 // wait for sync 276 while(true) { 277 sleepEL(); 278 if(zip.syn+interval<=System.currentTimeMillis()) break; 279 } 280 // sync 281 CompressUtil.compressZip(root.listResources(), zos, null); 282 } 283 catch (IOException e) {} 284 finally { 285 IOUtil.closeEL(zos); 286 running=false; 287 } 288 } 289 290 private void sleepEL() { 291 try { 292 sleep(interval); 293 } 294 catch (InterruptedException e) {} 295 } 296 297 public boolean isRunning() { 298 return running; 299 } 300 } 301}