/*
*
* 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.webdav;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.modeshape.common.i18n.TextI18n;
import org.modeshape.common.logging.Logger;
import org.modeshape.common.util.FileUtil;
import org.modeshape.common.util.IoUtil;
import org.modeshape.common.util.StringUtil;
import org.modeshape.webdav.exceptions.WebdavException;
/**
* Reference Implementation of WebdavStore
*
* @author joa
* @author re
* @author hchiorea@redhat.com
*/
public class LocalFileSystemStore implements IWebdavStore {
private static Logger LOG = Logger.getLogger(LocalFileSystemStore.class);
private static int BUF_SIZE = 65536;
private File root = null;
public LocalFileSystemStore( File root ) {
this.root = root;
}
@Override
public void destroy() {
}
@Override
public ITransaction begin( Principal principal ) throws WebdavException {
LOG.trace("LocalFileSystemStore.begin()");
if (!root.exists()) {
if (!root.mkdirs()) {
throw new WebdavException("root path: " + root.getAbsolutePath() + " does not exist and could not be created");
}
}
return null;
}
@Override
public void checkAuthentication( ITransaction transaction ) throws SecurityException {
LOG.trace("LocalFileSystemStore.checkAuthentication()");
// do nothing
}
@Override
public void commit( ITransaction transaction ) throws WebdavException {
// do nothing
LOG.trace("LocalFileSystemStore.commit()");
}
@Override
public void rollback( ITransaction transaction ) throws WebdavException {
// do nothing
LOG.trace("LocalFileSystemStore.rollback()");
}
@Override
public void createFolder( ITransaction transaction,
String uri ) throws WebdavException {
LOG.trace("LocalFileSystemStore.createFolder(" + uri + ")");
File file = new File(root, uri);
if (!file.mkdir()) {
throw new WebdavException("cannot create folder: " + uri);
}
}
@Override
public void createResource( ITransaction transaction,
String uri ) throws WebdavException {
LOG.trace("LocalFileSystemStore.createResource(" + uri + ")");
File file = new File(root, uri);
try {
if (!file.createNewFile()) {
throw new WebdavException("cannot create file: " + uri);
}
} catch (IOException e) {
LOG.error(new TextI18n("LocalFileSystemStore.createResource(" + uri + ") failed"));
throw new WebdavException(e);
}
}
@Override
public long setResourceContent( ITransaction transaction,
String uri,
InputStream is,
String contentType,
String characterEncoding ) throws WebdavException {
LOG.trace("LocalFileSystemStore.setResourceContent(" + uri + ")");
File file = new File(root, uri);
try {
OutputStream os = new BufferedOutputStream(new FileOutputStream(file), BUF_SIZE);
try {
int read;
byte[] copyBuffer = new byte[BUF_SIZE];
while ((read = is.read(copyBuffer, 0, copyBuffer.length)) != -1) {
os.write(copyBuffer, 0, read);
}
} finally {
try {
is.close();
} finally {
os.close();
}
}
} catch (IOException e) {
LOG.error(new TextI18n("LocalFileSystemStore.setResourceContent(" + uri + ") failed"));
throw new WebdavException(e);
}
long length = -1;
try {
length = file.length();
} catch (SecurityException e) {
LOG.error(new TextI18n("LocalFileSystemStore.setResourceContent(" + uri + ") failed" + "\nCan't get file.length"));
}
return length;
}
@Override
public String[] getChildrenNames( ITransaction transaction,
String uri ) throws WebdavException {
LOG.trace("LocalFileSystemStore.getChildrenNames(" + uri + ")");
File file = new File(root, uri);
String[] childrenNames = null;
if (file.isDirectory()) {
File[] children = file.listFiles();
List<String> childList = new ArrayList<String>();
String name = null;
for (int i = 0; i < children.length; i++) {
name = children[i].getName();
childList.add(name);
LOG.trace("Child " + i + ": " + name);
}
childrenNames = new String[childList.size()];
childrenNames = childList.toArray(childrenNames);
}
return childrenNames;
}
@Override
public void removeObject( ITransaction transaction,
String uri ) throws WebdavException {
File file = new File(root, uri);
boolean success = file.delete();
LOG.trace("LocalFileSystemStore.removeObject(" + uri + ")=" + success);
if (!success) {
throw new WebdavException("cannot delete object: " + uri);
}
}
@Override
public InputStream getResourceContent( ITransaction transaction,
String uri ) throws WebdavException {
LOG.trace("LocalFileSystemStore.getResourceContent(" + uri + ")");
File file = new File(root, uri);
InputStream in;
try {
in = new BufferedInputStream(new FileInputStream(file));
} catch (IOException e) {
LOG.error(new TextI18n("LocalFileSystemStore.getResourceContent(" + uri + ") failed"));
throw new WebdavException(e);
}
return in;
}
@Override
public long getResourceLength( ITransaction transaction,
String resourceUri ) throws WebdavException {
LOG.trace("LocalFileSystemStore.getResourceLength(" + resourceUri + ")");
File file = new File(root, resourceUri);
return file.length();
}
@Override
public StoredObject getStoredObject( ITransaction transaction,
String uri ) {
StoredObject so = null;
File file = new File(root, uri);
if (file.exists()) {
so = new StoredObject();
so.setFolder(file.isDirectory());
so.setLastModified(new Date(file.lastModified()));
so.setCreationDate(new Date(file.lastModified()));
so.setResourceLength(getResourceLength(transaction, uri));
}
return so;
}
@Override
public Map<String, String> setCustomProperties( ITransaction transaction,
String resourceUri,
Map<String, Object> propertiesToSet,
List<String> propertiesToRemove ) {
LOG.trace("LocalFileSystemStore.setCustomProperties(" + resourceUri + ")");
File propertiesFile = propertiesFileForResource(resourceUri);
try {
if (!propertiesFile.exists() && !propertiesToSet.isEmpty()) {
propertiesFile.createNewFile();
}
if (!propertiesFile.exists()) {
return null;
}
Map<String, Object> updatedProperties = readExistingProperties(propertiesFile);
for (String propertyToRemove : propertiesToRemove) {
updatedProperties.remove(propertyToRemove);
}
updatedProperties.putAll(propertiesToSet);
if (updatedProperties.isEmpty()) {
FileUtil.delete(propertiesFile);
} else {
writeProperties(propertiesFile, updatedProperties);
}
} catch (IOException e) {
throw new WebdavException(e);
}
return null;
}
private File propertiesFileForResource( String resourceUri ) {
File file = new File(root, resourceUri);
if (!file.exists()) {
throw new WebdavException(resourceUri + " does not represent an existing file or directory");
}
File propertiesFileParent = file.isFile() ? file.getParentFile() : file;
if (!propertiesFileParent.canWrite()) {
throw new WebdavException("Cannot write into the " + propertiesFileParent.getAbsolutePath()
+ " folder. Make sure that the FS permissions are correct");
}
String propertiesFileName = file.getName() + "_webdav.properties";
return new File(propertiesFileParent, propertiesFileName);
}
private Map<String, Object> readExistingProperties( File propertiesFile ) throws IOException {
Map<String, Object> properties = new HashMap<String, Object>();
String fileContent = IoUtil.read(propertiesFile);
if (StringUtil.isBlank(fileContent)) {
return properties;
}
String[] keyValuePairs = fileContent.split(";");
for (String keyValuePair : keyValuePairs) {
String[] keyValue = keyValuePair.split("=");
if (keyValue.length != 2) {
continue;
}
properties.put(keyValue[0], valueFromString(keyValue[1]));
}
return properties;
}
private Object valueFromString( String value ) {
if (value.startsWith("[") && value.endsWith("]")) {
value = value.replaceAll("\\[", "").replaceAll("\\]", "");
List<Object> array = new ArrayList<Object>();
for (String element : value.split(",")) {
array.add(valueFromString(element));
}
return array;
}
return value;
}
private void writeProperties( File propertiesFile,
Map<String, Object> properties ) throws IOException {
StringBuilder content = new StringBuilder();
for (Iterator<Map.Entry<String, Object>> it = properties.entrySet().iterator(); it.hasNext();) {
Map.Entry<String, Object> entry = it.next();
content.append(entry.getKey()).append("=").append(valueToString(entry.getValue()));
if (it.hasNext()) {
content.append(";");
}
}
IoUtil.write(content.toString(), propertiesFile);
}
private String valueToString( Object value ) {
if (value instanceof List) {
StringBuilder builder = new StringBuilder("[");
for (Iterator<?> it = ((List<?>)value).iterator(); it.hasNext();) {
builder.append(valueToString(it.next()));
if (it.hasNext()) {
builder.append(",");
}
}
builder.append("]");
return builder.toString();
}
return value.toString();
}
@Override
public Map<String, Object> getCustomProperties( ITransaction transaction,
String resourceUri ) {
LOG.trace("LocalFileSystemStore.getCustomProperties(" + resourceUri + ")");
try {
File propertiesFile = propertiesFileForResource(resourceUri);
if (propertiesFile.exists() && propertiesFile.canRead()) {
return readExistingProperties(propertiesFile);
}
return Collections.emptyMap();
} catch (IOException e) {
throw new WebdavException(e);
}
}
@Override
public Map<String, String> getCustomNamespaces( ITransaction transaction,
String resourceUri ) {
// the default FS based implementation does not use custom namespaces
return Collections.emptyMap();
}
}