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.datasource; 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.IOUtil; 028import lucee.commons.io.ModeUtil; 029import lucee.commons.io.res.Resource; 030import lucee.commons.io.res.ResourceProvider; 031import lucee.commons.io.res.type.datasource.DatasourceResourceProvider.ConnectionData; 032import lucee.commons.io.res.util.ResourceSupport; 033import lucee.commons.io.res.util.ResourceUtil; 034import lucee.commons.lang.ExceptionUtil; 035import lucee.commons.lang.StringUtil; 036import lucee.runtime.exp.PageException; 037import lucee.runtime.exp.PageRuntimeException; 038import lucee.runtime.type.util.ArrayUtil; 039 040public final class DatasourceResource extends ResourceSupport { 041 042 043 044 private DatasourceResourceProvider provider; 045 private String parent; 046 private String name; 047 private ConnectionData data; 048 private int fullPathHash; 049 private int pathHash; 050 051 /** 052 * Constructor of the class 053 * @param provider 054 * @param data 055 * @param path 056 */ 057 DatasourceResource(DatasourceResourceProvider provider, ConnectionData data,String path) { 058 this.provider=provider; 059 this.data=data; 060 if("/".equals(path)) { 061 this.parent=null; 062 this.name=""; 063 } 064 else { 065 String[] pn = ResourceUtil.translatePathName(path); 066 this.parent=pn[0]; 067 this.name=pn[1]; 068 } 069 } 070 071 072 private int fullPathHash() { 073 if(fullPathHash==0) fullPathHash=getInnerPath().hashCode(); 074 return fullPathHash; 075 } 076 077 private int pathHash() { 078 if(pathHash==0 && parent!=null) pathHash=parent.hashCode(); 079 return pathHash; 080 } 081 082 private Attr attr() { 083 return provider.getAttr(data,fullPathHash(),parent,name); 084 } 085 086 087 private boolean isRoot() { 088 return parent==null; 089 } 090 091 @Override 092 public void createDirectory(boolean createParentWhenNotExists) throws IOException { 093 ResourceUtil.checkCreateDirectoryOK(this,createParentWhenNotExists); 094 provider.create(data,fullPathHash(),pathHash(),parent,name,Attr.TYPE_DIRECTORY); 095 096 } 097 098 @Override 099 public void createFile(boolean createParentWhenNotExists) throws IOException { 100 ResourceUtil.checkCreateFileOK(this,createParentWhenNotExists); 101 provider.create(data,fullPathHash(),pathHash(),parent,name,Attr.TYPE_FILE); 102 } 103 104 @Override 105 public void remove(boolean force) throws IOException { 106 ResourceUtil.checkRemoveOK(this); 107 if(isRoot()) 108 throw new IOException("can't remove root resource ["+getPath()+"]"); 109 110 111 Resource[] children = listResources(); 112 if(children!=null && children.length>0) { 113 if(!force) { 114 throw new IOException("can't delete directory ["+getPath()+"], directory is not empty"); 115 } 116 for(int i=0;i<children.length;i++) { 117 children[i].remove(true); 118 } 119 } 120 provider.delete(data,fullPathHash(),parent,name); 121 } 122 123 @Override 124 public boolean exists() { 125 return attr().exists(); 126 } 127 128 @Override 129 public InputStream getInputStream() throws IOException { 130 ResourceUtil.checkGetInputStreamOK(this); 131 return provider.getInputStream(data,fullPathHash(),parent,name); 132 } 133 134 @Override 135 public int getMode() { 136 return attr().getMode(); 137 } 138 139 @Override 140 public String getName() { 141 return name; 142 } 143 144 @Override 145 public OutputStream getOutputStream(boolean append) throws IOException { 146 ResourceUtil.checkGetOutputStreamOK(this); 147 byte[] barr=null; 148 149 if(append && !provider.concatSupported(data) && isFile()){ 150 try{ 151 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 152 IOUtil.copy(getInputStream(), baos,true,true); 153 barr = baos.toByteArray(); 154 } 155 catch(Throwable t){ 156 ExceptionUtil.rethrowIfNecessary(t); 157 } 158 } 159 160 OutputStream os = provider.getOutputStream(data,fullPathHash(),pathHash(),parent,name,append); 161 if(!ArrayUtil.isEmpty(barr))IOUtil.copy(new ByteArrayInputStream(barr), os,true,false); 162 return os; 163 } 164 165 @Override 166 public String getParent() { 167 if(isRoot()) return null; 168 String p = (StringUtil.isEmpty(parent))?"/":parent; 169 return provider.getScheme().concat("://").concat(data.key()).concat(ResourceUtil.translatePath(p, true, false)); 170 171 } 172 173 @Override 174 public Resource getParentResource() { 175 return getParentDatasourceResource(); 176 } 177 private DatasourceResource getParentDatasourceResource() { 178 if(isRoot()) return null; 179 return new DatasourceResource(provider,data,parent); 180 } 181 182 @Override 183 public String getPath() { 184 return provider.getScheme().concat("://").concat(data.key()).concat(getInnerPath()); 185 } 186 private String getInnerPath() { 187 if(parent==null) return "/"; 188 return parent.concat(name); 189 } 190 191 @Override 192 public Resource getRealResource(String realpath) { 193 realpath=ResourceUtil.merge(getInnerPath(), realpath); 194 if(realpath.startsWith("../"))return null; 195 196 return new DatasourceResource(provider,data,realpath); 197 } 198 199 @Override 200 public ResourceProvider getResourceProvider() { 201 return provider; 202 } 203 204 @Override 205 public boolean isAbsolute() { 206 return true; 207 } 208 209 @Override 210 public boolean isDirectory() { 211 return attr().isDirectory(); 212 } 213 214 @Override 215 public boolean isFile() { 216 return attr().isFile(); 217 } 218 219 @Override 220 public boolean isReadable() { 221 return ModeUtil.isReadable(getMode()); 222 } 223 224 @Override 225 public boolean isWriteable() { 226 return ModeUtil.isWritable(getMode()); 227 } 228 229 @Override 230 public long lastModified() { 231 return attr().getLastModified(); 232 } 233 234 @Override 235 public long length() { 236 return attr().size(); 237 } 238 239 @Override 240 public Resource[] listResources() { 241 if(!attr().isDirectory())return null; 242 243 String path; 244 if(parent==null) path= "/"; 245 else path=parent.concat(name).concat("/"); 246 247 248 Attr[] children=null; 249 try { 250 children = provider.getAttrs(data,path.hashCode(),path); 251 } catch (PageException e) { 252 throw new PageRuntimeException(e); 253 } 254 if(children==null) return new Resource[0]; 255 Resource[] attrs = new Resource[children.length]; 256 for(int i=0;i<children.length;i++) { 257 // TODO optimieren, alle attr mitgeben 258 attrs[i]=new DatasourceResource(provider,data,path+children[i].getName()); 259 } 260 return attrs; 261 } 262 263 @Override 264 public boolean setLastModified(long time) { 265 if(!exists()) return false; 266 return provider.setLastModified(data,fullPathHash(),parent,name,time); 267 } 268 269 @Override 270 public void setMode(int mode) throws IOException { 271 if(!exists())throw new IOException("can't set mode on resource ["+this+"], resource does not exist"); 272 provider.setMode(data,fullPathHash(),parent,name,mode); 273 } 274 275 276 @Override 277 public void moveTo(Resource dest) throws IOException { 278 super.moveTo(dest);// TODO 279 } 280 281 282 @Override 283 public boolean setReadable(boolean readable) { 284 if(!exists())return false; 285 try { 286 setMode(ModeUtil.setReadable(getMode(), readable)); 287 return true; 288 } catch (IOException e) { 289 return false; 290 } 291 } 292 293 @Override 294 public boolean setWritable(boolean writable) { 295 if(!exists())return false; 296 try { 297 setMode(ModeUtil.setWritable(getMode(), writable)); 298 return true; 299 } catch (IOException e) { 300 return false; 301 } 302 } 303 304 @Override 305 public String toString() { 306 return getPath(); 307 } 308 309}