/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.atomojo.tools.sync;
import java.io.File;
import java.net.URI;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.atomojo.app.client.Entry;
import org.atomojo.app.client.EntryClient;
import org.atomojo.app.client.FeedClient;
import org.atomojo.app.client.FeedDestination;
import org.atomojo.app.client.Link;
import org.atomojo.app.client.StatusException;
import org.infoset.xml.Attribute;
import org.infoset.xml.Document;
import org.infoset.xml.Element;
import org.infoset.xml.Name;
import org.restlet.Client;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.data.ChallengeResponse;
import org.restlet.data.ChallengeScheme;
import org.restlet.data.MediaType;
import org.restlet.data.Method;
import org.restlet.data.Reference;
import org.restlet.data.Status;
import org.restlet.representation.Representation;
/**
*
* @author alex
*/
public class FeedSynchronizer implements Runnable {
public static final URI ATOM_NAMESPACE = URI.create("http://www.w3.org/2005/Atom");
public static final Name CONTENT_NAME = Name.create(ATOM_NAMESPACE,"content");
Logger log;
Link source;
Link target;
boolean additive;
int errorCount;
public FeedSynchronizer(Link source,Link target) {
this.source = source;
this.target = target;
this.additive = true;
this.log = Logger.getLogger(FeedSynchronizer.class.getName());
}
public void setAdditive(boolean flag)
{
this.additive = flag;
}
public void setLogger(Logger log)
{
this.log = log;
}
public int getErrorCount() {
return errorCount;
}
public void run() {
errorCount = 0;
final FeedClient sourceClient = new FeedClient(source.getLink());
sourceClient.setIdentity(source.getIdentity());
final FeedClient targetClient = new FeedClient(target.getLink());
targetClient.setIdentity(target.getIdentity());
try {
Response response = sourceClient.get(new FeedDestination() {
Set<UUID> entries = additive ? null : new TreeSet<UUID>();
boolean ok = true;
public void onFeed(Document feedDoc) {
URI baseURI = feedDoc.getBaseURI();
// Make sure to remove the xml:base on the feed element for storage;
feedDoc.getDocumentElement().getAttributes().remove(Attribute.XML_BASE);
if (!targetClient.exists()) {
log.info("Creating target feed "+targetClient.getLocation());
Status status = targetClient.create(feedDoc);
if (!status.isSuccess()) {
log.severe("Cannot create target feed, status="+status.getCode());
errorCount++;
ok = false;
}
}
}
public void onEntry(Document entryDoc) {
if (!ok) {
return;
}
Entry entry = new Entry(entryDoc);
entry.index();
UUID entryId = null;
try {
entryId = UUID.fromString(entry.getId().substring(9));
} catch (IllegalArgumentException ex) {
log.severe("Ignoring entry with bad UUID: "+entry.getId());
errorCount++;
return;
}
log.info("Entry: "+entryId);
EntryClient entryClient = new EntryClient(new Reference(targetClient.getEntryLocation(entryId).toString()),target.getIdentity());
String src = null;
Element content = entryDoc.getDocumentElement().getFirstElementNamed(CONTENT_NAME);
URI baseURI = null;
MediaType contentType = null;
if (content!=null) {
src = content.getAttributeValue("src");
String type = content.getAttributeValue("type");
if (type!=null) {
contentType = MediaType.valueOf(type);
}
baseURI = content.getBaseURI();
}
if (entries!=null) {
entries.add(entryId);
}
Status exists = null;
try {
exists = entryClient.get();
} catch (Exception ex) {
log.log(Level.SEVERE,"Cannot get entry "+entryClient.getLocation()+", error: "+ex.getMessage(),ex);
errorCount++;
return;
}
if (exists.isSuccess()) {
entryClient.getEntry().index();
try {
Status status = entryClient.update(entryDoc);
if (!status.isSuccess()) {
log.severe("Cannot update entry "+entryClient.getLocation()+", status="+status.getCode());
errorCount++;
}
} catch (Exception ex) {
log.log(Level.SEVERE,"Cannot update entry "+entryClient.getLocation()+", error: "+ex.getMessage(),ex);
errorCount++;
}
if (src!=null) {
// update media
Reference mediaLocation = new Reference(baseURI.resolve(src).toString());
Client client = new Client(mediaLocation.getSchemeProtocol());
Request getRequest = new Request(Method.GET,mediaLocation);
if (source.getIdentity()!=null) {
getRequest.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_BASIC,source.getUsername(),source.getPassword()));
}
Response getResponse = client.handle(getRequest);
if (getResponse.getStatus().isSuccess()) {
Reference targetLocation = new Reference(targetClient.getLocation().resolve(src).toString());
Client targetClient = new Client(targetLocation.getSchemeProtocol());
Request putRequest = new Request(Method.PUT,targetLocation);
if (target.getIdentity()!=null) {
putRequest.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_BASIC,target.getUsername(),target.getPassword()));
}
Representation rep = getResponse.getEntity();
if (contentType!=null) {
rep.setMediaType(contentType);
}
putRequest.setEntity(rep);
Response putResponse = targetClient.handle(putRequest);
if (!putResponse.getStatus().isSuccess()) {
log.severe("Cannot put media update to "+targetLocation+", status="+putResponse.getStatus().getCode());
}
} else {
if (!getResponse.getStatus().isSuccess()) {
log.severe("Cannot get media from "+mediaLocation+", status="+getResponse.getStatus().getCode());
}
}
}
} else if (exists.getCode()==Status.CLIENT_ERROR_NOT_FOUND.getCode()) {
if (src==null) {
// regular entry
try {
targetClient.createEntry(entryDoc);
} catch (StatusException ex) {
log.severe("Cannot create entry "+entryClient.getLocation()+", status="+ex.getStatus().getCode());
errorCount++;
} catch (Exception ex) {
log.log(Level.SEVERE,"Cannot create entry "+entryClient.getLocation()+", error: "+ex.getMessage(),ex);
errorCount++;
}
} else {
// media entry
Reference mediaLocation = new Reference(baseURI.resolve(src).toString());
Client client = new Client(mediaLocation.getSchemeProtocol());
Request request = new Request(Method.GET,mediaLocation);
if (source.getIdentity()!=null) {
request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_BASIC,source.getUsername(),source.getPassword()));
}
Response response = client.handle(request);
if (response.getStatus().isSuccess()) {
try {
Representation rep = response.getEntity();
if (contentType!=null) {
rep.setMediaType(contentType);
}
Entry mediaEntry = targetClient.createMedia(entryId, src, rep);
entryClient.setEntry(mediaEntry);
try {
Status status = entryClient.update(entryDoc);
if (!status.isSuccess()) {
log.severe("Cannot update entry "+entryClient.getLocation()+", status="+status.getCode());
errorCount++;
}
} catch (Exception ex) {
log.log(Level.SEVERE,"Cannot update entry "+entryClient.getLocation()+", error: "+ex.getMessage(),ex);
errorCount++;
}
} catch (StatusException ex) {
log.severe("Cannot create media resource "+src+", status="+ex.getStatus().getCode());
errorCount++;
} catch (Exception ex) {
log.log(Level.SEVERE,"Cannot create media resource "+src+", error: "+ex.getMessage(),ex);
errorCount++;
}
} else {
log.severe("Cannot get media resource "+mediaLocation+", status="+response.getStatus().getCode());
errorCount++;
}
}
} else {
log.severe("Cannot check entry "+entryClient.getLocation()+", status="+exists.getCode());
errorCount++;
}
}
public void onEnd() {
if (additive || !ok) {
return;
}
// TODO: remove extra entries
}
});
} catch (Exception ex) {
log.log(Level.SEVERE,"Error while getting source feed: "+ex.getMessage(),ex);
errorCount++;
}
}
static URI toURI(String value)
{
int colon = value.indexOf(':');
if (colon<2) {
File file = new File(value);
return file.toURI();
} else {
return URI.create(value);
}
}
public static void main(String [] args)
{
int index = 0;
String sourceIdentity = null;
String targetIdentity = null;
while (index<args.length && args[index].charAt(0)=='-') {
if (args[index].equals("--source-identity")) {
index++;
if (index==args.length) {
System.err.println("--source-identity requires an argument.");
return;
}
sourceIdentity = args[index];
} else if (args[index].equals("--target-identity")) {
index++;
if (index==args.length) {
System.err.println("--target-identity requires an argument.");
return;
}
targetIdentity = args[index];
}
index++;
}
if ((args.length-index)<2) {
System.err.println("A source and target feed URI is required.");
return;
}
Link source = new Link("source",toURI(args[index]));
if (sourceIdentity!=null) {
int colon = sourceIdentity.indexOf(':');
source.setIdentity(sourceIdentity.substring(0,colon), sourceIdentity.substring(colon+1));
}
Link target = new Link("target",toURI(args[index+1]));
if (targetIdentity!=null) {
int colon = targetIdentity.indexOf(':');
target.setIdentity(targetIdentity.substring(0,colon), targetIdentity.substring(colon+1));
}
FeedSynchronizer sync = new FeedSynchronizer(source,target);
sync.run();
int errorCount = sync.getErrorCount();
if (errorCount>0) {
System.err.println("There were "+errorCount+" errors during synchronization.");
}
System.exit(errorCount);
}
}