blob: 99b042fd9f2ac09f7f23b45d0b5a0f7245cdd0c5 [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.cocoon.components.repository;
import java.io.IOException;
import java.util.Iterator;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.Serviceable;
import org.apache.avalon.framework.thread.ThreadSafe;
import org.apache.excalibur.source.ModifiableSource;
import org.apache.excalibur.source.ModifiableTraversableSource;
import org.apache.excalibur.source.Source;
import org.apache.excalibur.source.SourceException;
import org.apache.excalibur.source.SourceResolver;
import org.apache.excalibur.source.SourceUtil;
import org.apache.excalibur.source.TraversableSource;
/**
* SourceRepository implementation.
*
* @version $Id$
*/
public class SourceRepositoryImpl extends AbstractLogEnabled
implements Serviceable, ThreadSafe, SourceRepository {
private RepositoryInterceptor m_interceptor = new RepositoryInterceptorBase(){};
private SourceResolver m_resolver;
// ---------------------------------------------------- lifecycle
public SourceRepositoryImpl() {
}
public void service(ServiceManager manager) throws ServiceException {
m_resolver = (SourceResolver) manager.lookup(SourceResolver.ROLE);
if (manager.hasService(RepositoryInterceptor.ROLE)) {
m_interceptor = (RepositoryInterceptor) manager.lookup(RepositoryInterceptor.ROLE);
}
}
// ---------------------------------------------------- repository operations
public int save(String in, String out) throws IOException, SourceException {
if (getLogger().isDebugEnabled()) {
getLogger().debug("save: "+in+"/"+out);
}
Source source = null;
Source destination = null;
try {
destination = m_resolver.resolveURI(out);
if (!(destination instanceof ModifiableSource)) {
final String message =
"Conflict during save(): protocol is not modifiable.";
getLogger().warn(message);
return STATUS_CONFLICT;
}
final boolean exists = destination.exists();
if (!exists) {
Source parent = ((TraversableSource) destination).getParent();
if (!parent.exists()) {
final String message =
"Conflict during save(): parent does not exist.";
getLogger().warn(message);
return STATUS_CONFLICT;
}
}
if (destination instanceof TraversableSource) {
if (((TraversableSource) destination).isCollection()) {
final String message =
"Conflict during save(): destination is a collection.";
getLogger().warn(message);
return STATUS_CONFLICT;
}
}
int status;
if (exists) {
status = STATUS_NO_CONTENT;
}
else {
status = STATUS_CREATED;
}
source = m_resolver.resolveURI(in);
m_interceptor.preStoreSource(destination);
SourceUtil.copy(source,destination);
m_interceptor.postStoreSource(destination);
return status;
}
catch (IOException e) {
getLogger().error("Unexpected exception during save().",e);
throw e;
}
finally {
if (source != null) {
m_resolver.release(source);
}
if (destination != null) {
m_resolver.release(destination);
}
}
}
public int makeCollection(String location) throws IOException, SourceException {
if (getLogger().isDebugEnabled()) {
getLogger().debug("makeCollection: " + location);
}
Source source = null;
Source parent = null;
try {
source = m_resolver.resolveURI(location);
if (source.exists()) {
final String message =
"makeCollection() is not allowed: the resource already exists.";
getLogger().warn(message);
return STATUS_NOT_ALLOWED;
}
if (!(source instanceof ModifiableTraversableSource)) {
final String message =
"Conflict in makeCollection(): source is not modifiable traversable.";
getLogger().warn(message);
return STATUS_CONFLICT;
}
parent = ((TraversableSource) source).getParent();
if (!parent.exists()) {
final String message = "Conflict in makeCollection(): parent does not exist.";
getLogger().warn(message);
return STATUS_CONFLICT;
}
m_interceptor.preStoreSource(source);
((ModifiableTraversableSource) source).makeCollection();
m_interceptor.postStoreSource(source);
return STATUS_CREATED;
}
catch (IOException e) {
getLogger().error("Unexpected exception during makeCollection().",e);
throw e;
}
finally {
if (source != null) {
m_resolver.release(source);
}
if (parent != null) {
m_resolver.release(parent);
}
}
}
/**
* Removes a Source and all of its descendants.
*
* @param location the location of the source to remove.
* @return a http status code describing the exit status.
* @throws IOException
*/
public int remove(String location) throws IOException, SourceException {
Source source = null;
try {
source = m_resolver.resolveURI(location);
if (!source.exists()) {
final String message =
"Trying to remove a non-existing source.";
getLogger().warn(message);
return STATUS_NOT_FOUND;
}
return remove(source);
}
catch (IOException e) {
getLogger().error("Unexpected exception during remove().",e);
throw e;
}
finally {
if (source != null) {
m_resolver.release(source);
}
}
}
private int remove(Source source) throws SourceException {
if (getLogger().isDebugEnabled()) {
getLogger().debug("remove: " + source.getURI());
}
if (!(source instanceof ModifiableSource)) {
final String message =
"Conflict in remove(): source is not modifiable";
getLogger().warn(message);
return STATUS_CONFLICT;
}
if (source instanceof TraversableSource) {
if (((TraversableSource) source).isCollection()) {
int status = STATUS_OK;
final Iterator iter = ((TraversableSource) source).getChildren().iterator();
while (iter.hasNext()) {
status = remove((Source) iter.next());
if (status != STATUS_OK) {
return status;
}
}
}
}
m_interceptor.preRemoveSource(source);
((ModifiableSource) source).delete();
m_interceptor.postRemoveSource(source);
return STATUS_OK;
}
public int move(String from, String to, boolean recurse, boolean overwrite)
throws IOException, SourceException {
int status = doCopy(from,to,recurse,overwrite);
if (status == STATUS_CREATED || status == STATUS_NO_CONTENT) {
// TODO: handle partially successful copies
remove(from);
// TODO: remove properties
}
return status;
}
public int copy(String from, String to, boolean recurse, boolean overwrite)
throws IOException, SourceException {
return doCopy(from,to,recurse,overwrite);
}
private int doCopy(String from, String to, boolean recurse, boolean overwrite)
throws IOException {
if (getLogger().isDebugEnabled()) {
getLogger().debug("copy: " + from + " -> " + to
+ " (recurse=" + recurse + ", overwrite=" + overwrite + ")");
}
if (from != null && from.equals(to)) {
final String message =
"copy() is forbidden: " +
"The source and destination URIs are the same.";
getLogger().warn(message);
return STATUS_FORBIDDEN;
}
Source source = null;
Source destination = null;
try {
source = m_resolver.resolveURI(from);
destination = m_resolver.resolveURI(to);
if (!source.exists()) {
final String message =
"Trying to copy a non-existing source.";
getLogger().warn(message);
return STATUS_NOT_FOUND;
}
int status;
if (destination.exists()) {
if (!overwrite) {
final String message =
"Failed precondition in copy(): " +
"Destination resource already exists.";
getLogger().warn(message);
return STATUS_PRECONDITION_FAILED;
}
remove(destination);
status = STATUS_NO_CONTENT;
}
else {
Source parent = null;
try {
parent = getParent(destination);
if (!parent.exists()) {
final String message =
"Conflict in copy(): " +
"A resource cannot be created at the destination " +
"until one or more intermediate collections have been " +
"created.";
getLogger().warn(message);
return STATUS_CONFLICT;
}
}
finally {
if (parent != null) {
m_resolver.release(parent);
}
}
status = STATUS_CREATED;
}
copy(source,destination,recurse);
return status;
}
catch (IOException e) {
getLogger().error("Unexpected exception during copy().",e);
throw e;
}
finally {
if (source != null) {
m_resolver.release(source);
}
if (destination != null) {
m_resolver.release(destination);
}
}
}
private int copy(Source source, Source destination, boolean recurse)
throws IOException {
if (getLogger().isDebugEnabled()) {
getLogger().debug("copy: " + source.getURI() + " -> " + destination.getURI());
}
if (source instanceof TraversableSource) {
final TraversableSource origin = (TraversableSource) source;
ModifiableTraversableSource target = null;
if (origin.isCollection()) {
if (!(destination instanceof ModifiableTraversableSource)) {
final String message = "copy() is forbidden: " +
"Cannot create a collection at the indicated destination.";
getLogger().warn(message);
return STATUS_FORBIDDEN;
}
// TODO: copy properties
target = ((ModifiableTraversableSource) destination);
m_interceptor.preStoreSource(target);
target.makeCollection();
m_interceptor.postStoreSource(target);
if (recurse) {
Iterator children = origin.getChildren().iterator();
while (children.hasNext()) {
TraversableSource child = (TraversableSource) children.next();
/*int status =*/ copy(child,target.getChild(child.getName()),recurse);
// TODO: record this status
// according to the spec we must continue moving files even though
// a part of the move has not succeeded
}
}
return STATUS_CREATED;
}
}
if (destination instanceof ModifiableSource) {
// TODO: copy properties
m_interceptor.preStoreSource(destination);
SourceUtil.copy(source,destination);
m_interceptor.postStoreSource(destination);
}
else {
final String message = "copy() is forbidden: " +
"Cannot create a resource at the indicated destination.";
getLogger().warn(message);
return STATUS_FORBIDDEN;
}
return STATUS_CREATED;
}
private Source getParent(Source source) throws IOException {
if (source instanceof TraversableSource) {
return ((TraversableSource) source).getParent();
}
else {
String uri = source.getURI();
int index = uri.lastIndexOf('/');
if (index != -1) {
return m_resolver.resolveURI(uri.substring(index+1));
}
}
return null;
}
}