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