Commits

Sam Adams committed 59c68b5

Extracted PingbackService

  • Participants
  • Parent commits 76ba5c9

Comments (0)

Files changed (8)

File chempound-client/src/main/java/net/chempound/client/DepositItemResponse.java

 package net.chempound.client;
 
-import org.apache.abdera.i18n.iri.IRI;
 import org.apache.abdera.model.Link;
 
 import java.net.URI;

File chempound-webapp/src/main/java/net/chempound/webapp/pingback/Fault.java

+package net.chempound.webapp.pingback;
+
+/**
+ * @author Sam Adams
+ */
+public interface Fault {
+
+    int getCode();
+
+    String getMessage();
+
+}

File chempound-webapp/src/main/java/net/chempound/webapp/pingback/PingbackException.java

+package net.chempound.webapp.pingback;
+
+/**
+ * @author Sam Adams
+ */
+public class PingbackException extends Exception {
+
+    private final Fault fault;
+
+    public PingbackException(final Fault fault, final Throwable cause) {
+        super(fault.getMessage(), cause);
+        this.fault = fault;
+    }
+
+    public PingbackException(final Fault fault) {
+        super(fault.getMessage());
+        this.fault = fault;
+    }
+
+    public Fault getFault() {
+        return fault;
+    }
+}

File chempound-webapp/src/main/java/net/chempound/webapp/pingback/PingbackFault.java

+package net.chempound.webapp.pingback;
+
+/**
+ * @author Sam Adams
+ */
+public enum PingbackFault implements Fault {
+
+    GENERIC_FAULT(0, "Error processing request."),
+    SOURCE_URI_DOES_NOT_EXIST(16, "The source URI does not exist."),
+    SOURCE_URI_DOES_NOT_LINK_TO_TARGET(17, ""),
+    TARGET_URI_DOES_NOT_EXIST(32, "The specified target URI does not exist."),
+    TARGET_URI_INVALID(33, "The specified target URI cannot be used as a target."),
+    ALREADY_REGISTERED(48, "The pingback has already been registered."),
+    ACCESS_DENIED(49, "Access denied."),
+    COMMUNICATION_ERROR(50, "The server could not communicate with an upstream server, or received an error from an " +
+                             "upstream server, and therefore could not complete the request.")
+    ;
+
+    private final int code;
+    private final String message;
+
+    private PingbackFault(final int code, final String message) {
+        this.code = code;
+        this.message = message;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+}

File chempound-webapp/src/main/java/net/chempound/webapp/pingback/PingbackResource.java

 package net.chempound.webapp.pingback;
 
-import com.hp.hpl.jena.rdf.model.Model;
-import com.hp.hpl.jena.rdf.model.ModelFactory;
-import com.hp.hpl.jena.rdf.model.Resource;
-import com.hp.hpl.jena.rdf.model.ResourceFactory;
-import net.chempound.datastore.TripleStore;
-import net.chempound.rdf.CPTerms;
 import net.chempound.webapp.utils.XOMRepresentation;
 import nu.xom.*;
 import org.apache.commons.io.IOUtils;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpUriRequest;
-import org.apache.http.impl.client.DefaultHttpClient;
 import org.restlet.representation.Representation;
 import org.restlet.resource.Post;
 import org.restlet.resource.ServerResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import javax.inject.Inject;
 import java.io.IOException;
  */
 public class PingbackResource extends ServerResource {
 
-    private final TripleStore tripleStore;
+    private static final Logger LOG = LoggerFactory.getLogger(PingbackResource.class);
+
+    private final PingbackService pingbackService;
 
     @Inject
-    public PingbackResource(final TripleStore tripleStore) {
-        this.tripleStore = tripleStore;
+    public PingbackResource(final PingbackService pingbackService) {
+        this.pingbackService = pingbackService;
     }
 
-    @Post
+    @Post("text/xml")
     public Representation doXmlRpc(final Representation xml) {
-        Document response;
-        final Document payload;
-        try {
-            payload = readPostXml(xml);
-            try {
-                response = handlePayload(payload);
-            } catch (Exception e) {
-                e.printStackTrace();
-                response = prepareFaultResponse(0, "Error handling XML-RPC call");
-            }
-        } catch (ParsingException e) {
-            e.printStackTrace();
-            response = prepareFaultResponse(-32700, "Parse error");
-        } catch (IOException e) {
-            e.printStackTrace();
-            response = prepareFaultResponse(-32300, "Transport error");
-        }
-
-        return new XOMRepresentation(response);
+        return new XOMRepresentation(handleXmlRpc(xml));
     }
 
-    private Document handlePayload(final Document doc) {
+    private Document handleXmlRpc(final Representation xml) {
+        try {
+            return handlePayload(readPostXml(xml));
+        } catch (PingbackException exception) {
+            return prepareFaultResponse(exception.getFault());
+        } catch (Exception exception) {
+            return prepareFaultResponse(PingbackFault.GENERIC_FAULT);
+        }
+    }
+
+    private Document handlePayload(final Document doc) throws PingbackException {
         final Element methodCall = doc.getRootElement();
         final Element methodName = methodCall.getFirstChildElement("methodName");
         if ("pingback.ping".equals(methodName.getValue())) {
-            final Element params = methodCall.getFirstChildElement("params");
-            final Elements parameters = params.getChildElements("param");
-            final URI sourceUri = URI.create(
-                    parameters.get(0)
-                            .getFirstChildElement("value")
-                            .getFirstChildElement("string")
-                            .getValue());
-            final URI targetUri = URI.create(
-                    parameters.get(1)
-                            .getFirstChildElement("value")
-                            .getFirstChildElement("string")
-                            .getValue());
-
-            return doPingback(sourceUri, targetUri);
+            final Elements parameters = getParameters(methodCall);
+            final URI sourceUri = getUri(parameters, 0);
+            final URI targetUri = getUri(parameters, 1);
+            pingbackService.pingback(sourceUri, targetUri);
+            return prepareSuccessResponse("Pingback registered.");
         }
-        return prepareFaultResponse(0, "Bad Request");
+        throw new PingbackException(XmlRpcFault.SERVER_ERROR_METHOD_NOT_FOUND);
     }
 
-    private Document doPingback(final URI sourceUri, final URI targetUri) {
-
-        if (!isValidTarget(targetUri)) {
-            return prepareFaultResponse(33, "The specified target URI cannot be used as a target.");
-        }
-
-        String source = null;
-        try {
-            source = fetchSource(sourceUri);
-        } catch (IOException e) {
-            e.printStackTrace(); // TODO
-        }
-        if (source == null) {
-            return prepareFaultResponse(16, "The source URI does not exist.");
-        }
-
-
-        if (!containsLink(source, targetUri)) {
-            return prepareFaultResponse(17,
-                    "The source URI does not contain a link to the target URI, and so cannot be used as a source.");
-        }
-
-        // Update target URI
-        final URI aggregationUri = findAggregation(targetUri);
-
-        if (alreadyRegistered(sourceUri, aggregationUri)) {
-            return prepareFaultResponse(48, "The pingback has already been registered.");
-        }
-
-        registerPingback(sourceUri, aggregationUri);
-
-        return prepareResponse("Pingback registered.");
+    private static Elements getParameters(final Element methodCall) {
+        final Element params = methodCall.getFirstChildElement("params");
+        return params.getChildElements("param");
     }
 
-
-    private boolean isValidTarget(final URI targetUri) {
-        // TODO extend this to check type of resource
-        return tripleStore.containsResource(targetUri);
+    private static URI getUri(final Elements parameters, final int index) {
+        return URI.create(
+                        parameters.get(index)
+                                .getFirstChildElement("value")
+                                .getFirstChildElement("string")
+                                .getValue());
     }
 
-    private String fetchSource(final URI sourceUri) throws IOException {
-        final HttpClient client = new DefaultHttpClient();
-        final HttpUriRequest request = new HttpGet(sourceUri);
-        final HttpResponse response = client.execute(request);
-        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
-            final InputStream in = response.getEntity().getContent();
-            try {
-                return IOUtils.toString(in);
-            } finally {
-                IOUtils.closeQuietly(in);
-            }
-        }
-        return null;
-    }
-
-    private boolean containsLink(final String source, final URI targetUri) {
-        // TODO can probably make this better... e.g. support entities, check a@href
-        return source.contains(targetUri.toString());
-    }
-
-    private boolean alreadyRegistered(final URI sourceUri, final URI targetUri) {
-        // TODO check whether URI already registered
-        return false;
-    }
-
-    private void registerPingback(final URI sourceUri, final URI targetUri) {
-        final Model model = ModelFactory.createDefaultModel();
-        final Resource sourceResource = model.getResource(sourceUri.toString());
-        final Resource targetResource = model.getResource(targetUri.toString());
-        model.add(targetResource, CPTerms.pingback, sourceResource);
-
-        tripleStore.updateModel(targetUri, model);
-    }
-
-    private URI findAggregation(final URI targetUri) {
-        return tripleStore.getAggregationUriForResourceMap(ResourceFactory.createResource(targetUri.toString()));
-    }
-
-    private Document prepareResponse(final String message) {
+    private static Document prepareSuccessResponse(final String message) {
         final Element methodResponse =
             element("methodResponse",
-                element("params",
-                    element("param",
-                        element("value",
-                            element("string", message)))));
+                    element("params",
+                            element("param",
+                                    element("value",
+                                            element("string", message)))));
 
             return new Document(methodResponse);
     }
 
-    private Document prepareFaultResponse(final int code, final String message) {
+    private static Document prepareFaultResponse(final Fault fault) {
         final Element methodResponse =
             element("methodResponse",
                 element("fault",
                             element("member",
                                 element("name", "faultCode"),
                                 element("value",
-                                    element("int", String.valueOf(code)))),
+                                    element("int", String.valueOf(fault.getCode())))),
                             element("member",
                                 element("name", "faultString"),
                                 element("value",
-                                    element("string", message)))))));
+                                    element("string", fault.getMessage())))))));
 
         return new Document(methodResponse);
     }
         return element;
     }
 
-    private Document readPostXml(final Representation xml) throws IOException, ParsingException {
-        final Builder builder = new Builder();
-        Document doc;
-        final InputStream in = xml.getStream();
+    private static Document readPostXml(final Representation xml) throws PingbackException {
         try {
-            doc = builder.build(in);
-        } finally {
-            IOUtils.closeQuietly(in);
+
+            final Builder builder = new Builder();
+            final InputStream in = xml.getStream();
+            try {
+                return builder.build(in);
+            } finally {
+                IOUtils.closeQuietly(in);
+            }
+
+        } catch (ParsingException e) {
+            throw new PingbackException(XmlRpcFault.PARSE_ERROR_NOT_WELL_FORMED, e);
+        } catch (IOException e) {
+            throw new PingbackException(XmlRpcFault.TRANSPORT_ERROR, e);
         }
-        return doc;
     }
-
 }

File chempound-webapp/src/main/java/net/chempound/webapp/pingback/PingbackService.java

+package net.chempound.webapp.pingback;
+
+import com.hp.hpl.jena.rdf.model.Model;
+import com.hp.hpl.jena.rdf.model.ModelFactory;
+import com.hp.hpl.jena.rdf.model.Resource;
+import com.hp.hpl.jena.rdf.model.ResourceFactory;
+import net.chempound.datastore.TripleStore;
+import net.chempound.rdf.CPTerms;
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.impl.client.DefaultHttpClient;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+
+/**
+ * @author Sam Adams
+ */
+@Singleton
+public class PingbackService {
+
+    private final TripleStore tripleStore;
+
+    @Inject
+    public PingbackService(final TripleStore tripleStore) {
+        this.tripleStore = tripleStore;
+    }
+
+    public void pingback(final URI sourceUri, final URI targetUri) throws PingbackException {
+
+        if (!isValidTarget(targetUri)) {
+            throw new PingbackException(PingbackFault.TARGET_URI_INVALID);
+        }
+
+        String source = null;
+        try {
+            source = fetchSource(sourceUri);
+        } catch (IOException e) {
+            e.printStackTrace(); // TODO
+        }
+        if (source == null) {
+            throw new PingbackException(PingbackFault.SOURCE_URI_DOES_NOT_EXIST);
+        }
+
+        if (!containsLink(source, targetUri)) {
+            throw new PingbackException(PingbackFault.SOURCE_URI_DOES_NOT_LINK_TO_TARGET);
+        }
+
+        // Update target URI
+        final URI aggregationUri = findAggregation(targetUri);
+
+        if (alreadyRegistered(sourceUri, aggregationUri)) {
+            throw new PingbackException(PingbackFault.ALREADY_REGISTERED);
+        }
+
+        registerPingback(sourceUri, aggregationUri);
+    }
+
+
+    private boolean isValidTarget(final URI targetUri) {
+        // TODO extend this to check type of resource
+        return tripleStore.containsResource(targetUri);
+    }
+
+    private String fetchSource(final URI sourceUri) throws IOException {
+        final HttpClient client = new DefaultHttpClient();
+        final HttpUriRequest request = new HttpGet(sourceUri);
+        final HttpResponse response = client.execute(request);
+        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
+            final InputStream in = response.getEntity().getContent();
+            try {
+                return IOUtils.toString(in);
+            } finally {
+                IOUtils.closeQuietly(in);
+            }
+        }
+        return null;
+    }
+
+    private boolean containsLink(final String source, final URI targetUri) {
+        // TODO can probably make this better... e.g. support entities, check a@href
+        return source.contains(targetUri.toString());
+    }
+
+    private boolean alreadyRegistered(final URI sourceUri, final URI targetUri) {
+        // TODO check whether URI already registered
+        return false;
+    }
+
+    private void registerPingback(final URI sourceUri, final URI targetUri) {
+        final Model model = ModelFactory.createDefaultModel();
+        final Resource sourceResource = model.getResource(sourceUri.toString());
+        final Resource targetResource = model.getResource(targetUri.toString());
+        model.add(targetResource, CPTerms.pingback, sourceResource);
+
+        tripleStore.updateModel(targetUri, model);
+    }
+
+    private URI findAggregation(final URI targetUri) {
+        return tripleStore.getAggregationUriForResourceMap(ResourceFactory.createResource(targetUri.toString()));
+    }
+
+}

File chempound-webapp/src/main/java/net/chempound/webapp/pingback/XmlRpcFault.java

+package net.chempound.webapp.pingback;
+
+/**
+ * @author Sam Adams
+ */
+public enum XmlRpcFault implements Fault {
+
+    PARSE_ERROR_NOT_WELL_FORMED(-32700, "Parse error: not well formed."),
+    PARSE_ERROR_UNSUPPORTED_ENCODING(-32701, "Parse error: unsupported encoding."),
+    PARSE_ERROR_INVALID_CHARACTER(-32702, "Parse error: invalid character for encoding."),
+    SERVER_ERROR_INVALID_XMLRPC(-32600, "Server error: invalid xml-rpc. not conforming to spec."),
+    SERVER_ERROR_METHOD_NOT_FOUND(-32601, "Server error: requested method not found."),
+    SERVER_ERROR_INVALID_PARAMETERS(-32602, "Server error: invalid method parameters."),
+    SERVER_ERROR_INTERNAL_XMLRPC_ERROR(-32603, "Server error: internal xml-rpc error."),
+    APPLICATION_ERROR(-32500, "Application error."),
+    SYSTEM_ERROR(-32400, "System error."),
+    TRANSPORT_ERROR(-32300, "Transport error.")
+    ;
+
+    private final int code;
+    private final String message;
+
+    private XmlRpcFault(final int code, final String message) {
+        this.code = code;
+        this.message = message;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+}

File chempound-webapp/src/test/java/net/chempound/webapp/pingback/PingbackIntegrationTest.java

         Header pingback = response.getFirstHeader("X-Pingback");
         assertNotNull(pingback);
         assertEquals("http://localhost:8717/repo/pingback", pingback.getValue());
-
     }
 
     @Test