// Copyright (C) 2006-2009 Google Inc. // // 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 com.google.enterprise.connector.mock.jcr; import com.google.enterprise.connector.mock.MockRepositoryDocument; import com.google.enterprise.connector.mock.MockRepositoryProperty; import com.google.enterprise.connector.spi.Document; import com.google.enterprise.connector.spi.SpiConstants; import java.io.FileNotFoundException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.logging.Logger; import javax.jcr.Item; import javax.jcr.ItemVisitor; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.Property; import javax.jcr.PropertyIterator; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.UnsupportedRepositoryOperationException; import javax.jcr.Value; import javax.jcr.lock.Lock; import javax.jcr.nodetype.NodeDefinition; import javax.jcr.nodetype.NodeType; import javax.jcr.version.Version; import javax.jcr.version.VersionHistory; /** * MockJcrNode implements the corresponding JCR interface, with these * limitations: * <ul> * <li> This is a "level 1" (read-only) implementation. All level 2 * (side-effecting) calls throw UnsupportedOperation exceptions. These are * grouped at the bottom of the class implementation. * <li> Some level 1 calls are not implemented because they will never be used * by our connector infrastructure. Eventually, these will be documented as part * of framework documentation. In this implementation, they also throw * UnsupportedOperation exceptions. These are grouped above the level 2 calls. * <li> Some level 1 calls are not currently needed by our implementation, but * may be soon. These are marked with todos and throw UnsupportedOperation * exceptions. * </ul> */ public class MockJcrNode implements Node { private static Set<String> propertySkipSet; private static Logger LOGGER = Logger.getLogger(MockJcrNode.class.getName()); static { propertySkipSet = new HashSet<String>(); propertySkipSet.add("acl"); propertySkipSet.add("acldeny"); } private MockRepositoryDocument doc; private List<MockJcrProperty> propList = null; private Property findProperty(String name) { for (MockJcrProperty prop : propList) { if (prop.getName().equals(name)) { return prop; } } return null; } private void init() { propList = new LinkedList<MockJcrProperty>(); // Convert the special MockRepositoryDocument schema to a JCR property list MockRepositoryProperty p = null; // content try { p = new MockRepositoryProperty("jcr:content", doc.getContentStream()); } catch (FileNotFoundException e) { LOGGER.severe(e.toString()); } if (p != null) { propList.add(new MockJcrProperty(p)); } // modified date p = new MockRepositoryProperty("jcr:lastModified", MockRepositoryProperty.PropertyType.DATE, Integer.toString(doc .getTimeStamp().getTicks())); propList.add(new MockJcrProperty(p)); // docid p = new MockRepositoryProperty("jcr:uuid", doc.getDocID()); propList.add(new MockJcrProperty(p)); // acl MockRepositoryProperty aclProp = doc.getProplist().getProperty("acl"); if (aclProp != null) { addAclProperty(MockRepositoryProperty.USER_SCOPE, aclProp, propList, SpiConstants.PROPNAME_ACLUSERS, SpiConstants.USER_ROLES_PROPNAME_PREFIX); addAclProperty(MockRepositoryProperty.GROUP_SCOPE, aclProp, propList, SpiConstants.PROPNAME_ACLGROUPS, SpiConstants.GROUP_ROLES_PROPNAME_PREFIX); } // acldeny MockRepositoryProperty aclDenyProp = doc.getProplist().getProperty("acldeny"); if (aclDenyProp != null) { addAclProperty(MockRepositoryProperty.USER_SCOPE, aclDenyProp, propList, SpiConstants.PROPNAME_ACLDENYUSERS, null); addAclProperty(MockRepositoryProperty.GROUP_SCOPE, aclDenyProp, propList, SpiConstants.PROPNAME_ACLDENYGROUPS, null); } // Now push all the other properties onto the list. for (MockRepositoryProperty prop : doc.getProplist()) { // Don't pass on the some of the special MockRepoDocument properties so // they don't clutter the meta-data. Shouldn't delete them from the // MockRepoDocument because they could be used by the MockRepo. if (!propertySkipSet.contains(prop.getName())) { propList.add(new MockJcrProperty(prop)); } } } /** * Utility method to take the given {@link MockRepositoryProperty} and parse * and convert it into ACL Entries for the SPI {@link Document} space. * @param scopeType the scopeType to be extracted from the given * <code>repoAclProp</code>. Should be one of * {@link MockRepositoryProperty#USER_SCOPE} or * {@link MockRepositoryProperty#GROUP_SCOPE}. * @param repoAclProp the repository property containing general ACL * information. * @param propList the property list to store any ACL Entries of the given * <code>scopeType</code> extracted from the given * <code>repoAclProp</code>. * @param propName the key or property named to be used to store the ACL * Entries in the given <code>propList</code>. * @param rolesPrefix the prefix to use to add a property to define specific * roles for a ACL Entry, or {@code null} to append roles to the scope ID */ private void addAclProperty(String scopeType, MockRepositoryProperty repoAclProp, List<MockJcrProperty> propList, String propName, String rolesPrefix) { String[] values = repoAclProp.getValues(); List<String> newAclScopes = new ArrayList<String>(); for (int i = 0; i < values.length; i++) { String aclEntry = values[i]; // Strip the scope type if present and continue to process the entries // that match the given scopeType. int scopeTokPos = aclEntry.indexOf(MockRepositoryProperty.SCOPE_TYPE_SEP); if (scopeTokPos != -1) { if (scopeType.equals(aclEntry.substring(0, scopeTokPos))) { aclEntry = aclEntry.substring(scopeTokPos + 1); } else { continue; } } else { // If a scope type is not specified in the aclEntry then it's assumed to // be of scope type "user". if (!MockRepositoryProperty.USER_SCOPE.equals(scopeType)) { continue; } } // Separate the scope identity from the role list if present. int roleTokPos = aclEntry.indexOf(MockRepositoryProperty.SCOPE_ROLE_SEP); String scopeId; String rolesStr; if (roleTokPos != -1) { scopeId = aclEntry.substring(0, roleTokPos); rolesStr = aclEntry.substring(roleTokPos + 1); } else { scopeId = aclEntry; rolesStr = null; } // At this point we have the scope and list of roles that make up an ACL // Entry. if (rolesPrefix == null) { // Append the roles to the scope IDs. if (rolesStr == null) { newAclScopes.add("\"" + scopeId + "\""); } else { String[] roles = rolesStr.split(",", 0); for (String role : roles) { newAclScopes.add("\"" + scopeId + "=" + role + "\""); } } } else { // Add the scope to the list and, if there are roles specified, // create an associated "<rolesPrefix><scopeId>" property and add it to // the propList. newAclScopes.add("\"" + scopeId + "\""); if (rolesStr != null) { // Create a multi-value property for the scope's roles. List<String> rolesList = Arrays.asList(rolesStr.split(",", 0)); MockRepositoryProperty newRolesProp = new MockRepositoryProperty( rolesPrefix + scopeId, "{type:string, value:" + rolesList.toString() + "}"); propList.add(new MockJcrProperty(newRolesProp)); } } } if (newAclScopes.size() > 0) { MockRepositoryProperty newAclProp = new MockRepositoryProperty(propName, "{type:string, value:" + newAclScopes.toString() + "}"); propList.add(new MockJcrProperty(newAclProp)); } } public MockJcrNode(MockRepositoryDocument doc) { this.doc = doc; init(); } public Property getProperty(String arg0) { return findProperty(arg0); } public PropertyIterator getProperties() { return new MockJcrPropertyIterator(propList); } public String getUUID() throws UnsupportedRepositoryOperationException, RepositoryException { Property p = this.getProperty("jcr:uuid"); if (p == null) { throw new IllegalArgumentException(); } return p.getString(); } public boolean hasProperty(String arg0) { Property p = findProperty(arg0); return (p != null); } // The following methods may be needed later but are temporarily // unimplemented public Node getNode(String arg0) { throw new UnsupportedOperationException(); } public NodeIterator getNodes() { throw new UnsupportedOperationException(); } public NodeIterator getNodes(String arg0) { throw new UnsupportedOperationException(); } // The following methods are JCR level 1 - but we do not anticipate using them public PropertyIterator getProperties(String s) { throw new UnsupportedOperationException(); } public Item getPrimaryItem() { throw new UnsupportedOperationException(); } public int getIndex() { throw new UnsupportedOperationException(); } public PropertyIterator getReferences() { throw new UnsupportedOperationException(); } public boolean hasNode(String arg0) { throw new UnsupportedOperationException(); } public boolean hasNodes() { throw new UnsupportedOperationException(); } public boolean hasProperties() { throw new UnsupportedOperationException(); } public NodeType getPrimaryNodeType() { throw new UnsupportedOperationException(); } public NodeType[] getMixinNodeTypes() { throw new UnsupportedOperationException(); } public boolean isNodeType(String arg0) { throw new UnsupportedOperationException(); } public NodeDefinition getDefinition() { throw new UnsupportedOperationException(); } public String getCorrespondingNodePath(String arg0) { throw new UnsupportedOperationException(); } public String getPath() { throw new UnsupportedOperationException(); } public String getName() { throw new UnsupportedOperationException(); } public Item getAncestor(int arg0) { throw new UnsupportedOperationException(); } public Node getParent() { throw new UnsupportedOperationException(); } public int getDepth() { throw new UnsupportedOperationException(); } public Session getSession() { throw new UnsupportedOperationException(); } public boolean isNode() { throw new UnsupportedOperationException(); } public boolean isNew() { throw new UnsupportedOperationException(); } public boolean isModified() { throw new UnsupportedOperationException(); } public boolean isSame(Item arg0) { throw new UnsupportedOperationException(); } public void accept(ItemVisitor arg0) { throw new UnsupportedOperationException(); } // The following methods are JCR level 2 - these would never be needed public void addMixin(String arg0) { throw new UnsupportedOperationException(); } public void removeMixin(String arg0) { throw new UnsupportedOperationException(); } public boolean canAddMixin(String arg0) { throw new UnsupportedOperationException(); } public Version checkin() { throw new UnsupportedOperationException(); } public void checkout() { throw new UnsupportedOperationException(); } public void doneMerge(Version arg0) { throw new UnsupportedOperationException(); } public void cancelMerge(Version arg0) { throw new UnsupportedOperationException(); } public void update(String arg0) { throw new UnsupportedOperationException(); } public NodeIterator merge(String arg0, boolean arg1) { throw new UnsupportedOperationException(); } public boolean isCheckedOut() { throw new UnsupportedOperationException(); } public void restore(String arg0, boolean arg1) { throw new UnsupportedOperationException(); } public void restore(Version arg0, boolean arg1) { throw new UnsupportedOperationException(); } public void restore(Version arg0, String arg1, boolean arg2) { throw new UnsupportedOperationException(); } public void restoreByLabel(String arg0, boolean arg1) { throw new UnsupportedOperationException(); } public VersionHistory getVersionHistory() { throw new UnsupportedOperationException(); } public Version getBaseVersion() { throw new UnsupportedOperationException(); } public Lock lock(boolean arg0, boolean arg1) { throw new UnsupportedOperationException(); } public Lock getLock() { throw new UnsupportedOperationException(); } public void unlock() { throw new UnsupportedOperationException(); } public boolean holdsLock() { throw new UnsupportedOperationException(); } public boolean isLocked() { throw new UnsupportedOperationException(); } public Node addNode(String arg0) { throw new UnsupportedOperationException(); } public Node addNode(String arg0, String arg1) { throw new UnsupportedOperationException(); } public void orderBefore(String arg0, String arg1) { throw new UnsupportedOperationException(); } public Property setProperty(String arg0, Value arg1) { throw new UnsupportedOperationException(); } public Property setProperty(String arg0, Value arg1, int arg2) { throw new UnsupportedOperationException(); } public Property setProperty(String arg0, Value[] arg1) { throw new UnsupportedOperationException(); } public Property setProperty(String arg0, Value[] arg1, int arg2) { throw new UnsupportedOperationException(); } public Property setProperty(String arg0, String[] arg1) { throw new UnsupportedOperationException(); } public Property setProperty(String arg0, String[] arg1, int arg2) { throw new UnsupportedOperationException(); } public Property setProperty(String arg0, String arg1) { throw new UnsupportedOperationException(); } public Property setProperty(String arg0, String arg1, int arg2) { throw new UnsupportedOperationException(); } public Property setProperty(String arg0, InputStream arg1) { throw new UnsupportedOperationException(); } public Property setProperty(String arg0, boolean arg1) { throw new UnsupportedOperationException(); } public Property setProperty(String arg0, double arg1) { throw new UnsupportedOperationException(); } public Property setProperty(String arg0, long arg1) { throw new UnsupportedOperationException(); } public Property setProperty(String arg0, Calendar arg1) { throw new UnsupportedOperationException(); } public Property setProperty(String arg0, Node arg1) { throw new UnsupportedOperationException(); } public void save() { throw new UnsupportedOperationException(); } public void refresh(boolean arg0) { throw new UnsupportedOperationException(); } public void remove() { throw new UnsupportedOperationException(); } }