| /* |
| * Copyright 2004-2005 The Apache Software Foundation or its licensors, |
| * as applicable. |
| * |
| * Licensed 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.jackrabbit.base; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.security.AccessControlException; |
| import java.util.regex.Pattern; |
| |
| import javax.jcr.Credentials; |
| import javax.jcr.Item; |
| import javax.jcr.ItemNotFoundException; |
| import javax.jcr.ItemVisitor; |
| import javax.jcr.NamespaceException; |
| import javax.jcr.Node; |
| import javax.jcr.NodeIterator; |
| import javax.jcr.PathNotFoundException; |
| import javax.jcr.Repository; |
| import javax.jcr.RepositoryException; |
| import javax.jcr.Session; |
| import javax.jcr.UnsupportedRepositoryOperationException; |
| import javax.jcr.ValueFactory; |
| import javax.jcr.Workspace; |
| import javax.jcr.query.Query; |
| import javax.jcr.query.QueryResult; |
| import javax.xml.transform.Transformer; |
| import javax.xml.transform.TransformerConfigurationException; |
| import javax.xml.transform.TransformerException; |
| import javax.xml.transform.TransformerFactory; |
| import javax.xml.transform.sax.SAXResult; |
| import javax.xml.transform.sax.SAXTransformerFactory; |
| import javax.xml.transform.sax.TransformerHandler; |
| import javax.xml.transform.stream.StreamResult; |
| import javax.xml.transform.stream.StreamSource; |
| |
| import org.apache.jackrabbit.name.QName; |
| import org.apache.jackrabbit.xml.DocumentViewExportVisitor; |
| import org.apache.jackrabbit.xml.SystemViewExportVisitor; |
| import org.xml.sax.ContentHandler; |
| import org.xml.sax.SAXException; |
| |
| /** |
| * Session base class. The dummy session implemented by this class |
| * attempts to act like a read-only anonymous session. Subclasses must |
| * override at least the {@link #getRepository() getRepository()}, |
| * {@link #getWorkspace() getWorkspace()}, |
| * {@link #getRootNode() getRootNode()}, and |
| * {@link #getValueFactory() getValueFactory()} methods to make this class |
| * somewhat useful. See the method javadocs for full details of which |
| * methods to override for each required feature. |
| */ |
| public class BaseSession implements Session { |
| |
| /** The pattern used to match UUID strings. */ |
| private static final Pattern UUID_PATTERN = Pattern.compile( |
| "[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}"); |
| |
| /** |
| * Unsupported operation. Subclasses should override this method to |
| * return the repository to which this session is attached. |
| * Overiding this method is required for the default implementation of the |
| * {@link #impersonate(Credentials) impersonate(Credentials)} method. |
| * |
| * @return nothing, throws a {@link UnsupportedOperationException} |
| * @see Session#getRepository() |
| */ |
| public Repository getRepository() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * Returns <code>null</code> to indicate that this session is associated |
| * with the "anonymous" user. Subclasses should override this method to |
| * return the actual user identity associated with the session. |
| * |
| * @return always <code>null</code> |
| * @see Session#getUserID() |
| */ |
| public String getUserID() { |
| return null; |
| } |
| |
| /** |
| * Returns <code>null</code> to indicate that the named attribute does |
| * not exist. Subclasses should override this method to return the |
| * available attribute values. |
| * |
| * @param name attribute name |
| * @return always <code>null</code> |
| * @see Session#getAttribute(String) |
| */ |
| public Object getAttribute(String name) { |
| return null; |
| } |
| |
| /** |
| * Returns an empty string array to indicate that no attributes are |
| * available. Subclasses should override this method to return the |
| * available attribute names. |
| * |
| * @return empty array |
| * @see Session#getAttributeNames() |
| */ |
| public String[] getAttributeNames() { |
| return new String[0]; |
| } |
| |
| /** |
| * Unsupported operation. Subclasses should override this method to |
| * return the workspace attached to this session. Overiding this method |
| * is required for the default implementations of the |
| * {@link #impersonate(Credentials) impersonate(Credentials)} and |
| * {@link #getNodeByUUID(String) getNodeByUUID(String)} methods. |
| * |
| * @return nothing, throws a {@link UnsupportedOperationException} |
| * @see Session#getWorkspace() |
| */ |
| public Workspace getWorkspace() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * Implemented by calling the |
| * {@link Repository#login(Credentials, String) login(Credentials, String)} |
| * method of the {@link Repository} instance returned by the |
| * {@link #getRepository() getRepository()} method. The method is invoked |
| * with the given login credentials and the workspace name returned by |
| * the {@link Workspace#getName() getName()} method of the |
| * {@link Workspace} instance returned by the |
| * {@link #getWorkspace() getWorkspace()} method. |
| * <p> |
| * There should normally be little need for subclasses to override this |
| * method unless the underlying repository implementation suggests a |
| * more straightforward implementation. |
| * |
| * @param credentials login credentials |
| * @return impersonated session |
| * @see Session#impersonate(Credentials) |
| */ |
| public Session impersonate(Credentials credentials) |
| throws RepositoryException { |
| return getRepository().login(credentials, getWorkspace().getName()); |
| } |
| |
| /** |
| * Unsupported operation. Subclasses should override this method to |
| * return the root node in the workspace attached to this session. |
| * Overriding this method is required for the default implementation of |
| * the {@link #getItem(String) getItem(String)} method. |
| * |
| * @return nothing, throws a {@link UnsupportedRepositoryOperationException} |
| * @see Session#getRootNode() |
| */ |
| public Node getRootNode() throws RepositoryException { |
| throw new UnsupportedRepositoryOperationException(); |
| } |
| |
| /** |
| * Implemented by making the XPath query <code>//*[@jcr:uuid='...']</code> |
| * and returning the node that matches the query. Subclasses may want to |
| * override this method if an UUID index or another more efficient |
| * lookup mechanism can be used directly. |
| * |
| * @return the identified node |
| * @throws ItemNotFoundException if the identified node does not exist |
| * @see Session#getNodeByUUID(String) |
| */ |
| public Node getNodeByUUID(String uuid) throws RepositoryException { |
| if (UUID_PATTERN.matcher(uuid).matches()) { |
| String xpath; |
| String jcr = getNamespacePrefix(QName.NS_JCR_URI); |
| if (jcr.length() > 0) { |
| xpath = "//*[@" + jcr + ":uuid='" + uuid + "']"; |
| } else { |
| xpath = "//*[@uuid='" + uuid + "']"; |
| } |
| Query query = |
| getWorkspace().getQueryManager().createQuery(Query.XPATH, xpath); |
| QueryResult result = query.execute(); |
| NodeIterator nodes = result.getNodes(); |
| if (nodes.hasNext()) { |
| return nodes.nextNode(); |
| } |
| } |
| throw new ItemNotFoundException(uuid); |
| } |
| |
| /** |
| * Implemented by invoking the {@link Node#getNode(String) getNode(String)} |
| * (or {@link Node#getProperty(String) getProperty(String)} if getNode |
| * fails) method on the root node returned by the |
| * {@link #getRootNode() getRootNode()} method. The path given to the |
| * getNode or getProperty method is the given path without the leading "/". |
| * If the given path is "/" then the root node is returned directly. |
| * <p> |
| * Subclasses should not normally need to override this method as long as |
| * the referenced methods have been implemented. For performance reasons |
| * it might make sense to override this method because this implementation |
| * can cause the item path to be traversed twice. |
| * |
| * @param absPath absolute item path |
| * @return identified item |
| * @see Session#getItem(String) |
| */ |
| public Item getItem(String absPath) throws PathNotFoundException, |
| RepositoryException { |
| if (absPath == null || !absPath.startsWith("/")) { |
| throw new PathNotFoundException("Invalid item path: " + absPath); |
| } |
| |
| Node node = getRootNode(); |
| if (absPath.equals("/")) { |
| return node; |
| } else { |
| String relPath = absPath.substring(1); |
| try { |
| return node.getNode(relPath); |
| } catch (PathNotFoundException e) { |
| return node.getProperty(relPath); |
| } |
| } |
| } |
| |
| /** |
| * Implemented by trying to retrieve the identified item using the |
| * {@link #getItem(String) getItem(String)} method. Subclasses may |
| * want to override this method for performance as there is no real |
| * need for instantiating the identified item. |
| * |
| * @param absPath absolute item path |
| * @return <code>true</code> if the identified item exists, |
| * <code>false</code> otherwise |
| * @see Session#itemExists(String) |
| */ |
| public boolean itemExists(String absPath) throws RepositoryException { |
| try { |
| getItem(absPath); |
| return true; |
| } catch (PathNotFoundException e) { |
| return false; |
| } |
| } |
| |
| /** |
| * Unsupported operation. Subclasses should override this method to |
| * make it possible to move and rename items. |
| * |
| * @param srcAbsPath source item path |
| * @param destAbsPath destination item path |
| * @see Session#move(String, String) |
| */ |
| public void move(String srcAbsPath, String destAbsPath) |
| throws RepositoryException { |
| throw new UnsupportedRepositoryOperationException(); |
| } |
| |
| /** |
| * Unsupported operation. Subclasses should override this method to |
| * allow modifications to the content repository. |
| * |
| * @see Session#save() |
| */ |
| public void save() throws RepositoryException { |
| throw new UnsupportedRepositoryOperationException(); |
| } |
| |
| /** |
| * Does nothing. Subclasses should override this method to correctly |
| * manage the transient state and underlying repository changes. |
| * |
| * @param keepChanges whether to keep transient changes (ignored) |
| * @see Session#refresh(boolean) |
| */ |
| public void refresh(boolean keepChanges) throws RepositoryException { |
| } |
| |
| /** |
| * Returns <code>false</code> to indicate that there are no pending |
| * changes. Subclasses should override this method to correctly manage |
| * the transient state. |
| * |
| * @return always <code>false</code> |
| * @see Session#hasPendingChanges() |
| */ |
| public boolean hasPendingChanges() throws RepositoryException { |
| return false; |
| } |
| |
| /** |
| * Throws an {@link AccessControlException} for anything else than |
| * <code>read</code> actions to indicate that only read access is |
| * permitted. Subclasses should override this method to correctly |
| * report access controls settings. |
| * |
| * @param absPath item path |
| * @param actions action strings |
| * @throws AccessControlException if other than <code>read</code> actions |
| * are requested |
| * @see Session#checkPermission(String, String) |
| */ |
| public void checkPermission(String absPath, String actions) |
| throws AccessControlException { |
| String[] parts = actions.split(","); |
| for (int i = 0; i < parts.length; i++) { |
| if (!"read".equals(parts[i])) { |
| throw new AccessControlException( |
| "No " + actions + " permission for " + absPath); |
| } |
| } |
| } |
| |
| /** |
| * Unsupported operation. Subclasses should override this method to |
| * support XML imports. Overriding this method is required for the |
| * default implementation of the |
| * {@link #importXML(String, InputStream, int) importXML(String, InputStream, int)} |
| * method. |
| * |
| * @param parentAbsPath path of the parent node |
| * @param uuidBehaviour UUID behaviour flag |
| * @return nothing, throws an {@link UnsupportedRepositoryOperationException} |
| * @see Session#getImportContentHandler(String, int) |
| */ |
| public ContentHandler getImportContentHandler( |
| String parentAbsPath, int uuidBehaviour) |
| throws RepositoryException { |
| throw new UnsupportedRepositoryOperationException(); |
| } |
| |
| /** |
| * Parses the XML input stream and feeds the SAX events to the content |
| * handler returned by the |
| * {@link #getImportContentHandler(String, int) getImportContentHandler(String, int)} |
| * method. |
| * |
| * @param parentAbsPath path of the parent node |
| * @param in XML input stream |
| * @param uuidBehaviour UUID behaviour flag |
| * @see Session#importXML(String, InputStream, int) |
| */ |
| public void importXML( |
| String parentAbsPath, InputStream in, int uuidBehaviour) |
| throws IOException, RepositoryException { |
| try { |
| ContentHandler handler = |
| getImportContentHandler(parentAbsPath, uuidBehaviour); |
| |
| TransformerFactory factory = TransformerFactory.newInstance(); |
| Transformer transformer = factory.newTransformer(); |
| transformer.transform(new StreamSource(in), new SAXResult(handler)); |
| } catch (TransformerConfigurationException e) { |
| throw new IOException( |
| "Unable to configure a SAX transformer: " + e.getMessage()); |
| } catch (TransformerException e) { |
| throw new IOException( |
| "Unable to deserialize a SAX stream: " + e.getMessage()); |
| } |
| } |
| |
| /** |
| * Creates a {@link SystemViewExportVisitor} instance and passes it |
| * to the node identified by the given path. The export visitor traverses |
| * the entire content tree and generates the system view SAX events for |
| * the given content handler. Subclasses may override this method for |
| * better performance or custom processing, but the default implementation |
| * should be good enough for general use. |
| * |
| * @param absPath node path |
| * @param contentHandler SAX content handler |
| * @param skipBinary binary property skip flag |
| * @param noRecurse subtree recursion flag |
| * @see Session#exportSystemView(String, ContentHandler, boolean, boolean) |
| */ |
| public void exportSystemView( |
| String absPath, ContentHandler contentHandler, |
| boolean skipBinary, boolean noRecurse) |
| throws SAXException, RepositoryException { |
| Item item = getItem(absPath); |
| if (item.isNode()) { |
| ItemVisitor visitor = new SystemViewExportVisitor( |
| contentHandler, skipBinary, noRecurse); |
| item.accept(visitor); |
| } else { |
| throw new PathNotFoundException("Invalid node path: " + absPath); |
| } |
| } |
| |
| /** |
| * Creates a SAX serializer for the given output stream and passes it to the |
| * {@link #exportSystemView(String, ContentHandler, boolean, boolean) exportSystemView(String, ContentHandler, boolean, boolean)} |
| * method along with the other parameters. |
| * |
| * @param absPath node path |
| * @param out XML output stream |
| * @param skipBinary binary property skip flag |
| * @param noRecurse subtree recursion flag |
| * @see Session#exportSystemView(String, OutputStream, boolean, boolean) |
| */ |
| public void exportSystemView( |
| String absPath, OutputStream out, |
| boolean skipBinary, boolean noRecurse) |
| throws IOException, RepositoryException { |
| try { |
| SAXTransformerFactory factory = (SAXTransformerFactory) |
| SAXTransformerFactory.newInstance(); |
| TransformerHandler handler = factory.newTransformerHandler(); |
| handler.setResult(new StreamResult(out)); |
| exportSystemView(absPath, handler, skipBinary, noRecurse); |
| } catch (TransformerConfigurationException e) { |
| throw new IOException( |
| "Unable to configure a SAX transformer: " + e.getMessage()); |
| } catch (SAXException e) { |
| throw new IOException( |
| "Unable to serialize a SAX stream: " + e.getMessage()); |
| } |
| } |
| |
| /** |
| * Creates a {@link DocumentViewExportVisitor} instance and passes it |
| * to the node identified by the given path. The export visitor traverses |
| * the entire content tree and generates the document view SAX events for |
| * the given content handler. Subclasses may override this method for |
| * better performance or custom processing, but the default implementation |
| * should be good enough for general use. |
| * |
| * @param absPath node path |
| * @param contentHandler SAX content handler |
| * @param skipBinary binary property skip flag |
| * @param noRecurse subtree recursion flag |
| * @see Session#exportDocumentView(String, ContentHandler, boolean, boolean) |
| */ |
| public void exportDocumentView( |
| String absPath, ContentHandler contentHandler, |
| boolean skipBinary, boolean noRecurse) |
| throws SAXException, RepositoryException { |
| Item item = getItem(absPath); |
| if (item.isNode()) { |
| ItemVisitor visitor = new DocumentViewExportVisitor( |
| contentHandler, skipBinary, noRecurse); |
| item.accept(visitor); |
| } else { |
| throw new PathNotFoundException("Invalid node path: " + absPath); |
| } |
| } |
| |
| /** |
| * Creates a SAX serializer for the given output stream and passes it to the |
| * {@link #exportDocumentView(String, ContentHandler, boolean, boolean) exportDocumentView(String, ContentHandler, boolean, boolean)} |
| * method along with the other parameters. |
| * |
| * @param absPath node path |
| * @param out XML output stream |
| * @param skipBinary binary property skip flag |
| * @param noRecurse subtree recursion flag |
| * @see Session#exportDocumentView(String, OutputStream, boolean, boolean) |
| */ |
| public void exportDocumentView( |
| String absPath, OutputStream out, |
| boolean skipBinary, boolean noRecurse) |
| throws IOException, RepositoryException { |
| try { |
| SAXTransformerFactory factory = (SAXTransformerFactory) |
| SAXTransformerFactory.newInstance(); |
| TransformerHandler handler = factory.newTransformerHandler(); |
| handler.setResult(new StreamResult(out)); |
| exportDocumentView(absPath, handler, skipBinary, noRecurse); |
| } catch (TransformerConfigurationException e) { |
| throw new IOException( |
| "Unable to configure a SAX transformer: " + e.getMessage()); |
| } catch (SAXException e) { |
| throw new IOException( |
| "Unable to serialize a SAX stream: " + e.getMessage()); |
| } |
| } |
| |
| /** |
| * Unsupported operation. Subclasses should override this method to |
| * support namespace remapping. |
| * |
| * @param prefix namespace prefix |
| * @param uri namespace uri |
| * @see Session#setNamespacePrefix(String, String) |
| */ |
| public void setNamespacePrefix(String prefix, String uri) |
| throws NamespaceException, RepositoryException { |
| throw new UnsupportedRepositoryOperationException(); |
| } |
| |
| /** |
| * Returns the namespace prefixes registered in the |
| * {@link javax.jcr.NamespaceRegistry} associated with the workspace of |
| * this session. Subclasses should override this method to support |
| * namespace remapping. |
| * |
| * @return namespace prefixes |
| * @see Session#getNamespacePrefixes() |
| */ |
| public String[] getNamespacePrefixes() throws RepositoryException { |
| return getWorkspace().getNamespaceRegistry().getPrefixes(); |
| } |
| |
| /** |
| * Returns the namespace URI registered for the given prefix in the |
| * {@link javax.jcr.NamespaceRegistry} associated with the workspace of |
| * this session. Subclasses should override this method to support |
| * namespace remapping. |
| * |
| * @param prefix namespace prefix |
| * @return namespace URI |
| * @see Session#getNamespaceURI(String) |
| */ |
| public String getNamespaceURI(String prefix) throws RepositoryException { |
| return getWorkspace().getNamespaceRegistry().getURI(prefix); |
| } |
| |
| /** |
| * Returns the namespace prefix registered for the given URI in the |
| * {@link javax.jcr.NamespaceRegistry} associated with the workspace of |
| * this session. Subclasses should override this method to support |
| * namespace remapping. |
| * |
| * @param uri namespace URI |
| * @return namespace prefix |
| * @see Session#getNamespacePrefix(String) |
| */ |
| public String getNamespacePrefix(String uri) throws RepositoryException { |
| return getWorkspace().getNamespaceRegistry().getPrefix(uri); |
| } |
| |
| /** |
| * Does nothing. Subclasses should override this method to actually |
| * close this session. |
| * |
| * @see Session#logout() |
| */ |
| public void logout() { |
| } |
| |
| /** |
| * Unsupported operation. Subclasses should override this method to support |
| * lock token management. |
| * |
| * @param lock token |
| * @see Session#addLockToken(String) |
| */ |
| public void addLockToken(String lt) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * Returns an empty string array to indicate that no lock tokens are held |
| * by this session. Subclasses should override this method to return the |
| * actual lock tokens held by this session. |
| * |
| * @return empty array |
| * @see Session#getLockTokens() |
| */ |
| public String[] getLockTokens() { |
| return new String[0]; |
| } |
| |
| /** |
| * Unsupported operation. Subclasses should override this method to support |
| * lock token management. |
| * |
| * @param lock token |
| * @see Session#removeLockToken(String) |
| */ |
| public void removeLockToken(String lt) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * Unsupported operation. Subclasses should override this method to |
| * allow the creation of new {@link javax.jcr.Value Value} instances. |
| * |
| * @param lock token |
| * @see Session#removeLockToken(String) |
| */ |
| public ValueFactory getValueFactory() |
| throws UnsupportedRepositoryOperationException, RepositoryException { |
| throw new UnsupportedRepositoryOperationException(); |
| } |
| |
| /** |
| * Returns <code>true</code> to indicate that the session has not been |
| * closed. Subclasses should override this method to correctly report |
| * the state of the session. |
| * |
| * @return always <code>true</code> |
| * @see Session#isLive() |
| */ |
| public boolean isLive() { |
| return true; |
| } |
| |
| } |