package org.freeplane.plugin.remote.client.services;
import java.io.PrintStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import org.apache.commons.io.output.NullOutputStream;
import org.freeplane.core.util.LogUtils;
import org.freeplane.plugin.remote.client.ClientController;
import org.freeplane.plugin.remote.client.User;
import org.freeplane.plugin.remote.v10.model.updates.AddNodeUpdate;
import org.freeplane.plugin.remote.v10.model.updates.ChangeNodeAttributeUpdate;
import org.freeplane.plugin.remote.v10.model.updates.DeleteNodeUpdate;
import org.freeplane.plugin.remote.v10.model.updates.MapUpdate;
import org.freeplane.plugin.remote.v10.model.updates.MoveNodeUpdate;
import scala.concurrent.Await;
import scala.concurrent.Future;
import scala.concurrent.duration.Duration;
import akka.dispatch.Futures;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientHandlerException;
import com.sun.jersey.api.client.ClientRequest;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.filter.ClientFilter;
import com.sun.jersey.api.client.filter.LoggingFilter;
import com.sun.jersey.client.apache.ApacheHttpClient;
import com.sun.jersey.core.util.MultivaluedMapImpl;
public class DocearOnlineWs implements WS {
private final ClientController clientController;
private final String serviceUrl = "http://localhost:9000";
// private final String serviceUrl = "https://staging.my.docear.org";
private final Client restClient;
public DocearOnlineWs(ClientController clientController) {
this.clientController = clientController;
// com.google.common.util.concurrent.
PrintStream stream = new PrintStream(new NullOutputStream());
// disableCertificateValidation();
restClient = ApacheHttpClient.create();
restClient.addFilter(new LoggingFilter(stream));
final String source = clientController.source();
restClient.addFilter(new ClientFilter() {
@Override
public ClientResponse handle(ClientRequest request) throws ClientHandlerException {
String uriString = request.getURI().toASCIIString();
uriString = uriString.contains("?") ? uriString + "&" : uriString + "?";
final URI newUri = URI.create(uriString + "source=" + source);
request.setURI(newUri);
return getNext().handle(request);
}
});
}
@Override
public Future<User> login(final String username, final String password) {
final WebResource loginResource = restClient.resource(serviceUrl).path("user/login");
MultivaluedMap<String, String> formData = new MultivaluedMapImpl();
formData.add("username", username);
formData.add("password", password);
final ClientResponse loginResponse = loginResource.type(MediaType.APPLICATION_FORM_URLENCODED_TYPE).post(ClientResponse.class, formData);
if(loginResponse.getStatus() == 200) {
final User user = new User(username, loginResponse.getEntity(String.class));
return Futures.successful(user);
} else {
return null;
}
}
@Override
public Future<Boolean> listenIfUpdatesOccur(final String username, final String accessToken, final String mapId) {
return Futures.future(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
final WebResource resource = preparedResource(username, accessToken).path("map/" + mapId + "/listen");
final ClientResponse loginResponse = resource.get(ClientResponse.class);
return loginResponse.getStatus() == 200;
}
}, clientController.system().dispatcher());
}
@Override
public Future<JsonNode> getMapAsXml(String username, String accessToken, final String mapId) {
try {
final WebResource mapAsXmlResource = preparedResource(username, accessToken).path("map/" + mapId + "/xml");
final JsonNode response = new ObjectMapper().readTree(mapAsXmlResource.get(String.class));
return Futures.successful(response);
} catch (Exception e) {
e.printStackTrace();
return Futures.failed(e);
}
}
@Override
public Future<GetUpdatesResponse> getUpdatesSinceRevision(String username, String accessToken, final String mapId, final int sinceRevision) {
int currentRevision = -1;
List<MapUpdate> updates = new ArrayList<MapUpdate>();
final WebResource fetchUpdates = preparedResource(username, accessToken).path("map/" + mapId + "/updates/" + sinceRevision);
final ClientResponse response = fetchUpdates.get(ClientResponse.class);
final ObjectMapper mapper = new ObjectMapper();
try {
JsonNode json = mapper.readTree(response.getEntity(String.class));
currentRevision = json.get("currentRevision").asInt();
Iterator<JsonNode> it = json.get("orderedUpdates").iterator();
while (it.hasNext()) {
final JsonNode mapUpdateJson = mapper.readTree(it.next().asText());
final MapUpdate.Type type = MapUpdate.Type.valueOf(mapUpdateJson.get("type").asText());
switch (type) {
case AddNode:
updates.add(mapper.treeToValue(mapUpdateJson, AddNodeUpdate.class));
break;
case ChangeNodeAttribute:
updates.add(mapper.treeToValue(mapUpdateJson, ChangeNodeAttributeUpdate.class));
break;
case DeleteNode:
updates.add(mapper.treeToValue(mapUpdateJson, DeleteNodeUpdate.class));
break;
case MoveNode:
updates.add(mapper.treeToValue(mapUpdateJson, MoveNodeUpdate.class));
break;
}
}
} catch (Exception e) {
return Futures.failed(e);
}
return Futures.successful(new GetUpdatesResponse(currentRevision, updates));
}
@Override
public Future<String> createNode(String username, String accessToken, final String mapId, final String parentNodeId) {
final WebResource resource = preparedResource(username, accessToken).path("map/" + mapId + "/node/create");
final MultivaluedMap<String, String> formData = new MultivaluedMapImpl();
formData.add("parentNodeId", parentNodeId);
final ClientResponse response = resource.post(ClientResponse.class, formData);
try {
final AddNodeUpdate update = new ObjectMapper().readValue(response.getEntity(String.class), AddNodeUpdate.class);
return Futures.successful(update.getNewNodeId());
} catch (Exception e) {
e.printStackTrace();
return Futures.failed(e);
}
}
@Override
public Future<Boolean> moveNodeTo(String username, String accessToken, final String mapId, final String newParentId, final String nodeToMoveId, final int newIndex) {
final WebResource resource = preparedResource(username, accessToken).path("map/" + mapId + "/node/move");
final MultivaluedMap<String, String> formData = new MultivaluedMapImpl();
formData.add("newParentNodeId", newParentId);
formData.add("nodetoMoveId", nodeToMoveId);
formData.add("newIndex", newIndex + "");
final ClientResponse response = resource.post(ClientResponse.class, formData);
LogUtils.info("Status: " + response.getStatus());
return Futures.successful(response.getStatus() == 200);
}
@Override
public Future<Boolean> removeNode(String username, String accessToken, final String mapId, final String nodeId) {
final WebResource resource = preparedResource(username, accessToken).path("map/" + mapId + "/node/delete");
final MultivaluedMap<String, String> formData = new MultivaluedMapImpl();
formData.add("nodeId", nodeId);
ClientResponse response = resource.delete(ClientResponse.class, formData);
LogUtils.info("Status: " + response.getStatus());
return Futures.successful(response.getStatus() == 200);
}
@Override
public Future<Boolean> changeNode(String username, String accessToken, final String mapId, final String nodeId, final String attribute, final Object value) {
try {
final WebResource resource = preparedResource(username, accessToken).path("map/" + mapId + "/node/change");
final MultivaluedMap<String, String> formData = new MultivaluedMapImpl();
formData.add("nodeId", nodeId);
Map<String, Object> attributeValueMap = new HashMap<String, Object>();
attributeValueMap.put(attribute, value);
formData.add("AttributeValueMapJson", new ObjectMapper().writeValueAsString(attributeValueMap));
LogUtils.info("locking node");
// boolean isLocked =
boolean isLocked = Await.result(lockNode(username, accessToken, mapId, nodeId), Duration.create("5 seconds"));
if (!isLocked)
return Futures.successful(false);
LogUtils.info("changing");
ClientResponse response = resource.post(ClientResponse.class, formData);
LogUtils.info("releasing node");
releaseNode(username, accessToken, mapId, nodeId);
LogUtils.info("Status: " + response.getStatus());
return Futures.successful(response.getStatus() == 200);
} catch (Exception e) {
e.printStackTrace();
return Futures.failed(e);
}
}
private Future<Boolean> lockNode(String username, String accessToken, final String mapId, final String nodeId) {
final WebResource resource = preparedResource(username, accessToken).path("map/" + mapId + "/node/requestLock");
final MultivaluedMap<String, String> formData = new MultivaluedMapImpl();
formData.add("nodeId", nodeId);
ClientResponse response = resource.post(ClientResponse.class, formData);
LogUtils.info("Status: " + response.getStatus());
return Futures.successful(response.getStatus() == 200);
}
private Future<Boolean> releaseNode(String username, String accessToken, final String mapId, final String nodeId) {
final WebResource resource = preparedResource(username, accessToken).path("map/" + mapId + "/node/releaseLock");
final MultivaluedMap<String, String> formData = new MultivaluedMapImpl();
formData.add("nodeId", nodeId);
ClientResponse response = resource.post(ClientResponse.class, formData);
LogUtils.info("Status: " + response.getStatus());
return Futures.successful(response.getStatus() == 200);
}
private WebResource preparedResource(String username, String accessToken) {
return restClient.resource(serviceUrl).queryParam("username", username).queryParam("accessToken", accessToken);
}
}