/*
* ModeShape (http://www.modeshape.org)
*
* 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.modeshape.web.jcr.webdav;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.servlet.ServletContext;
import org.modeshape.common.logging.Logger;
/**
* Implementation of the {@link ContentMapper} interface that maps webdav requests to JCR operations.
*/
public class DefaultContentMapper implements ContentMapper {
public static final String INIT_CONTENT_PRIMARY_TYPE_NAMES = "org.modeshape.web.jcr.webdav.CONTENT_PRIMARY_TYPE_NAMES";
public static final String INIT_RESOURCE_PRIMARY_TYPES_NAMES = "org.modeshape.web.jcr.webdav.RESOURCE_PRIMARY_TYPE_NAMES";
public static final String INIT_NEW_FOLDER_PRIMARY_TYPE_NAME = "org.modeshape.web.jcr.webdav.NEW_FOLDER_PRIMARY_TYPE_NAME";
public static final String INIT_NEW_RESOURCE_PRIMARY_TYPE_NAME = "org.modeshape.web.jcr.webdav.NEW_RESOURCE_PRIMARY_TYPE_NAME";
public static final String INIT_NEW_CONTENT_PRIMARY_TYPE_NAME = "org.modeshape.web.jcr.webdav.NEW_CONTENT_PRIMARY_TYPE_NAME";
private static final String CONTENT_NODE_NAME = "jcr:content";
private static final String DATA_PROP_NAME = "jcr:data";
private static final String MODIFIED_PROP_NAME = "jcr:lastModified";
private static final String ENCODING_PROP_NAME = "jcr:encoding";
private static final String MIME_TYPE_PROP_NAME = "jcr:mimeType";
private static final String DEFAULT_CONTENT_PRIMARY_TYPES = "nt:resource, mode:resource";
private static final String DEFAULT_RESOURCE_PRIMARY_TYPES = "nt:file";
private static final String DEFAULT_NEW_FOLDER_PRIMARY_TYPE = "nt:folder";
private static final String DEFAULT_NEW_RESOURCE_PRIMARY_TYPE = "nt:file";
private static final String DEFAULT_NEW_CONTENT_PRIMARY_TYPE = "nt:resource";
private Collection<String> contentPrimaryTypes;
private Collection<String> filePrimaryTypes;
private String newFolderPrimaryType;
private String newResourcePrimaryType;
private String newContentPrimaryType;
private final Logger logger = Logger.getLogger(getClass());
@Override
public void initialize( ServletContext servletContext ) {
String contentPrimaryTypes = getParam(servletContext, INIT_CONTENT_PRIMARY_TYPE_NAMES);
String resourcePrimaryTypes = getParam(servletContext, INIT_RESOURCE_PRIMARY_TYPES_NAMES);
String newFolderPrimaryType = getParam(servletContext, INIT_NEW_FOLDER_PRIMARY_TYPE_NAME);
String newResourcePrimaryType = getParam(servletContext, INIT_NEW_RESOURCE_PRIMARY_TYPE_NAME);
String newContentPrimaryType = getParam(servletContext, INIT_NEW_CONTENT_PRIMARY_TYPE_NAME);
logger.debug("DefaultContentMapper initial content primary types = " + contentPrimaryTypes);
logger.debug("DefaultContentMapper initial file primary types = " + filePrimaryTypes);
logger.debug("DefaultContentMapper initial new folder primary types = " + newFolderPrimaryType);
logger.debug("DefaultContentMapper initial new resource primary types = " + newResourcePrimaryType);
logger.debug("DefaultContentMapper initial new content primary types = " + newContentPrimaryType);
this.contentPrimaryTypes = split(contentPrimaryTypes != null ? contentPrimaryTypes : DEFAULT_CONTENT_PRIMARY_TYPES);
this.filePrimaryTypes = split(resourcePrimaryTypes != null ? resourcePrimaryTypes : DEFAULT_RESOURCE_PRIMARY_TYPES);
this.newFolderPrimaryType = newFolderPrimaryType != null ? newFolderPrimaryType : DEFAULT_NEW_FOLDER_PRIMARY_TYPE;
this.newResourcePrimaryType = newResourcePrimaryType != null ? newResourcePrimaryType : DEFAULT_NEW_RESOURCE_PRIMARY_TYPE;
this.newContentPrimaryType = newContentPrimaryType != null ? newContentPrimaryType : DEFAULT_NEW_CONTENT_PRIMARY_TYPE;
}
protected String getParam( ServletContext servletContext,
String name ) {
return servletContext.getInitParameter(name);
}
/**
* Returns an unmodifiable set containing the elements passed in to this method
*
* @param elements a set of elements; may not be null
* @return an unmodifiable set containing all of the elements in {@code elements}; never null
*/
private static Set<String> setFor( String... elements ) {
Set<String> set = new HashSet<String>(elements.length);
set.addAll(Arrays.asList(elements));
return set;
}
/**
* Splits a comma-delimited string into an unmodifiable set containing the substrings between the commas in the source string.
* The elements in the set will be {@link String#trim() trimmed}.
*
* @param commaDelimitedString input string; may not be null, but need not contain any commas
* @return an unmodifiable set whose elements are the trimmed substrings of the source string; never null
*/
private static Set<String> split( String commaDelimitedString ) {
return setFor(commaDelimitedString.split("\\s*,\\s*"));
}
@Override
public InputStream getResourceContent( Node node ) throws RepositoryException {
if (!node.hasNode(CONTENT_NODE_NAME)) return null;
return node.getProperty(CONTENT_NODE_NAME + "/" + DATA_PROP_NAME).getBinary().getStream();
}
@Override
public long getResourceLength( Node node ) throws RepositoryException {
if (!node.hasNode(CONTENT_NODE_NAME)) {
return -1;
}
return node.getProperty(CONTENT_NODE_NAME + "/" + DATA_PROP_NAME).getLength();
}
@Override
public Date getLastModified( Node node ) throws RepositoryException {
if (!node.hasNode(CONTENT_NODE_NAME)) return null;
return node.getProperty(CONTENT_NODE_NAME + "/" + MODIFIED_PROP_NAME).getDate().getTime();
}
@Override
public boolean isFolder( Node node ) throws RepositoryException {
return !isFile(node) && !isContent(node);
}
/**
* @param node the node to check
* @return true if {@code node}'s primary type is one of the types in {@link #filePrimaryTypes}; may not be null
* @throws RepositoryException if an error occurs checking the node's primary type
*/
@Override
public boolean isFile( Node node ) throws RepositoryException {
for (String nodeType : filePrimaryTypes) {
if (node.isNodeType(nodeType)) return true;
}
return false;
}
/**
* @param node the node to check
* @return true if {@code node}'s primary type is one of the types in {@link #contentPrimaryTypes}; may not be null
* @throws RepositoryException if an error occurs checking the node's primary type
*/
private boolean isContent( Node node ) throws RepositoryException {
for (String nodeType : contentPrimaryTypes) {
if (node.isNodeType(nodeType)) return true;
}
return false;
}
@Override
public void createFile( Node parentNode,
String fileName ) throws RepositoryException {
Node resourceNode = parentNode.addNode(fileName, newResourcePrimaryType);
Node contentNode = resourceNode.addNode(CONTENT_NODE_NAME, newContentPrimaryType);
contentNode.setProperty(DATA_PROP_NAME, "");
contentNode.setProperty(MODIFIED_PROP_NAME, Calendar.getInstance());
contentNode.setProperty(ENCODING_PROP_NAME, "UTF-8");
contentNode.setProperty(MIME_TYPE_PROP_NAME, "text/plain");
}
@Override
public void createFolder( Node parentNode,
String folderName ) throws RepositoryException {
parentNode.addNode(folderName, newFolderPrimaryType);
}
@Override
public long setContent( Node parentNode,
String resourceName,
InputStream newContent,
String contentType,
String characterEncoding ) throws RepositoryException, IOException {
Node contentNode;
if (parentNode.hasNode(CONTENT_NODE_NAME)) {
contentNode = parentNode.getNode(CONTENT_NODE_NAME);
} else {
contentNode = parentNode.addNode(CONTENT_NODE_NAME, newContentPrimaryType);
}
// contentNode.setProperty(MIME_TYPE_PROP_NAME, contentType != null ? contentType : "application/octet-stream");
contentNode.setProperty(ENCODING_PROP_NAME, characterEncoding != null ? characterEncoding : "UTF-8");
org.modeshape.jcr.api.Binary binary = (org.modeshape.jcr.api.Binary)parentNode.getSession()
.getValueFactory()
.createBinary(newContent);
contentNode.setProperty(DATA_PROP_NAME, binary);
contentNode.setProperty(MODIFIED_PROP_NAME, Calendar.getInstance());
// Copy the content to the property, THEN re-read the content from the Binary value to avoid discarding the first
// bytes of the stream
if (contentType == null) {
contentType = binary.getMimeType(resourceName);
}
if (contentType != null) {
contentNode.setProperty(MIME_TYPE_PROP_NAME, contentType);
}
return contentNode.getProperty(DATA_PROP_NAME).getLength();
}
}