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