001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.components.repository;
018:
019: import java.io.IOException;
020: import java.util.Iterator;
021:
022: import org.apache.avalon.framework.logger.AbstractLogEnabled;
023: import org.apache.avalon.framework.service.ServiceException;
024: import org.apache.avalon.framework.service.ServiceManager;
025: import org.apache.avalon.framework.service.Serviceable;
026: import org.apache.avalon.framework.thread.ThreadSafe;
027: import org.apache.excalibur.source.ModifiableSource;
028: import org.apache.excalibur.source.ModifiableTraversableSource;
029: import org.apache.excalibur.source.Source;
030: import org.apache.excalibur.source.SourceException;
031: import org.apache.excalibur.source.SourceResolver;
032: import org.apache.excalibur.source.SourceUtil;
033: import org.apache.excalibur.source.TraversableSource;
034:
035: /**
036: * SourceRepository implementation.
037: *
038: * @version $Id: SourceRepositoryImpl.java 433543 2006-08-22 06:22:54Z crossley $
039: */
040: public class SourceRepositoryImpl extends AbstractLogEnabled implements
041: Serviceable, ThreadSafe, SourceRepository {
042:
043: private RepositoryInterceptor m_interceptor = new RepositoryInterceptorBase() {
044: };
045: private SourceResolver m_resolver;
046:
047: // ---------------------------------------------------- lifecycle
048:
049: public SourceRepositoryImpl() {
050: }
051:
052: public void service(ServiceManager manager) throws ServiceException {
053: m_resolver = (SourceResolver) manager
054: .lookup(SourceResolver.ROLE);
055: if (manager.hasService(RepositoryInterceptor.ROLE)) {
056: m_interceptor = (RepositoryInterceptor) manager
057: .lookup(RepositoryInterceptor.ROLE);
058: }
059: }
060:
061: // ---------------------------------------------------- repository operations
062:
063: public int save(String in, String out) throws IOException,
064: SourceException {
065:
066: if (getLogger().isDebugEnabled()) {
067: getLogger().debug("save: " + in + "/" + out);
068: }
069:
070: Source source = null;
071: Source destination = null;
072: try {
073: destination = m_resolver.resolveURI(out);
074:
075: if (!(destination instanceof ModifiableSource)) {
076: final String message = "Conflict during save(): protocol is not modifiable.";
077: getLogger().warn(message);
078: return STATUS_CONFLICT;
079: }
080:
081: final boolean exists = destination.exists();
082: if (!exists) {
083: Source parent = ((TraversableSource) destination)
084: .getParent();
085: if (!parent.exists()) {
086: final String message = "Conflict during save(): parent does not exist.";
087: getLogger().warn(message);
088: return STATUS_CONFLICT;
089: }
090: }
091:
092: if (destination instanceof TraversableSource) {
093: if (((TraversableSource) destination).isCollection()) {
094: final String message = "Conflict during save(): destination is a collection.";
095: getLogger().warn(message);
096: return STATUS_CONFLICT;
097: }
098: }
099:
100: int status;
101: if (exists) {
102: status = STATUS_OK;
103: } else {
104: status = STATUS_CREATED;
105: }
106:
107: source = m_resolver.resolveURI(in);
108: m_interceptor.preStoreSource(destination);
109: SourceUtil.copy(source, destination);
110: m_interceptor.postStoreSource(destination);
111: return status;
112: } catch (IOException e) {
113: getLogger().error("Unexpected exception during save().", e);
114: throw e;
115: } finally {
116: if (source != null) {
117: m_resolver.release(source);
118: }
119: if (destination != null) {
120: m_resolver.release(destination);
121: }
122: }
123:
124: }
125:
126: public int makeCollection(String location) throws IOException,
127: SourceException {
128:
129: if (getLogger().isDebugEnabled()) {
130: getLogger().debug("makeCollection: " + location);
131: }
132:
133: Source source = null;
134: Source parent = null;
135: try {
136: source = m_resolver.resolveURI(location);
137:
138: if (source.exists()) {
139: final String message = "makeCollection() is not allowed: the resource already exists.";
140: getLogger().warn(message);
141: return STATUS_NOT_ALLOWED;
142: }
143:
144: if (!(source instanceof ModifiableTraversableSource)) {
145: final String message = "Conflict in makeCollection(): source is not modifiable traversable.";
146: getLogger().warn(message);
147: return STATUS_CONFLICT;
148: }
149:
150: parent = ((TraversableSource) source).getParent();
151: if (!parent.exists()) {
152: final String message = "Conflict in makeCollection(): parent does not exist.";
153: getLogger().warn(message);
154: return STATUS_CONFLICT;
155: }
156:
157: m_interceptor.preStoreSource(source);
158: ((ModifiableTraversableSource) source).makeCollection();
159: m_interceptor.postStoreSource(source);
160: return STATUS_CREATED;
161: } catch (IOException e) {
162: getLogger().error(
163: "Unexpected exception during makeCollection().", e);
164: throw e;
165: } finally {
166: if (source != null) {
167: m_resolver.release(source);
168: }
169: if (parent != null) {
170: m_resolver.release(parent);
171: }
172: }
173: }
174:
175: /**
176: * Removes a Source and all of its descendants.
177: *
178: * @param location the location of the source to remove.
179: * @return a http status code describing the exit status.
180: * @throws IOException
181: */
182: public int remove(String location) throws IOException,
183: SourceException {
184:
185: Source source = null;
186: try {
187: source = m_resolver.resolveURI(location);
188: if (!source.exists()) {
189: final String message = "Trying to remove a non-existing source.";
190: getLogger().warn(message);
191: return STATUS_NOT_FOUND;
192: }
193: return remove(source);
194: } catch (IOException e) {
195: getLogger().error("Unexpected exception during remove().",
196: e);
197: throw e;
198: } finally {
199: if (source != null) {
200: m_resolver.release(source);
201: }
202: }
203:
204: }
205:
206: private int remove(Source source) throws SourceException {
207:
208: if (getLogger().isDebugEnabled()) {
209: getLogger().debug("remove: " + source.getURI());
210: }
211:
212: if (!(source instanceof ModifiableSource)) {
213: final String message = "Conflict in remove(): source is not modifiable";
214: getLogger().warn(message);
215: return STATUS_CONFLICT;
216: }
217:
218: if (source instanceof TraversableSource) {
219: if (((TraversableSource) source).isCollection()) {
220: int status = STATUS_OK;
221: final Iterator iter = ((TraversableSource) source)
222: .getChildren().iterator();
223: while (iter.hasNext()) {
224: status = remove((Source) iter.next());
225: if (status != STATUS_OK) {
226: return status;
227: }
228: }
229: }
230: }
231: m_interceptor.preRemoveSource(source);
232: ((ModifiableSource) source).delete();
233: m_interceptor.postRemoveSource(source);
234: return STATUS_OK;
235: }
236:
237: public int move(String from, String to, boolean recurse,
238: boolean overwrite) throws IOException, SourceException {
239:
240: int status = doCopy(from, to, recurse, overwrite);
241: if (status == STATUS_CREATED || status == STATUS_NO_CONTENT) {
242: // TODO: handle partially successful copies
243: remove(from);
244: // TODO: remove properties
245: }
246: return status;
247: }
248:
249: public int copy(String from, String to, boolean recurse,
250: boolean overwrite) throws IOException, SourceException {
251:
252: return doCopy(from, to, recurse, overwrite);
253:
254: }
255:
256: private int doCopy(String from, String to, boolean recurse,
257: boolean overwrite) throws IOException {
258:
259: if (getLogger().isDebugEnabled()) {
260: getLogger().debug(
261: "copy: " + from + " -> " + to + " (recurse="
262: + recurse + ", overwrite=" + overwrite
263: + ")");
264: }
265:
266: if (from != null && from.equals(to)) {
267: final String message = "copy() is forbidden: "
268: + "The source and destination URIs are the same.";
269: getLogger().warn(message);
270: return STATUS_FORBIDDEN;
271: }
272: Source source = null;
273: Source destination = null;
274: try {
275: source = m_resolver.resolveURI(from);
276: destination = m_resolver.resolveURI(to);
277: if (!source.exists()) {
278: final String message = "Trying to copy a non-existing source.";
279: getLogger().warn(message);
280: return STATUS_NOT_FOUND;
281: }
282: int status;
283: if (destination.exists()) {
284: if (!overwrite) {
285: final String message = "Failed precondition in copy(): "
286: + "Destination resource already exists.";
287: getLogger().warn(message);
288: return STATUS_PRECONDITION_FAILED;
289: }
290: remove(destination);
291: status = STATUS_NO_CONTENT;
292: } else {
293: Source parent = null;
294: try {
295: parent = getParent(destination);
296: if (!parent.exists()) {
297: final String message = "Conflict in copy(): "
298: + "A resource cannot be created at the destination "
299: + "until one or more intermediate collections have been "
300: + "created.";
301: getLogger().warn(message);
302: return STATUS_CONFLICT;
303: }
304: } finally {
305: if (parent != null) {
306: m_resolver.release(parent);
307: }
308: }
309: status = STATUS_CREATED;
310: }
311: copy(source, destination, recurse);
312: return status;
313: } catch (IOException e) {
314: getLogger().error("Unexpected exception during copy().", e);
315: throw e;
316: } finally {
317: if (source != null) {
318: m_resolver.release(source);
319: }
320: if (destination != null) {
321: m_resolver.release(destination);
322: }
323: }
324: }
325:
326: private int copy(Source source, Source destination, boolean recurse)
327: throws IOException {
328:
329: if (getLogger().isDebugEnabled()) {
330: getLogger().debug(
331: "copy: " + source.getURI() + " -> "
332: + destination.getURI());
333: }
334:
335: if (source instanceof TraversableSource) {
336: final TraversableSource origin = (TraversableSource) source;
337: ModifiableTraversableSource target = null;
338: if (origin.isCollection()) {
339: if (!(destination instanceof ModifiableTraversableSource)) {
340: final String message = "copy() is forbidden: "
341: + "Cannot create a collection at the indicated destination.";
342: getLogger().warn(message);
343: return STATUS_FORBIDDEN;
344: }
345: // TODO: copy properties
346: target = ((ModifiableTraversableSource) destination);
347: m_interceptor.preStoreSource(target);
348: target.makeCollection();
349: m_interceptor.postStoreSource(target);
350: if (recurse) {
351: Iterator children = origin.getChildren().iterator();
352: while (children.hasNext()) {
353: TraversableSource child = (TraversableSource) children
354: .next();
355: /*int status =*/copy(child, target
356: .getChild(child.getName()), recurse);
357: // TODO: record this status
358: // according to the spec we must continue moving files even though
359: // a part of the move has not succeeded
360: }
361: }
362: return STATUS_CREATED;
363: }
364: }
365: if (destination instanceof ModifiableSource) {
366: // TODO: copy properties
367: m_interceptor.preStoreSource(destination);
368: SourceUtil.copy(source, destination);
369: m_interceptor.postStoreSource(destination);
370: } else {
371: final String message = "copy() is forbidden: "
372: + "Cannot create a resource at the indicated destination.";
373: getLogger().warn(message);
374: return STATUS_FORBIDDEN;
375: }
376: return STATUS_CREATED;
377: }
378:
379: private Source getParent(Source source) throws IOException {
380: if (source instanceof TraversableSource) {
381: return ((TraversableSource) source).getParent();
382: } else {
383: String uri = source.getURI();
384: int index = uri.lastIndexOf('/');
385: if (index != -1) {
386: return m_resolver.resolveURI(uri.substring(index + 1));
387: }
388: }
389: return null;
390: }
391:
392: }
|