// This software is released into the Public Domain. See copying.txt for details.
package org.openstreetmap.osmosis.replicationhttp.v0_6;
import java.net.InetSocketAddress;
import java.util.Map;
import org.openstreetmap.osmosis.core.container.v0_6.ChangeContainer;
import org.openstreetmap.osmosis.core.task.v0_6.ChangeSink;
import org.openstreetmap.osmosis.core.task.v0_6.ChangeSinkChangeSource;
import org.openstreetmap.osmosis.core.task.v0_6.RunnableChangeSource;
import org.openstreetmap.osmosis.replicationhttp.v0_6.impl.ReplicationDataClientChannelPipelineFactory;
import org.openstreetmap.osmosis.replicationhttp.v0_6.impl.SequenceClient;
import org.openstreetmap.osmosis.replicationhttp.v0_6.impl.SequenceClientRestartManager;
/**
* This task connects to a replication data server to obtain replication data,
* then sends the replication data to the sink. This requires the change sink to
* support the replication extensions allowing state persistence and multiple
* invocations.
*
* @author Brett Henderson
*/
public class ReplicationDataClient implements RunnableChangeSource {
private NoReleaseChangeSinkWrapper changeSinkWrapper;
private InetSocketAddress serverAddress;
private String pathPrefix;
/**
* Creates a new instance.
*
* @param serverAddress
* The server to connect to.
* @param pathPrefix
* The base path to add to the URL. This is necessary if a data
* server is sitting behind a proxy server that adds a prefix to
* the request path.
*/
public ReplicationDataClient(InetSocketAddress serverAddress, String pathPrefix) {
this.serverAddress = serverAddress;
this.pathPrefix = pathPrefix;
changeSinkWrapper = new NoReleaseChangeSinkWrapper();
}
@Override
public void setChangeSink(ChangeSink changeSink) {
changeSinkWrapper.setChangeSink(changeSink);
}
@Override
public void run() {
try {
// Create a sequence client restart manager so that our sequence
// client continues processing in the face of temporary connectivity
// issues.
SequenceClientRestartManager clientRestartManager = new SequenceClientRestartManager();
// Create the client for receiving replication data.
ReplicationDataClientChannelPipelineFactory pipelineFactory =
new ReplicationDataClientChannelPipelineFactory(
clientRestartManager.getControl(), changeSinkWrapper, serverAddress.getHostName(),
pathPrefix);
SequenceClient client = new SequenceClient(serverAddress, pipelineFactory);
// Run the client and perform restarts if it fails. This call will
// block.
clientRestartManager.manageClient(client);
} finally {
changeSinkWrapper.realRelease();
}
}
/**
* This acts as a proxy between the sequence client and the real change
* sink. The primary purpose is to prevent the release method from being
* called until all processing has completed.
*/
private static class NoReleaseChangeSinkWrapper implements ChangeSinkChangeSource {
private ChangeSink changeSink;
@Override
public void setChangeSink(ChangeSink changeSink) {
this.changeSink = changeSink;
}
@Override
public void initialize(Map<String, Object> metaData) {
changeSink.initialize(metaData);
}
@Override
public void process(ChangeContainer change) {
changeSink.process(change);
}
@Override
public void complete() {
changeSink.complete();
}
@Override
public void close() {
// Do nothing.
}
/**
* Called by the main replication data client when all processing is
* complete. Unlike the release method which does nothing, this calls
* the change sink release method.
*/
public void realRelease() {
changeSink.close();
}
}
}