| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.sling.scripting.jsp; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.Set; |
| |
| import javax.jcr.Item; |
| import javax.jcr.Node; |
| import javax.jcr.RepositoryException; |
| import javax.jcr.Session; |
| |
| import org.apache.sling.api.SlingException; |
| import org.apache.sling.api.resource.Resource; |
| import org.apache.sling.api.resource.ResourceMetadata; |
| import org.apache.sling.api.resource.ResourceResolver; |
| import org.apache.sling.jcr.api.SlingRepository; |
| import org.apache.sling.scripting.jsp.jasper.IOProvider; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * The <code>SlingIOProvider</code> TODO |
| */ |
| class SlingIOProvider implements IOProvider { |
| |
| /** default log */ |
| private static final Logger log = LoggerFactory.getLogger(SlingIOProvider.class); |
| |
| private final SlingRepository repository; |
| |
| private ThreadLocal<ResourceResolver> requestResourceResolver; |
| |
| // private session for write access |
| private ThreadLocal<Session> privateSession; |
| |
| SlingIOProvider(SlingRepository repository) { |
| this.repository = repository; |
| this.requestResourceResolver = new ThreadLocal<ResourceResolver>(); |
| this.privateSession = new ThreadLocal<Session>(); |
| } |
| |
| void setRequestResourceResolver(ResourceResolver resolver) { |
| requestResourceResolver.set(resolver); |
| } |
| |
| void resetRequestResourceResolver() { |
| requestResourceResolver.remove(); |
| |
| // at the same time logout this thread's session |
| Session session = privateSession.get(); |
| if (session != null) { |
| if (session.isLive()) { |
| session.logout(); |
| } |
| privateSession.remove(); |
| } |
| } |
| |
| // ---------- IOProvider interface ----------------------------------------- |
| |
| /** |
| * Returns an InputStream for the file name which is looked up with the |
| * ResourceProvider and retrieved from the Resource if the StreamProvider |
| * interface is implemented. |
| */ |
| public InputStream getInputStream(String fileName) |
| throws FileNotFoundException, IOException { |
| |
| try { |
| |
| Resource resource = getResourceInternal(fileName); |
| if (resource == null) { |
| throw new FileNotFoundException("Cannot find " + fileName); |
| } |
| |
| InputStream stream = resource.adaptTo(InputStream.class); |
| if (stream == null) { |
| throw new FileNotFoundException("Cannot find " + fileName); |
| } |
| |
| return stream; |
| |
| } catch (SlingException se) { |
| throw (IOException) new IOException( |
| "Failed to get InputStream for " + fileName).initCause(se); |
| } |
| } |
| |
| /** |
| * Returns the value of the last modified meta data field of the resource |
| * found at file name or zero if the meta data field is not set. If the |
| * resource does not exist or an error occurrs finding the resource, -1 is |
| * returned. |
| */ |
| public long lastModified(String fileName) { |
| try { |
| Resource resource = getResourceInternal(fileName); |
| if (resource != null) { |
| ResourceMetadata meta = resource.getResourceMetadata(); |
| long modTime = meta.getModificationTime(); |
| return (modTime > 0) ? modTime : 0; |
| } |
| |
| } catch (SlingException se) { |
| log.error("Cannot get last modification time for " + fileName, se); |
| } |
| |
| // fallback to "non-existant" in case of problems |
| return -1; |
| } |
| |
| /** |
| * Removes the named item from the repository. |
| */ |
| public boolean delete(String fileName) { |
| Node parentNode = null; |
| try { |
| fileName = cleanPath(fileName); |
| Session session = getPrivateSession(); |
| if (session.itemExists(fileName)) { |
| Item fileItem = session.getItem(fileName); |
| parentNode = fileItem.getParent(); |
| fileItem.remove(); |
| parentNode.save(); |
| return true; |
| } |
| } catch (RepositoryException re) { |
| log.error("Cannot remove " + fileName, re); |
| } finally { |
| checkNode(parentNode, fileName); |
| } |
| |
| // fall back to false if item does not exist or in case of error |
| return false; |
| } |
| |
| /** |
| * Returns an output stream to write to the repository. |
| */ |
| public OutputStream getOutputStream(String fileName) { |
| fileName = cleanPath(fileName); |
| return new RepositoryOutputStream(this, fileName); |
| } |
| |
| /** |
| * Renames a node in the repository. |
| */ |
| public boolean rename(String oldFileName, String newFileName) { |
| try { |
| oldFileName = cleanPath(oldFileName); |
| newFileName = cleanPath(newFileName); |
| |
| Session session = getPrivateSession(); |
| session.getWorkspace().move(oldFileName, newFileName); |
| return true; |
| } catch (RepositoryException re) { |
| log.error("Cannot rename " + oldFileName + " to " + newFileName, re); |
| } |
| |
| // fallback to false in case of error or non-existence of oldFileName |
| return false; |
| } |
| |
| /** |
| * Creates a folder hierarchy in the repository. |
| */ |
| public boolean mkdirs(String path) { |
| Node parentNode = null; |
| try { |
| Session session = getPrivateSession(); |
| |
| // quick test |
| path = cleanPath(path); |
| if (session.itemExists(path) && session.getItem(path).isNode()) { |
| return true; |
| } |
| |
| // check path walking it down |
| Node current = session.getRootNode(); |
| String[] names = path.split("/"); |
| for (int i = 0; i < names.length; i++) { |
| if (names[i] == null || names[i].length() == 0) { |
| continue; |
| } else if (current.hasNode(names[i])) { |
| current = current.getNode(names[i]); |
| } else { |
| if (parentNode == null) { |
| parentNode = current; |
| } |
| current = current.addNode(names[i], "nt:folder"); |
| } |
| } |
| |
| if (parentNode != null) { |
| parentNode.save(); |
| return true; |
| } |
| |
| } catch (RepositoryException re) { |
| log.error("Cannot create folder path " + path, re); |
| } finally { |
| checkNode(parentNode, path); |
| } |
| |
| // false in case of error or no need to create |
| return false; |
| } |
| |
| // ---------- Helper Methods for JspServletContext ------------------------- |
| |
| /* package */URL getURL(String path) throws MalformedURLException { |
| try { |
| Resource resource = getResourceInternal(path); |
| return (resource != null) ? resource.adaptTo(URL.class) : null; |
| } catch (SlingException se) { |
| throw (MalformedURLException) new MalformedURLException( |
| "Cannot get URL for " + path).initCause(se); |
| } |
| } |
| |
| /* package */Set<String> getResourcePaths(String path) { |
| Set<String> paths = new HashSet<String>(); |
| |
| ResourceResolver resolver = requestResourceResolver.get(); |
| if (resolver != null) { |
| try { |
| Resource resource = resolver.getResource(cleanPath(path)); |
| if (resource != null) { |
| Iterator<Resource> entries = resolver.listChildren(resource); |
| while (entries.hasNext()) { |
| paths.add(entries.next().getPath()); |
| } |
| } |
| } catch (SlingException se) { |
| log.warn("getResourcePaths: Cannot list children of " + path, |
| se); |
| } |
| } |
| |
| return paths.isEmpty() ? null : paths; |
| } |
| |
| private Resource getResourceInternal(String path) throws SlingException { |
| ResourceResolver resolver = requestResourceResolver.get(); |
| if (resolver != null) { |
| return resolver.getResource(cleanPath(path)); |
| } |
| |
| return null; |
| } |
| |
| // ---------- internal ----------------------------------------------------- |
| |
| private Session getPrivateSession() throws RepositoryException { |
| Session session = privateSession.get(); |
| if (session == null) { |
| session = repository.loginAdministrative(null); |
| privateSession.set(session); |
| } |
| |
| return session; |
| } |
| |
| private static void checkNode(Node node, String path) { |
| if (node != null && node.isModified()) { |
| try { |
| node.refresh(false); |
| } catch (RepositoryException re) { |
| log.error("Cannot refresh node for " + path |
| + " after failed save", re); |
| } |
| } |
| } |
| |
| private String cleanPath(String path) { |
| // replace backslash by slash |
| path = path.replace('\\', '/'); |
| |
| // cut off trailing slash |
| while (path.endsWith("/")) { |
| path = path.substring(0, path.length() - 1); |
| } |
| |
| return path; |
| } |
| |
| private static class RepositoryOutputStream extends ByteArrayOutputStream { |
| |
| private final SlingIOProvider repositoryOutputProvider; |
| |
| private final String fileName; |
| |
| RepositoryOutputStream(SlingIOProvider repositoryOutputProvider, |
| String fileName) { |
| this.repositoryOutputProvider = repositoryOutputProvider; |
| this.fileName = fileName; |
| } |
| |
| public void close() throws IOException { |
| super.close(); |
| |
| Node parentNode = null; |
| try { |
| Session session = repositoryOutputProvider.getPrivateSession(); |
| Node fileNode = null; |
| Node contentNode = null; |
| if (session.itemExists(fileName)) { |
| Item item = session.getItem(fileName); |
| if (item.isNode()) { |
| Node node = item.isNode() |
| ? (Node) item |
| : item.getParent(); |
| if ("jcr:content".equals(node.getName())) { |
| // replace the content properties of the jcr:content |
| // node |
| parentNode = node; |
| contentNode = node; |
| } else if (node.isNodeType("nt:file")) { |
| // try to set the content properties of jcr:content |
| // node |
| parentNode = node; |
| contentNode = node.getNode("jcr:content"); |
| } else { // fileName is a node |
| // try to set the content properties of the node |
| parentNode = node; |
| contentNode = node; |
| } |
| } else { |
| // replace property with an nt:file node (if possible) |
| parentNode = item.getParent(); |
| String name = item.getName(); |
| fileNode = parentNode.addNode(name, "nt:file"); |
| item.remove(); |
| } |
| } else { |
| int lastSlash = fileName.lastIndexOf('/'); |
| if (lastSlash <= 0) { |
| parentNode = session.getRootNode(); |
| } else { |
| Item parent = session.getItem(fileName.substring(0, |
| lastSlash)); |
| if (!parent.isNode()) { |
| // TODO: fail |
| } |
| parentNode = (Node) parent; |
| } |
| String name = fileName.substring(lastSlash + 1); |
| fileNode = parentNode.addNode(name, "nt:file"); |
| } |
| |
| // if we have a file node, create the contentNode |
| if (fileNode != null) { |
| contentNode = fileNode.addNode("jcr:content", "nt:resource"); |
| } |
| |
| contentNode.setProperty("jcr:lastModified", |
| System.currentTimeMillis()); |
| contentNode.setProperty("jcr:data", new ByteArrayInputStream( |
| buf, 0, size())); |
| contentNode.setProperty("jcr:mimeType", |
| "application/octet-stream"); |
| |
| parentNode.save(); |
| } catch (RepositoryException re) { |
| log.error("Cannot write file " + fileName, re); |
| throw new IOException("Cannot write file " + fileName |
| + ", reason: " + re.toString()); |
| } finally { |
| checkNode(parentNode, fileName); |
| } |
| } |
| } |
| } |