| /* |
| * 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; |
| } |
| |
| } |