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    }