| /* |
| * 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.jackrabbit.test; |
| |
| import junit.framework.TestResult; |
| |
| import javax.jcr.Node; |
| import javax.jcr.PropertyType; |
| import javax.jcr.Session; |
| import javax.jcr.NodeIterator; |
| import javax.jcr.RepositoryException; |
| import javax.jcr.NamespaceRegistry; |
| import javax.jcr.Repository; |
| import javax.jcr.NamespaceException; |
| import javax.jcr.RangeIterator; |
| import javax.jcr.UnsupportedRepositoryOperationException; |
| import javax.jcr.Value; |
| import javax.jcr.ValueFactory; |
| import javax.jcr.nodetype.NoSuchNodeTypeException; |
| import javax.jcr.nodetype.NodeDefinition; |
| import javax.jcr.nodetype.ConstraintViolationException; |
| import javax.jcr.nodetype.NodeType; |
| import javax.jcr.nodetype.PropertyDefinition; |
| import javax.jcr.retention.RetentionManager; |
| |
| import java.util.StringTokenizer; |
| import java.util.Random; |
| import java.util.List; |
| import java.util.Arrays; |
| |
| /** |
| * Abstract base class for all JCR test classes. |
| */ |
| public abstract class AbstractJCRTest extends JUnitTest { |
| |
| /** |
| * Pool of helper objects to access repository transparently |
| */ |
| private static final RepositoryHelperPool HELPER_POOL = RepositoryHelperPoolImpl.getInstance(); |
| |
| /** |
| * Namespace URI for jcr prefix. |
| */ |
| public static final String NS_JCR_URI = "http://www.jcp.org/jcr/1.0"; |
| |
| /** |
| * Namespace URI for nt prefix. |
| */ |
| public static final String NS_NT_URI = "http://www.jcp.org/jcr/nt/1.0"; |
| |
| /** |
| * Namespace URI for mix prefix. |
| */ |
| public static final String NS_MIX_URI = "http://www.jcp.org/jcr/mix/1.0"; |
| |
| /** |
| * Namespace URI for sv prefix |
| */ |
| public static final String NS_SV_URI = "http://www.jcp.org/jcr/sv/1.0"; |
| |
| /** |
| * The repository helper for this test. |
| */ |
| private RepositoryHelper helper; |
| |
| /** |
| * JCR Name jcr:primaryType using the namespace resolver of the current session. |
| */ |
| protected String jcrPrimaryType; |
| |
| /** |
| * JCR Name jcr:mixinTypes using the namespace resolver of the current session. |
| */ |
| protected String jcrMixinTypes; |
| |
| /** |
| * JCR Name jcr:predecessors using the namespace resolver of the current session. |
| */ |
| protected String jcrPredecessors; |
| |
| /** |
| * JCR Name jcr:successors using the namespace resolver of the current session. |
| */ |
| protected String jcrSuccessors; |
| |
| /** |
| * JCR Name jcr:created using the namespace resolver of the current session. |
| */ |
| protected String jcrCreated; |
| |
| /** |
| * JCR Name jcr:created using the namespace resolver of the current session. |
| */ |
| protected String jcrVersionHistory; |
| |
| /** |
| * JCR Name jcr:copiedFrom using the namespace resolver of the current session. |
| */ |
| protected String jcrCopiedFrom; |
| |
| /** |
| * JCR Name jcr:frozenNode using the namespace resolver of the current session. |
| */ |
| protected String jcrFrozenNode; |
| |
| /** |
| * JCR Name jcr:frozenUuid using the namespace resolver of the current session. |
| */ |
| protected String jcrFrozenUuid; |
| |
| /** |
| * JCR Name jcr:rootVersion using the namespace resolver of the current session. |
| */ |
| protected String jcrRootVersion; |
| |
| /** |
| * JCR Name jcr:isCheckedOut using the namespace resolver of the current session. |
| */ |
| protected String jcrIsCheckedOut; |
| |
| /** |
| * JCR Name jcr:baseVersion using the namespace resolver of the current session. |
| */ |
| protected String jcrBaseVersion; |
| |
| /** |
| * JCR Name jcr:uuid using the namespace resolver of the current session. |
| */ |
| protected String jcrUUID; |
| |
| /** |
| * JCR Name jcr:lockOwner using the namespace resolver of the current session. |
| */ |
| protected String jcrLockOwner; |
| |
| /** |
| * JCR Name jcr:lockIsDeep using the namespace resolver of the current session. |
| */ |
| protected String jcrlockIsDeep; |
| |
| /** |
| * JCR Name jcr:mergeFailed using the namespace resolver of the current session. |
| */ |
| protected String jcrMergeFailed; |
| |
| /** |
| * JCR Name jcr:system using the namespace resolver of the current session. |
| */ |
| protected String jcrSystem; |
| |
| /** |
| * JCR Name nt:base using the namespace resolver of the current session. |
| */ |
| protected String ntBase; |
| |
| /** |
| * JCR Name nt:unstructured using the namespace resolver of the current session. |
| */ |
| protected String ntUnstructured; |
| |
| /** |
| * JCR Name nt:version using the namespace resolver of the current session. |
| */ |
| protected String ntVersion; |
| |
| /** |
| * JCR Name nt:versionHistory using the namespace resolver of the current session. |
| */ |
| protected String ntVersionHistory; |
| |
| /** |
| * JCR Name nt:versionHistory using the namespace resolver of the current session. |
| */ |
| protected String ntVersionLabels; |
| |
| /** |
| * JCR Name nt:frozenNode using the namespace resolver of the current session. |
| */ |
| protected String ntFrozenNode; |
| |
| /** |
| * JCR Name mix:referenceable using the namespace resolver of the current session. |
| */ |
| protected String mixReferenceable; |
| |
| /** |
| * JCR Name mix:versionable using the namespace resolver of the current session. |
| */ |
| protected String mixVersionable; |
| |
| /** |
| * JCR Name mix:simpleVersionable using the namespace resolver of the current session. |
| */ |
| protected String mixSimpleVersionable; |
| |
| /** |
| * JCR Name mix:lockable using the namespace resolver of the current session. |
| */ |
| protected String mixLockable; |
| |
| /** |
| * JCR Name mix:title using the namespace resolver of the current session. |
| */ |
| protected String mixTitle; |
| |
| /** |
| * JCR Name mix:shareable using the namespace resolver of the current session. |
| */ |
| protected String mixShareable; |
| |
| /** |
| * JCR Name nt:query using the namespace resolver of the current session. |
| */ |
| protected String ntQuery; |
| |
| /** |
| * JCR Name nt:activity using the namespace resolver of the current session. |
| */ |
| protected String ntActivity; |
| |
| /** |
| * Relative path to the test root node. |
| */ |
| protected String testPath; |
| |
| /** |
| * Absolute path to the test root node. |
| */ |
| protected String testRoot; |
| |
| /** |
| * The node type name for newly created nodes. |
| */ |
| protected String testNodeType; |
| |
| /** |
| * The node type name for the test root node. |
| */ |
| protected String testNodeTypeTestRoot; |
| |
| /** |
| * A node type that does not allow any child nodes, such as nt:base. |
| */ |
| protected String testNodeTypeNoChildren; |
| |
| /** |
| * Name of a node that will be created during a test case. |
| */ |
| protected String nodeName1; |
| |
| /** |
| * Name of a node that will be created during a test case. |
| */ |
| protected String nodeName2; |
| |
| /** |
| * Name of a node that will be created during a test case. |
| */ |
| protected String nodeName3; |
| |
| /** |
| * Name of a node that will be created during a test case. |
| */ |
| protected String nodeName4; |
| |
| /** |
| * Name of a property that will be used during a test case. |
| */ |
| protected String propertyName1; |
| |
| /** |
| * Name of a property that will be used during a test case. |
| */ |
| protected String propertyName2; |
| |
| /** |
| * Name of a workspace to use instead of the default workspace. |
| */ |
| protected String workspaceName; |
| |
| /** |
| * The superuser session for the default workspace |
| */ |
| protected Session superuser; |
| |
| /** |
| * Flag that indicates if the current test is a read-only test, that is |
| * no content is written to the workspace by the test. |
| */ |
| protected boolean isReadOnly = false; |
| |
| /** |
| * The root <code>Node</code> for testing |
| */ |
| protected Node testRootNode; |
| |
| /** |
| * The value factory for {@link #superuser}. |
| */ |
| protected ValueFactory vf; |
| |
| protected void setUp() throws Exception { |
| super.setUp(); |
| testRoot = getProperty(RepositoryStub.PROP_TESTROOT); |
| if (testRoot == null) { |
| fail("Property '" + RepositoryStub.PROP_TESTROOT + "' is not defined."); |
| } |
| |
| // cut off '/' to build testPath |
| testPath = testRoot.substring(1); |
| testNodeType = getProperty(RepositoryStub.PROP_NODETYPE); |
| testNodeTypeTestRoot = getProperty(RepositoryStub.PROP_NODETYPETESTROOT); |
| if (testNodeTypeTestRoot == null) { |
| testNodeTypeTestRoot = testNodeType; // backwards compatibility |
| } |
| testNodeTypeNoChildren = getProperty(RepositoryStub.PROP_NODETYPENOCHILDREN); |
| // setup node names |
| nodeName1 = getProperty(RepositoryStub.PROP_NODE_NAME1); |
| if (nodeName1 == null) { |
| fail("Property '" + RepositoryStub.PROP_NODE_NAME1 + "' is not defined."); |
| } |
| nodeName2 = getProperty(RepositoryStub.PROP_NODE_NAME2); |
| if (nodeName2 == null) { |
| fail("Property '" + RepositoryStub.PROP_NODE_NAME2 + "' is not defined."); |
| } |
| nodeName3 = getProperty(RepositoryStub.PROP_NODE_NAME3); |
| if (nodeName3 == null) { |
| fail("Property '" + RepositoryStub.PROP_NODE_NAME3 + "' is not defined."); |
| } |
| nodeName4 = getProperty(RepositoryStub.PROP_NODE_NAME4); |
| if (nodeName4 == null) { |
| fail("Property '" + RepositoryStub.PROP_NODE_NAME4 + "' is not defined."); |
| } |
| propertyName1 = getProperty(RepositoryStub.PROP_PROP_NAME1); |
| if (propertyName1 == null) { |
| fail("Property '" + RepositoryStub.PROP_PROP_NAME1 + "' is not defined."); |
| } |
| propertyName2 = getProperty(RepositoryStub.PROP_PROP_NAME2); |
| if (propertyName2 == null) { |
| fail("Property '" + RepositoryStub.PROP_PROP_NAME2 + "' is not defined."); |
| } |
| workspaceName = getProperty(RepositoryStub.PROP_WORKSPACE_NAME); |
| if (workspaceName == null) { |
| fail("Property '" + RepositoryStub.PROP_WORKSPACE_NAME + "' is not defined."); |
| } |
| |
| superuser = getHelper().getSuperuserSession(); |
| |
| // setup some common names |
| jcrPrimaryType = superuser.getNamespacePrefix(NS_JCR_URI) + ":primaryType"; |
| jcrMixinTypes = superuser.getNamespacePrefix(NS_JCR_URI) + ":mixinTypes"; |
| jcrPredecessors = superuser.getNamespacePrefix(NS_JCR_URI) + ":predecessors"; |
| jcrSuccessors = superuser.getNamespacePrefix(NS_JCR_URI) + ":successors"; |
| jcrCreated = superuser.getNamespacePrefix(NS_JCR_URI) + ":created"; |
| jcrVersionHistory = superuser.getNamespacePrefix(NS_JCR_URI) + ":versionHistory"; |
| jcrCopiedFrom = superuser.getNamespacePrefix(NS_JCR_URI) + ":copiedFrom"; |
| jcrFrozenNode = superuser.getNamespacePrefix(NS_JCR_URI) + ":frozenNode"; |
| jcrFrozenUuid = superuser.getNamespacePrefix(NS_JCR_URI) + ":frozenUuid"; |
| jcrRootVersion = superuser.getNamespacePrefix(NS_JCR_URI) + ":rootVersion"; |
| jcrBaseVersion = superuser.getNamespacePrefix(NS_JCR_URI) + ":baseVersion"; |
| jcrIsCheckedOut = superuser.getNamespacePrefix(NS_JCR_URI) + ":isCheckedOut"; |
| jcrUUID = superuser.getNamespacePrefix(NS_JCR_URI) + ":uuid"; |
| jcrLockOwner = superuser.getNamespacePrefix(NS_JCR_URI) + ":lockOwner"; |
| jcrlockIsDeep = superuser.getNamespacePrefix(NS_JCR_URI) + ":lockIsDeep"; |
| jcrMergeFailed = superuser.getNamespacePrefix(NS_JCR_URI) + ":mergeFailed"; |
| jcrSystem = superuser.getNamespacePrefix(NS_JCR_URI) + ":system"; |
| ntBase = superuser.getNamespacePrefix(NS_NT_URI) + ":base"; |
| ntUnstructured = superuser.getNamespacePrefix(NS_NT_URI) + ":unstructured"; |
| ntVersion = superuser.getNamespacePrefix(NS_NT_URI) + ":version"; |
| ntVersionHistory = superuser.getNamespacePrefix(NS_NT_URI) + ":versionHistory"; |
| ntVersionLabels = superuser.getNamespacePrefix(NS_NT_URI) + ":versionLabels"; |
| ntFrozenNode = superuser.getNamespacePrefix(NS_NT_URI) + ":frozenNode"; |
| mixReferenceable = superuser.getNamespacePrefix(NS_MIX_URI) + ":referenceable"; |
| mixVersionable = superuser.getNamespacePrefix(NS_MIX_URI) + ":versionable"; |
| mixSimpleVersionable = superuser.getNamespacePrefix(NS_MIX_URI) + ":simpleVersionable"; |
| mixLockable = superuser.getNamespacePrefix(NS_MIX_URI) + ":lockable"; |
| mixShareable = superuser.getNamespacePrefix(NS_MIX_URI) + ":shareable"; |
| mixTitle = superuser.getNamespacePrefix(NS_MIX_URI) + ":title"; |
| ntQuery = superuser.getNamespacePrefix(NS_NT_URI) + ":query"; |
| ntActivity = superuser.getNamespacePrefix(NS_NT_URI) + ":activity"; |
| |
| // setup custom namespaces |
| if (isSupported(Repository.LEVEL_2_SUPPORTED)) { |
| NamespaceRegistry nsReg = superuser.getWorkspace().getNamespaceRegistry(); |
| String namespaces = getProperty(RepositoryStub.PROP_NAMESPACES); |
| if (namespaces != null) { |
| String[] prefixes = namespaces.split(" "); |
| for (int i = 0; i < prefixes.length; i++) { |
| String uri = getProperty(RepositoryStub.PROP_NAMESPACES + "." + prefixes[i]); |
| if (uri != null) { |
| try { |
| nsReg.getPrefix(uri); |
| } catch (NamespaceException e) { |
| // not yet registered |
| nsReg.registerNamespace(prefixes[i], uri); |
| } |
| } |
| } |
| } |
| vf = superuser.getValueFactory(); |
| } |
| |
| if (isReadOnly) { |
| if (testPath.length() == 0) { |
| // test root is the root node |
| testRootNode = superuser.getRootNode(); |
| } else if (!superuser.getRootNode().hasNode(testPath)) { |
| cleanUp(); |
| fail("Workspace does not contain test data at: " + testRoot); |
| } else { |
| testRootNode = superuser.getRootNode().getNode(testPath); |
| } |
| } else if (isSupported(Repository.LEVEL_2_SUPPORTED)) { |
| testRootNode = cleanUpTestRoot(superuser); |
| // also clean second workspace |
| Session s = getHelper().getSuperuserSession(workspaceName); |
| try { |
| cleanUpTestRoot(s); |
| } finally { |
| s.logout(); |
| } |
| } else { |
| cleanUp(); |
| fail("Test case requires level 2 support."); |
| } |
| } |
| |
| protected void cleanUp() throws Exception { |
| if (superuser != null) { |
| try { |
| if (!isReadOnly && isSupported(Repository.LEVEL_2_SUPPORTED)) { |
| cleanUpTestRoot(superuser); |
| } |
| } catch (Exception e) { |
| log.println("Exception in tearDown: " + e.toString()); |
| } finally { |
| superuser.logout(); |
| superuser = null; |
| vf = null; |
| } |
| } |
| testRootNode = null; |
| } |
| |
| protected void tearDown() throws Exception { |
| cleanUp(); |
| super.tearDown(); |
| } |
| |
| /** |
| * Runs the test cases of this test class and reports the results to |
| * <code>testResult</code>. In contrast to the default implementation of |
| * <code>TestCase.run()</code> this method will suppress tests errors with |
| * a {@link NotExecutableException}. That is, test cases that throw this |
| * exception will still result as successful. |
| * @param testResult the test result. |
| */ |
| public void run(TestResult testResult) { |
| try { |
| helper = HELPER_POOL.borrowHelper(); |
| try { |
| super.run(new JCRTestResult(testResult, log)); |
| } finally { |
| HELPER_POOL.returnHelper(helper); |
| helper = null; |
| } |
| } catch (InterruptedException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| /** |
| * @return the repository helper instance that is associated with this test. |
| */ |
| protected RepositoryHelper getHelper() { |
| return helper; |
| } |
| |
| /** |
| * Returns the value of the configuration property with <code>propName</code>. |
| * The sequence how configuration properties are read is the follwoing: |
| * <ol> |
| * <li><code>javax.jcr.tck.<testClassName>.<testCaseName>.<propName></code></li> |
| * <li><code>javax.jcr.tck.<testClassName>.<propName></code></li> |
| * <li><code>javax.jcr.tck.<packageName>.<propName></code></li> |
| * <li><code>javax.jcr.tck.<propName></code></li> |
| * </ol> |
| * Where: |
| * <ul> |
| * <li><code><testClassName></code> is the name of the test class without package prefix.</li> |
| * <li><code><testMethodName></code> is the name of the test method</li> |
| * <li><code><packageName></code> is the name of the package of the test class. |
| * Example: packageName for <code>org.apache.jackrabbit.test.api.BooleanPropertyTest</code>: <code>api</code></li> |
| * </ul> |
| * @param propName the propName of the configration property. |
| * @return the value of the property or <code>null</code> if the property |
| * does not exist. |
| * @throws RepositoryException if an error occurs while reading from |
| * the configuration. |
| */ |
| public String getProperty(String propName) throws RepositoryException { |
| String testCaseName = getName(); |
| String testClassName = getClass().getName(); |
| String testPackName = ""; |
| int idx; |
| if ((idx = testClassName.lastIndexOf('.')) > -1) { |
| testPackName = testClassName.substring(testClassName.lastIndexOf('.', idx - 1) + 1, idx); |
| testClassName = testClassName.substring(idx + 1); |
| } |
| |
| // 1) test case specific property first |
| String value = getHelper().getProperty(RepositoryStub.PROP_PREFIX + "." |
| + testClassName + "." + testCaseName + "." + propName); |
| if (value != null) { |
| return value; |
| } |
| |
| // 2) check test class property |
| value = getHelper().getProperty(RepositoryStub.PROP_PREFIX + "." |
| + testClassName + "." + propName); |
| if (value != null) { |
| return value; |
| } |
| |
| // 3) check package property |
| value = getHelper().getProperty(RepositoryStub.PROP_PREFIX + "." |
| + testPackName + "." + propName); |
| if (value != null) { |
| return value; |
| } |
| |
| // finally try global property |
| return getHelper().getProperty(RepositoryStub.PROP_PREFIX + "." + propName); |
| } |
| |
| /** |
| * Returns the value of the configuration property with specified |
| * <code>name</code>. If the property does not exist <code>defaultValue</code> is |
| * returned. |
| * <p> |
| * Configuration properties are defined in the file: |
| * <code>repositoryStubImpl.properties</code>. |
| * |
| * @param name the name of the property to retrieve. |
| * @param defaultValue the default value if the property does not exist. |
| * @return the value of the property or <code>defaultValue</code> if non existent. |
| * @throws RepositoryException if the configuration file cannot be found. |
| */ |
| public String getProperty(String name, String defaultValue) throws RepositoryException { |
| String val = getProperty(name); |
| if (val == null) { |
| val = defaultValue; |
| } |
| return val; |
| } |
| |
| /** |
| * Create a JCR value based on the configuration. |
| * |
| * @param s |
| * @param valueProp Name of the config property that contains the property value. |
| * @param typeProp Name of the config property that contains the property type. |
| * If the config parameter is missing, {@link PropertyType#STRING} is used |
| * to create the JCR value. |
| * @param defaultValue Default value to be used if the config does not define |
| * the value property. |
| * @return JCR value to be used for a test. |
| * @throws RepositoryException |
| */ |
| public Value getJcrValue(Session s, String valueProp, String typeProp, String defaultValue) throws RepositoryException { |
| ValueFactory vf = s.getValueFactory(); |
| String val = getProperty(valueProp, defaultValue); |
| int type = PropertyType.valueFromName(getProperty(typeProp, PropertyType.TYPENAME_STRING)); |
| return vf.createValue(val, type); |
| } |
| |
| /** |
| * Returns the size of the <code>RangeIterator</code> <code>it</code>. |
| * Note, that the <code>RangeIterator</code> might get consumed, because |
| * {@link RangeIterator#getSize()} might return -1 (information unavailable). |
| * @param it a <code>RangeIterator</code>. |
| * @return the size of the iterator (number of elements). |
| */ |
| protected long getSize(RangeIterator it) { |
| long size = it.getSize(); |
| if (size != -1) { |
| return size; |
| } |
| size = 0; |
| while (it.hasNext()) { |
| it.next(); |
| size++; |
| } |
| return size; |
| } |
| |
| /** |
| * Returns the local name for the given <code>jcrName</code>. |
| * |
| * @param jcrName |
| * the name. |
| * @return the local name part. |
| */ |
| protected static String getLocalName(String jcrName) { |
| int idx = jcrName.indexOf(':'); |
| if (idx != -1) { |
| return jcrName.substring(idx + 1); |
| } else { |
| return jcrName; |
| } |
| } |
| |
| /** |
| * Returns the prefix for the given <code>jcrName</code>. |
| * |
| * @param jcrName |
| * the name. |
| * @return the prefix part (empty string when not prefixed) |
| */ |
| protected static String getPrefix(String jcrName) { |
| int idx = jcrName.indexOf(':'); |
| if (idx != -1) { |
| return jcrName.substring(0, idx); |
| } else { |
| return ""; |
| } |
| } |
| |
| /** |
| * Returns the expanded name for the given <code>jcrName</code>. |
| * |
| * @param jcrName |
| * the name. |
| * @return the expanded name representation |
| * @throws RepositoryException |
| * @throws NamespaceException |
| */ |
| protected static String getQualifiedName(Session session, String jcrName) throws RepositoryException { |
| String prefix = getPrefix(jcrName); |
| String namespace = session.getNamespaceURI(prefix); |
| String localname = getLocalName(jcrName); |
| return (namespace.length() > 0 ? "{" + namespace + "}" : "{}") + localname; |
| } |
| |
| /** |
| * Returns the name of a workspace that is not accessible from |
| * <code>session</code>. |
| * @param session the session. |
| * @return name of a non existing workspace. |
| * @throws RepositoryException if an error occurs. |
| */ |
| protected String getNonExistingWorkspaceName(Session session) throws RepositoryException { |
| List<String> names = Arrays.asList(session.getWorkspace().getAccessibleWorkspaceNames()); |
| String nonExisting = null; |
| while (nonExisting == null) { |
| String name = createRandomString(10); |
| if (!names.contains(name)) { |
| nonExisting = name; |
| } |
| } |
| return nonExisting; |
| } |
| |
| /** |
| * Creates a <code>String</code> with a random sequence of characters |
| * using 'a' - 'z'. |
| * @param numChars number of characters. |
| * @return the generated String. |
| */ |
| protected String createRandomString(int numChars) { |
| Random rand = new Random(System.currentTimeMillis()); |
| StringBuffer tmp = new StringBuffer(numChars); |
| for (int i = 0; i < numChars; i++) { |
| char c = (char) (rand.nextInt(('z' + 1) - 'a') + 'a'); |
| tmp.append(c); |
| } |
| return tmp.toString(); |
| } |
| |
| /** |
| * Returns <code>true</code> if this repository support a certain optional |
| * feature; otherwise <code>false</code> is returned. If there is no |
| * such <code>descriptorKey</code> present in the repository, this method |
| * also returns false. |
| * |
| * @param descriptorKey the descriptor key. |
| * @return <code>true</code> if the option is supported. |
| * @throws RepositoryException if an error occurs. |
| */ |
| protected boolean isSupported(String descriptorKey) throws RepositoryException { |
| return "true".equals(getHelper().getRepository().getDescriptor(descriptorKey)); |
| } |
| |
| /** |
| * Throws a <code>NotExecutableException</code> if the repository does |
| * not support the feature identified by the given <code>discriptorKey</code>. |
| * |
| * @param descriptorKey the descriptor key. |
| * @throws RepositoryException if an error occurs. |
| * @throws NotExecutableException If the feature is not supported. |
| */ |
| protected void checkSupportedOption(String descriptorKey) throws RepositoryException, NotExecutableException { |
| String value = getHelper().getRepository().getDescriptor(descriptorKey); |
| if (value == null || ! Boolean.valueOf(value).booleanValue()) { |
| throw new NotExecutableException ( |
| "Repository feature not supported: " + descriptorKey); |
| } |
| } |
| |
| /** |
| * Checks that the repository supports multiple workspace, otherwise aborts with |
| * {@link NotExecutableException}. |
| * @throws NotExecutableException when the repository only supports a single |
| * workspace |
| */ |
| protected void ensureMultipleWorkspacesSupported() throws RepositoryException, NotExecutableException { |
| String workspacenames[] = superuser.getWorkspace().getAccessibleWorkspaceNames(); |
| if (workspacenames == null || workspacenames.length < 2) { |
| throw new NotExecutableException("This repository does not seem to support multiple workspaces."); |
| } |
| } |
| |
| /** |
| * Checks that the repository supports locking, otherwise aborts with |
| * {@link NotExecutableException}. |
| * @throws NotExecutableException when the repository does not support locking |
| */ |
| protected void ensureLockingSupported() throws RepositoryException, NotExecutableException { |
| if (!isSupported(Repository.OPTION_LOCKING_SUPPORTED)) { |
| throw new NotExecutableException("This repository does not support locking."); |
| } |
| } |
| |
| private boolean canSetProperty(NodeType nodeType, String propertyName, int propertyType, boolean isMultiple) { |
| PropertyDefinition propDefs[] = nodeType.getPropertyDefinitions(); |
| |
| for (int i = 0; i < propDefs.length; i++) { |
| if (propDefs[i].getName().equals(propertyName) || propDefs[i].getName().equals("*")) { |
| if ((propDefs[i].getRequiredType() == propertyType || propDefs[i].getRequiredType() == PropertyType.UNDEFINED) |
| && propDefs[i].isMultiple() == isMultiple) { |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| private boolean canSetProperty(Node node, String propertyName, int propertyType, boolean isMultiple) throws RepositoryException { |
| |
| if (canSetProperty(node.getPrimaryNodeType(), propertyName, propertyType, isMultiple)) { |
| return true; |
| } |
| else { |
| NodeType mixins[] = node.getMixinNodeTypes(); |
| boolean canSetIt = false; |
| for (int i = 0; i < mixins.length && !canSetIt; i++) { |
| canSetIt |= canSetProperty(mixins[i], propertyName, propertyType, isMultiple); |
| } |
| return canSetIt; |
| } |
| } |
| |
| /** |
| * Checks that the repository can set the property to the required type, otherwise aborts with |
| * {@link NotExecutableException}. |
| * @throws NotExecutableException when setting the property to the required |
| * type is not supported |
| */ |
| protected void ensureCanSetProperty(Node node, String propertyName, int propertyType, boolean isMultiple) throws NotExecutableException, RepositoryException { |
| |
| if (! canSetProperty(node, propertyName, propertyType, isMultiple)) { |
| throw new NotExecutableException("configured property name " + propertyName + " can not be set on node " + node.getPath()); |
| } |
| } |
| |
| /** |
| * Checks that the repository can set the property to the required type, otherwise aborts with |
| * {@link NotExecutableException}. |
| * @throws NotExecutableException when setting the property to the required |
| * type is not supported |
| */ |
| protected void ensureCanSetProperty(Node node, String propertyName, Value value) throws NotExecutableException, RepositoryException { |
| ensureCanSetProperty(node, propertyName, value.getType(), false); |
| } |
| |
| /** |
| * Checks that the repository can set the property to the required type, otherwise aborts with |
| * {@link NotExecutableException}. |
| * @throws NotExecutableException when setting the property to the required |
| * type is not supported |
| */ |
| protected void ensureCanSetProperty(Node node, String propertyName, Value[] values) throws NotExecutableException, RepositoryException { |
| |
| int propertyType = values.length == 0 ? PropertyType.UNDEFINED : values[0].getType(); |
| |
| if (! canSetProperty(node, propertyName, propertyType, true)) { |
| throw new NotExecutableException("configured property name " + propertyName + " can not be set on node " + node.getPath()); |
| } |
| } |
| |
| /** |
| * Checks that the repository supports the specified node type, otherwise aborts with |
| * {@link NotExecutableException} |
| * @throws NotExecutableException when the specified node type is unknown |
| */ |
| protected void ensureKnowsNodeType(Session session, String nodetype) throws NotExecutableException, RepositoryException { |
| |
| try { |
| session.getWorkspace().getNodeTypeManager().getNodeType(nodetype); |
| } |
| catch (NoSuchNodeTypeException ex) { |
| throw new NotExecutableException("Repository does not support node type " + nodetype); |
| } |
| } |
| |
| /** |
| * Ensures that the given <code>node</code> is of the given mixin type. |
| * |
| * @param node a node. |
| * @param mixin the name of a mixin type. |
| * @throws NotExecutableException if the node is not of type mixin and the |
| * mixin cannot be added. |
| * @throws RepositoryException if an error occurs. |
| */ |
| protected void ensureMixinType(Node node, String mixin) |
| throws NotExecutableException, RepositoryException { |
| if (!node.isNodeType(mixin)) { |
| if (node.canAddMixin(mixin)) { |
| node.addMixin(mixin); |
| } else { |
| throw new NotExecutableException(node.getPath() + |
| " does not support adding " + mixin); |
| } |
| } |
| } |
| |
| /** |
| * Checks whether the node already has the specified mixin node type |
| */ |
| protected boolean needsMixin(Node node, String mixin) throws RepositoryException { |
| return ! node.getSession().getWorkspace().getNodeTypeManager().getNodeType(node.getPrimaryNodeType().getName()).isNodeType(mixin); |
| } |
| |
| /** |
| * Reverts any pending changes made by <code>s</code> and deletes any nodes |
| * under {@link #testRoot}. If there is no node at {@link #testRoot} then |
| * the necessary nodes are created. |
| * |
| * @param s the session to clean up. |
| * @return the {@link javax.jcr.Node} that represents the test root. |
| * @throws RepositoryException if an error occurs. |
| */ |
| protected Node cleanUpTestRoot(Session s) throws RepositoryException { |
| // do a 'rollback' |
| s.refresh(false); |
| Node root = s.getRootNode(); |
| Node testRootNode; |
| |
| if (root.hasNode(testPath)) { |
| RetentionManager rm; |
| try { |
| rm = s.getRetentionManager(); |
| } catch (UnsupportedRepositoryOperationException e) { |
| rm = null; |
| } |
| |
| // clean test root |
| testRootNode = root.getNode(testPath); |
| NodeIterator children = testRootNode.getNodes(); |
| while (children.hasNext()) { |
| Node child = children.nextNode(); |
| |
| // Remove retention policy if needed |
| String childPath = child.getPath(); |
| if (rm != null && rm.getRetentionPolicy(childPath) != null) { |
| rm.removeRetentionPolicy(childPath); |
| s.save(); |
| } |
| |
| NodeDefinition nodeDef = child.getDefinition(); |
| if (!nodeDef.isMandatory() && !nodeDef.isProtected()) { |
| // try to remove child |
| try { |
| child.remove(); |
| } catch (ConstraintViolationException e) { |
| log.println("unable to remove node: " + child.getPath()); |
| } |
| } |
| } |
| } else { |
| // create nodes to testPath |
| StringTokenizer names = new StringTokenizer(testPath, "/"); |
| Node currentNode = root; |
| while (names.hasMoreTokens()) { |
| String name = names.nextToken(); |
| if (currentNode.hasNode(name)) { |
| currentNode = currentNode.getNode(name); |
| } else { |
| currentNode = currentNode.addNode(name, testNodeTypeTestRoot); |
| } |
| } |
| testRootNode = currentNode; |
| } |
| s.save(); |
| return testRootNode; |
| } |
| } |