blob: 98d0a7f136045709bcda9034fd7b90314179e47d [file] [log] [blame]
/*
* 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.ide.impl.vlt;
import static org.apache.sling.ide.transport.Repository.CommandExecutionFlag.CREATE_ONLY_WHEN_MISSING;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.concurrent.Callable;
import javax.jcr.Credentials;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.Value;
import javax.jcr.nodetype.InvalidNodeTypeDefinitionException;
import javax.jcr.nodetype.NodeType;
import javax.jcr.nodetype.NodeTypeExistsException;
import org.apache.jackrabbit.commons.cnd.CndImporter;
import org.apache.jackrabbit.commons.cnd.ParseException;
import org.apache.jackrabbit.core.TransientRepository;
import org.apache.sling.ide.filter.Filter;
import org.apache.sling.ide.filter.FilterResult;
import org.apache.sling.ide.log.Logger;
import org.apache.sling.ide.transport.CommandContext;
import org.apache.sling.ide.transport.ResourceProxy;
import org.hamcrest.Matchers;
import org.junit.Ignore;
import org.junit.Test;
public class AddOrUpdateNodeCommandTest {
private static final CommandContext DEFAULT_CONTEXT = new CommandContext(new Filter() {
@Override
public FilterResult filter(String repositoryPath) {
return FilterResult.ALLOW;
}
});
private static final String PROP_NAME = "jcr:title";
private Logger logger = new Slf4jLogger();
@Test
public void setProperty() throws Exception {
doPropertyChangeTest(null, "Title");
}
private void doPropertyChangeTest(final Object initialPropertyValues, final Object newPropertyValues)
throws Exception {
doWithTransientRepository(new CallableWithSession() {
@Override
public Void call() throws Exception {
Node contentNode = session().getRootNode().addNode("content");
if (initialPropertyValues instanceof String) {
contentNode.setProperty(PROP_NAME, (String) initialPropertyValues);
} else if (initialPropertyValues instanceof String[]) {
contentNode.setProperty(PROP_NAME, (String[]) initialPropertyValues);
}
session().save();
ResourceProxy resource = newResource("/content", NodeType.NT_UNSTRUCTURED);
if (newPropertyValues != null) {
resource.addProperty(PROP_NAME, newPropertyValues);
}
AddOrUpdateNodeCommand cmd = new AddOrUpdateNodeCommand(repo(), credentials(), DEFAULT_CONTEXT, null, resource, logger);
cmd.execute().get();
session().refresh(false);
if (newPropertyValues == null) {
assertThat(session().getNode("/content").hasProperty(PROP_NAME), equalTo(false));
return null;
}
Property newProp = session().getNode("/content").getProperty(PROP_NAME);
if (newPropertyValues instanceof String) {
assertThat("property.isMultiple", newProp.isMultiple(), equalTo(Boolean.FALSE));
assertThat(newProp.getString(), equalTo((String) newPropertyValues));
} else {
String[] expectedValues = (String[]) newPropertyValues;
assertThat("property.isMultiple", newProp.isMultiple(), equalTo(Boolean.TRUE));
Value[] values = session().getNode("/content").getProperty(PROP_NAME).getValues();
assertThat(values.length, equalTo(expectedValues.length));
for (int i = 0; i < values.length; i++) {
assertThat(values[i].getString(), equalTo(expectedValues[i]));
}
}
return null;
}
});
}
@Test
public void removeProperty() throws Exception {
doPropertyChangeTest("Title", null);
}
@Test
public void singlePropertyToMultiValued() throws Exception {
doPropertyChangeTest("Title", new String[] { "Title", "Title 2" });
}
@Test
public void multiValuesPropertyToSingle() throws Exception {
doPropertyChangeTest(new String[] { "Title", "Title 2" }, "Title");
}
@Test
public void changeNtFolderToSlingFolderWithAddedProperty() throws Exception {
doWithTransientRepository(new CallableWithSession() {
@Override
public Void call() throws Exception {
session().getRootNode().addNode("content", "nt:folder");
session().save();
ResourceProxy resource = newResource("/content", "sling:Folder");
resource.getProperties().put("newProperty", "some/value");
AddOrUpdateNodeCommand cmd = new AddOrUpdateNodeCommand(repo(), credentials(), DEFAULT_CONTEXT, null, resource, logger);
cmd.execute().get();
session().refresh(false);
Node content = session().getRootNode().getNode("content");
assertThat(content.getPrimaryNodeType().getName(), equalTo("sling:Folder"));
return null;
}
});
}
@Test
public void changeSlingFolderToNtFolderWithExistingProperty() throws Exception {
doWithTransientRepository(new CallableWithSession() {
@Override
public Void call() throws Exception {
Node content = session().getRootNode().addNode("content", "sling:Folder");
content.setProperty("newProperty", "some/value");
session().save();
ResourceProxy resource = newResource("/content", "nt:folder");
AddOrUpdateNodeCommand cmd = new AddOrUpdateNodeCommand(repo(), credentials(), DEFAULT_CONTEXT, null, resource, logger);
cmd.execute().get();
session().refresh(false);
content = session().getRootNode().getNode("content");
assertThat(content.getPrimaryNodeType().getName(), equalTo("nt:folder"));
return null;
}
});
}
@Test
@Ignore("SLING-4036")
public void updateNtUnstructuredToNodeWithRequiredProperty() throws Exception {
doWithTransientRepository(new CallableWithSession() {
@Override
public Void call() throws Exception {
Node content = session().getRootNode().addNode("content", "nt:unstructured");
session().save();
ResourceProxy resource = newResource("/content", "custom");
resource.getProperties().put("attribute", "some value");
AddOrUpdateNodeCommand cmd = new AddOrUpdateNodeCommand(repo(), credentials(), DEFAULT_CONTEXT, null, resource, logger);
cmd.execute().get();
session().refresh(false);
content = session().getRootNode().getNode("content");
assertThat(content.getPrimaryNodeType().getName(), equalTo("custom"));
return null;
}
});
}
@Test
public void nodeNotPresentButOutsideOfFilterIsNotRemoved() throws Exception {
final CommandContext context = new CommandContext(new Filter() {
@Override
public FilterResult filter(String repositoryPath) {
if ( repositoryPath.equals("/content/not-included-child")) {
return FilterResult.DENY;
}
return FilterResult.ALLOW;
}
});
doWithTransientRepository(new CallableWithSession() {
@Override
public Void call() throws Exception {
Node content = session().getRootNode().addNode("content", "nt:unstructured");
content.addNode("included-child");
content.addNode("not-included-child");
session().save();
ResourceProxy resource = newResource("/content", "nt:unstructured");
resource.addChild(newResource("/content/included-child", "nt:unstructured"));
AddOrUpdateNodeCommand cmd = new AddOrUpdateNodeCommand(repo(), credentials(), context, null, resource, logger);
cmd.execute().get();
session().refresh(false);
content = session().getRootNode().getNode("content");
content.getNode("included-child");
content.getNode("not-included-child");
return null;
}
});
}
private ResourceProxy newResource(String path, String primaryType) {
ResourceProxy resource = new ResourceProxy(path);
resource.addProperty("jcr:primaryType", primaryType);
return resource;
}
@Test
public void createIfRequiredFlagSkipsExistingResources() throws Exception {
doWithTransientRepository(new CallableWithSession() {
@Override
public Void call() throws Exception {
Node content = session().getRootNode().addNode("content", "nt:folder");
session().save();
ResourceProxy resource = newResource("/content", "nt:unstructured");
AddOrUpdateNodeCommand cmd = new AddOrUpdateNodeCommand(repo(), credentials(), DEFAULT_CONTEXT, null, resource, logger,
CREATE_ONLY_WHEN_MISSING);
cmd.execute().get();
session().refresh(false);
content = session().getRootNode().getNode("content");
assertThat(content.getPrimaryNodeType().getName(), equalTo("nt:folder"));
return null;
}
});
}
@Test
public void createIfRequiredFlagCreatesNeededResources() throws Exception {
doWithTransientRepository(new CallableWithSession() {
@Override
public Void call() throws Exception {
ResourceProxy resource = newResource("/content", "nt:unstructured");
AddOrUpdateNodeCommand cmd = new AddOrUpdateNodeCommand(repo(), credentials(), DEFAULT_CONTEXT, null, resource, logger,
CREATE_ONLY_WHEN_MISSING);
cmd.execute().get();
session().refresh(false);
Node content = session().getRootNode().getNode("content");
assertThat(content.getPrimaryNodeType().getName(), equalTo("nt:unstructured"));
return null;
}
});
}
@Test
public void createIfRequiredFlagCreatesNeededResourcesEvenWhenPrimaryTypeIsMissing() throws Exception {
doWithTransientRepository(new CallableWithSession() {
@Override
public Void call() throws Exception {
ResourceProxy resource = new ResourceProxy("/content");
AddOrUpdateNodeCommand cmd = new AddOrUpdateNodeCommand(repo(), credentials(), DEFAULT_CONTEXT, null, resource, logger,
CREATE_ONLY_WHEN_MISSING);
cmd.execute().get();
session().refresh(false);
Node content = session().getRootNode().getNode("content");
assertThat(content.getPrimaryNodeType().getName(), equalTo("nt:unstructured"));
return null;
}
});
}
@Test
public void autoCreatedPropertiesAreNotRemoved() throws Exception {
doWithTransientRepository(new CallableWithSession() {
@Override
public Void call() throws Exception {
Node content = session().getRootNode().addNode("content", "nt:folder");
session().save();
ResourceProxy resource = newResource("/content", "nt:folder");
resource.addProperty("jcr:mixinTypes", "mix:lastModified");
AddOrUpdateNodeCommand cmd = new AddOrUpdateNodeCommand(repo(), credentials(), DEFAULT_CONTEXT, null, resource, logger);
cmd.execute().get();
cmd.execute().get(); // second time since mixins are processed after properties so we need two
// executions to
// expose the problem
session().refresh(false);
content = session().getRootNode().getNode("content");
assertThat("jcr:lastModified property not present", content.hasProperty("jcr:lastModified"),
equalTo(true));
assertThat("jcr:lastModifiedBy property not present", content.hasProperty("jcr:lastModifiedBy"),
equalTo(true));
return null;
}
});
}
@Test
public void autoCreatedPropertiesAreUpdatedIfPresent() throws Exception {
doWithTransientRepository(new CallableWithSession() {
@Override
public Void call() throws Exception {
Node content = session().getRootNode().addNode("content", "nt:folder");
session().save();
ResourceProxy resource = newResource("/content", "nt:folder");
resource.addProperty("jcr:mixinTypes", "mix:lastModified");
resource.addProperty("jcr:lastModifiedBy", "admin2");
AddOrUpdateNodeCommand cmd = new AddOrUpdateNodeCommand(repo(), credentials(), DEFAULT_CONTEXT, null, resource, logger);
cmd.execute().get();
cmd.execute().get(); // second time since mixins are processed after properties so we need two
// executions to
// expose the problem
session().refresh(false);
content = session().getRootNode().getNode("content");
assertThat("jcr:lastModifiedBy property not modified", content.getProperty("jcr:lastModifiedBy")
.getString(), equalTo("admin2"));
return null;
}
});
}
@Test
public void setEmptyMixinTypes() throws Exception {
setMixinTypes0();
}
private void setMixinTypes0(final String... mixinTypeNames) throws Exception {
doWithTransientRepository(new CallableWithSession() {
@Override
public Void call() throws Exception {
ResourceProxy resource = newResource("/content", "nt:unstructured");
resource.addProperty("jcr:mixinTypes", mixinTypeNames);
AddOrUpdateNodeCommand cmd = new AddOrUpdateNodeCommand(repo(), credentials(), DEFAULT_CONTEXT, null, resource, logger);
cmd.execute().get();
session().refresh(false);
Node content = session().getRootNode().getNode("content");
assertThat(content.getMixinNodeTypes(), Matchers.arrayWithSize(mixinTypeNames.length));
return null;
}
});
}
@Test
public void setMixinTypes() throws Exception {
setMixinTypes0("mix:created");
}
private void doWithTransientRepository(CallableWithSession callable) throws Exception {
File out = new File(new File("target"), "jackrabbit");
TransientRepository repo = new TransientRepository(new File(out, "repository.xml"), new File(out, "repository"));
SimpleCredentials credentials = new SimpleCredentials("admin", "admin".toCharArray());
Session session = repo.login(credentials);
importNodeTypeDefinitions(session, "test-definitions.cnd");
importNodeTypeDefinitions(session, "folder.cnd");
try {
callable.setCredentials(credentials);
callable.setSession(session);
callable.call();
} finally {
if (session.itemExists("/content"))
session.removeItem("/content");
session.save();
session.logout();
}
}
private void importNodeTypeDefinitions(Session session, String cndFile) throws InvalidNodeTypeDefinitionException,
NodeTypeExistsException, UnsupportedRepositoryOperationException, ParseException, RepositoryException,
IOException {
try ( InputStream cndInput = getClass().getResourceAsStream(cndFile) ) {
if (cndInput == null) {
throw new IllegalArgumentException("Unable to read classpath resource " + cndFile);
}
CndImporter.registerNodeTypes(new InputStreamReader(cndInput), session);
}
}
private static abstract class CallableWithSession implements Callable<Void> {
private Session session;
private Credentials credentials;
public void setSession(Session session) {
this.session = session;
}
public void setCredentials(Credentials credentials) {
this.credentials = credentials;
}
protected Session session() {
if (session == null)
throw new IllegalStateException("session is null");
return session;
}
protected Credentials credentials() {
if (credentials == null)
throw new IllegalStateException("credentials is null");
return credentials;
}
protected Repository repo() {
return session().getRepository();
}
}
}