001 package railo.commons.io.res.type.cache; 002 003 import java.io.ByteArrayInputStream; 004 import java.io.ByteArrayOutputStream; 005 import java.io.IOException; 006 import java.io.InputStream; 007 import java.io.OutputStream; 008 009 import railo.commons.io.ModeUtil; 010 import railo.commons.io.res.ContentType; 011 import railo.commons.io.res.Resource; 012 import railo.commons.io.res.ResourceMetaData; 013 import railo.commons.io.res.ResourceProvider; 014 import railo.commons.io.res.util.ResourceSupport; 015 import railo.commons.io.res.util.ResourceUtil; 016 import railo.runtime.type.Struct; 017 018 019 /** 020 * a ram resource 021 */ 022 public final class CacheResource extends ResourceSupport implements ResourceMetaData { 023 024 private final CacheResourceProvider provider; 025 026 private final String parent; 027 private final String name; 028 //private CacheResourceCore _core; 029 030 CacheResource(CacheResourceProvider provider, String path) { 031 this.provider=provider; 032 if(path.equals("/")) { 033 this.parent=null; 034 this.name=""; 035 } 036 else { 037 String[] pn = ResourceUtil.translatePathName(path); 038 this.parent=pn[0]; 039 this.name=pn[1]; 040 } 041 } 042 043 private CacheResource(CacheResourceProvider provider, String parent,String name) { 044 this.provider=provider; 045 this.parent=parent ; 046 this.name=name; 047 } 048 049 private CacheResourceCore getCore() { 050 return provider.getCore(parent,name); 051 } 052 053 private void removeCore() { 054 provider.removeCore(parent,name); 055 } 056 057 private CacheResourceCore createCore(int type) throws IOException { 058 return provider.createCore(parent,name,type); 059 } 060 061 062 private void touch() { 063 provider.touch(parent,name); 064 } 065 066 /** 067 * @see res.Resource#getPath() 068 */ 069 public String getPath() { 070 return provider.getScheme().concat("://").concat(getInnerPath()); 071 } 072 private String getInnerPath() { 073 if(parent==null) return "/"; 074 return parent.concat(name); 075 } 076 077 /** 078 * @see res.Resource#getFullName() 079 */ 080 public String getName() { 081 return name; 082 } 083 084 /** 085 * @see res.Resource#getParent() 086 */ 087 public String getParent() { 088 if(isRoot()) return null; 089 return provider.getScheme().concat("://").concat(ResourceUtil.translatePath(parent, true, false)); 090 } 091 092 /** 093 * @see res.Resource#isReadable() 094 */ 095 public boolean isReadable() { 096 return ModeUtil.isReadable(getMode()); 097 } 098 099 /** 100 * @see res.Resource#isWriteable() 101 */ 102 public boolean isWriteable() { 103 return ModeUtil.isWritable(getMode()); 104 } 105 106 /** 107 * @see res.Resource#removeE(boolean) 108 */ 109 public void remove(boolean force) throws IOException { 110 if(isRoot()) 111 throw new IOException("can't remove root resource ["+getPath()+"]"); 112 113 provider.read(this); 114 CacheResourceCore core = getCore(); 115 if(core==null) 116 throw new IOException("can't remove resource ["+getPath()+"],resource does not exists"); 117 118 Resource[] children = listResources(); 119 if(children!=null && children.length>0) { 120 if(!force) { 121 throw new IOException("can't delete directory ["+getPath()+"], directory is not empty"); 122 } 123 for(int i=0;i<children.length;i++) { 124 children[i].remove(true); 125 } 126 } 127 removeCore(); 128 } 129 130 /** 131 * @see res.Resource#exists() 132 */ 133 public boolean exists() { 134 try { 135 provider.read(this); 136 } catch (IOException e) { 137 return true; 138 } 139 return getCore()!=null; 140 } 141 142 /** 143 * @see res.Resource#getParentResource() 144 */ 145 public Resource getParentResource() { 146 return getParentRamResource(); 147 } 148 149 150 private CacheResource getParentRamResource() { 151 if(isRoot()) return null; 152 return new CacheResource(provider,parent); 153 } 154 155 public Resource getRealResource(String realpath) { 156 realpath=ResourceUtil.merge(getInnerPath(), realpath); 157 if(realpath.startsWith("../"))return null; 158 159 return new CacheResource(provider,realpath); 160 } 161 162 /** 163 * @see res.Resource#isAbsolute() 164 */ 165 public boolean isAbsolute() { 166 return true; 167 } 168 169 /** 170 * @see res.Resource#isDirectory() 171 */ 172 public boolean isDirectory() { 173 return exists() && getCore().getType()==CacheResourceCore.TYPE_DIRECTORY; 174 } 175 176 /** 177 * @see res.Resource#isFile() 178 */ 179 public boolean isFile() { 180 return exists() && getCore().getType()==CacheResourceCore.TYPE_FILE; 181 } 182 183 /** 184 * @see res.Resource#lastModified() 185 */ 186 public long lastModified() { 187 if(!exists()) return 0; 188 return getCore().getLastModified(); 189 } 190 191 /** 192 * @see res.Resource#length() 193 */ 194 public long length() { 195 if(!exists()) return 0; 196 byte[] data= getCore().getData(); 197 if(data==null) return 0; 198 return data.length; 199 } 200 201 /** 202 * @see res.Resource#list() 203 */ 204 public String[] list() { 205 if(!exists()) return null; 206 207 CacheResourceCore core = getCore(); 208 if(core.getType()!=CacheResourceCore.TYPE_DIRECTORY) 209 return null; 210 211 return provider.getChildNames(getInnerPath()); 212 } 213 214 /** 215 * @see res.Resource#listResources() 216 */ 217 public Resource[] listResources() { 218 String[] list = list(); 219 if(list==null)return null; 220 221 Resource[] children=new Resource[list.length]; 222 String p=getInnerPath(); 223 if(!isRoot())p=p.concat("/"); 224 for(int i=0;i<children.length;i++) { 225 children[i]=new CacheResource(provider,p,list[i]); 226 } 227 return children; 228 } 229 230 /** 231 * @see res.Resource#setLastModified(long) 232 */ 233 public boolean setLastModified(long time) { 234 if(!exists()) return false; 235 getCore().setLastModified(time); 236 return true; 237 } 238 239 /** 240 * @see res.Resource#setReadOnly() 241 */ 242 public boolean setReadOnly() { 243 return setWritable(false); 244 } 245 246 /** 247 * @throws IOException 248 * @see res.Resource#createFile(boolean) 249 */ 250 public void createFile(boolean createParentWhenNotExists) throws IOException { 251 ResourceUtil.checkCreateFileOK(this,createParentWhenNotExists); 252 provider.lock(this); 253 try { 254 createCore(CacheResourceCore.TYPE_FILE); 255 } 256 finally { 257 provider.unlock(this); 258 } 259 } 260 261 262 /** 263 * @see res.Resource#createDirectory(boolean) 264 */ 265 public void createDirectory(boolean createParentWhenNotExists) throws IOException { 266 ResourceUtil.checkCreateDirectoryOK(this,createParentWhenNotExists); 267 provider.lock(this); 268 try { 269 createCore(CacheResourceCore.TYPE_DIRECTORY); 270 } 271 finally { 272 provider.unlock(this); 273 } 274 275 } 276 277 /** 278 * @see res.Resource#getInputStream() 279 */ 280 public InputStream getInputStream() throws IOException { 281 ResourceUtil.checkGetInputStreamOK(this); 282 283 provider.lock(this); 284 CacheResourceCore core = getCore(); 285 286 byte[] data = core.getData(); 287 if(data==null)data=new byte[0]; 288 provider.unlock(this); 289 return new ByteArrayInputStream(data); 290 } 291 292 public OutputStream getOutputStream(boolean append) throws IOException { 293 ResourceUtil.checkGetOutputStreamOK(this); 294 provider.lock(this); 295 return new CacheOutputStream(this,append); 296 } 297 298 /** 299 * 300 * @see railo.commons.io.res.Resource#getContentType() 301 */ 302 public ContentType getContentType() { 303 return ResourceUtil.getContentType(this); 304 } 305 306 /** 307 * @see res.Resource#getResourceProvider() 308 */ 309 public ResourceProvider getResourceProvider() { 310 return provider; 311 } 312 /** 313 * @see java.lang.Object#toString() 314 */ 315 public String toString() { 316 return getPath(); 317 } 318 319 320 /** 321 * This is useed by the MemoryResource too write back data to, that are written to outputstream 322 */ 323 class CacheOutputStream extends ByteArrayOutputStream { 324 325 private CacheResource res; 326 private boolean append; 327 328 /** 329 * Constructor of the class 330 * @param res 331 */ 332 public CacheOutputStream(CacheResource res, boolean append) { 333 this.append=append; 334 this.res=res; 335 } 336 337 /** 338 * @see java.io.ByteArrayOutputStream#close() 339 */ 340 public void close() throws IOException { 341 try { 342 super.close(); 343 CacheResourceCore core = res.getCore(); 344 if(core==null)core=res.createCore(CacheResourceCore.TYPE_FILE); 345 else core.setLastModified(System.currentTimeMillis()); 346 core.setData(this.toByteArray(),append); 347 touch(); 348 } 349 finally { 350 res.getResourceProvider().unlock(res); 351 } 352 } 353 } 354 355 /** 356 * @see railo.commons.io.res.Resource#setReadable(boolean) 357 */ 358 public boolean setReadable(boolean value) { 359 if(!exists())return false; 360 try { 361 setMode(ModeUtil.setReadable(getMode(), value)); 362 return true; 363 } catch (IOException e) { 364 return false; 365 } 366 367 } 368 369 /** 370 * @see railo.commons.io.res.Resource#setWritable(boolean) 371 */ 372 public boolean setWritable(boolean value) { 373 if(!exists())return false; 374 try { 375 setMode(ModeUtil.setWritable(getMode(), value)); 376 return true; 377 } catch (IOException e) { 378 return false; 379 } 380 } 381 382 private boolean isRoot() { 383 return parent==null; 384 } 385 386 public int getMode() { 387 if(!exists())return 0; 388 return getCore().getMode(); 389 } 390 391 public void setMode(int mode) throws IOException { 392 if(!exists())throw new IOException("can't set mode on resource ["+this+"], resource does not exists"); 393 getCore().setMode(mode); 394 } 395 /** 396 * 397 * @see railo.commons.io.res.util.ResourceSupport#getAttribute(short) 398 */ 399 public boolean getAttribute(short attribute) { 400 if(!exists())return false; 401 return (getCore().getAttributes()&attribute)>0; 402 } 403 /** 404 * 405 * @see railo.commons.io.res.util.ResourceSupport#setAttribute(short, boolean) 406 */ 407 public void setAttribute(short attribute, boolean value) throws IOException { 408 if(!exists())throw new IOException("can't get attributes on resource ["+this+"], resource does not exists"); 409 int attr = getCore().getAttributes(); 410 if(value) { 411 if((attr&attribute)==0) attr+=attribute; 412 } 413 else { 414 if((attr&attribute)>0) attr-=attribute; 415 } 416 getCore().setAttributes(attr); 417 } 418 419 public Struct getMetaData() { 420 return provider.getMeta(parent,name); 421 } 422 423 }