
it.could.webdav.replication.DAVReplica Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of webdav Show documentation
Show all versions of webdav Show documentation
A Simple Approach to WebDAV (Servlet Implementation)
The newest version!
/* ========================================================================== *
* Copyright (C) 2004-2006, Pier Fumagalli *
* All rights reserved. *
* ========================================================================== *
* *
* 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 . *
* *
* 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 it.could.webdav.replication;
import it.could.util.StreamTools;
import it.could.util.http.WebDavClient;
import it.could.util.location.Location;
import it.could.webdav.DAVListener;
import it.could.webdav.DAVLogger;
import it.could.webdav.DAVRepository;
import it.could.webdav.DAVResource;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* TODO: Document this class.
*
* @author Pier Fumagalli
*/
public class DAVReplica extends Thread implements DAVListener {
private static final int SYNCHRONIZE = -1;
private final DAVRepository repository;
private final DAVLogger logger;
private final Location location;
private final List actions = new ArrayList();
public DAVReplica(DAVRepository repository, Location location,
DAVLogger logger)
throws IOException {
this.location = new WebDavClient(location).getLocation();
this.repository = repository;
this.logger = logger;
this.start();
}
public void synchronize()
throws IOException {
this.logger.log("Scheduling full synchronization");
this.notify(this.repository.getResource((String)null), SYNCHRONIZE);
}
public void notify(DAVResource resource, int event) {
this.logger.debug("Event for \"" + resource.getRelativePath() + "\"");
if (resource.getRepository() != this.repository) return;
synchronized (this.actions) {
this.actions.add(new Action(resource, event));
this.actions.notify();
}
}
public void run() {
this.logger.debug("Starting background replica thread on " + location);
while (true) try {
final DAVReplica.Action array[];
synchronized(this.actions) {
try {
if (this.actions.isEmpty()) this.actions.wait();
final int s = this.actions.size();
array = (Action []) this.actions.toArray(new Action[s]);
this.actions.clear();
} catch (InterruptedException exception) {
this.logger.debug("Exiting background replica thread");
return;
}
}
for (int x = 0; x < array.length; x ++) try {
this.replicate(array[x]);
} catch (Throwable throwable) {
final String path = array[x].resource.getRelativePath();
final String message = "Error synchronizing resource " + path;
this.logger.log(message, throwable);
}
} catch (Throwable throwable) {
this.logger.log("Replica thread attempted suicide", throwable);
}
}
private void replicate(DAVReplica.Action action) {
final DAVResource resource = action.resource;
if (action.event == SYNCHRONIZE) {
this.synchronize(resource);
} else try {
final String path = resource.getParent().getRelativePath();
final Location location = this.location.resolve(path);
final WebDavClient client = new WebDavClient(location);
final String child = resource.getName();
switch(action.event) {
case RESOURCE_CREATED:
case RESOURCE_MODIFIED:
this.logger.debug("Putting resource " + path);
this.put(resource, client);
break;
case RESOURCE_REMOVED:
case COLLECTION_REMOVED:
this.logger.debug("Deleting resource " + path);
client.delete(child);
break;
case COLLECTION_CREATED:
this.logger.debug("Creating collection " + path);
client.mkcol(child);
break;
}
} catch (IOException exception) {
String message = "Error replicating " + resource.getRelativePath();
this.logger.log(message, exception);
}
}
private void put(DAVResource resource, WebDavClient client)
throws IOException {
final String name = resource.getName();
final long length = resource.getContentLength().longValue();
final OutputStream output = client.put(name, length);
final InputStream input = resource.read();
StreamTools.copy(input, output);
}
private void synchronize(DAVResource resource) {
/* Figure out the path of the resource */
final String path = resource.getRelativePath();
/* If it's a file or null, just skip the whole thing */
if (! resource.isCollection()) {
this.logger.log("Synchronization on non-collection " + path);
return;
}
/* Open a webdav client to the collection to synchronize */
this.logger.log("Synchronizing collection " + path);
final WebDavClient client;
try {
final Location location = this.location.resolve(path);
client = new WebDavClient(location);
} catch (IOException exception) {
this.logger.log("Error creating WebDAV client", exception);
return;
}
/* Create a list of all children from the DAV client */
final Set children = new HashSet();
for (Iterator iter = client.iterator(); iter.hasNext(); )
children.add(iter.next());
/* Process all resource children one by one and ensure they exist */
for (Iterator iter = resource.getChildren(); iter.hasNext(); ) {
final DAVResource child = (DAVResource) iter.next();
final String name = child.getName();
/* Remove this from the resources that will be removed later */
children.remove(name);
/* If the client doesn't have this child, add it to the replica */
if (! client.hasChild(name)) try {
if (child.isCollection()) {
this.logger.debug("Client doesn't have collection " + name);
client.mkcol(name);
this.synchronize(child);
} else {
this.logger.debug("Client doesn't have resource " + name);
this.put(child, client);
}
} catch (IOException exception) {
this.logger.log("Error creating new child " + name, exception);
/* If this child is a collection, it must be a collection on dav */
} else if (child.isCollection()) try {
if (!client.isCollection(name)) {
this.logger.debug("Recreating collection " + name);
client.delete(name).mkcol(name);
}
this.synchronize(child);
} catch (IOException exception) {
this.logger.log("Error creating collection " + name, exception);
/* Ok, the resource is a normal one, verify size and timestamp */
} else try {
final Date rlast = child.getLastModified();
final Date dlast = client.getLastModified(name);
if ((rlast != null) && (rlast.equals(dlast))) {
final Long rlen = child.getContentLength();
final long dlen = client.getContentLength(name);
if ((rlen == null) || (rlen.longValue() != dlen)) {
this.logger.debug("Resending resource " + name);
this.put(child, client.delete(name));
}
}
} catch (IOException exception) {
this.logger.log("Error resending resource " + name, exception);
}
}
/* Any other child that was not removed above, will go away now! */
for (Iterator iter = children.iterator(); iter.hasNext(); ) {
final String name = (String) iter.next();
try {
this.logger.debug("Removing leftovers " + name);
client.delete(name);
} catch (IOException exception) {
this.logger.log("Error removing left over " + name, exception);
}
}
}
private static final class Action {
final DAVResource resource;
final int event;
private Action(DAVResource resource, int event) {
this.resource = resource;
this.event = event;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy