summaryrefslogtreecommitdiff
path: root/src/main/java/com/p4square/fmfacade
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/p4square/fmfacade')
-rw-r--r--src/main/java/com/p4square/fmfacade/FMFacade.java107
-rw-r--r--src/main/java/com/p4square/fmfacade/FreeMarkerPageResource.java98
-rw-r--r--src/main/java/com/p4square/fmfacade/ftl/GetMethod.java94
-rw-r--r--src/main/java/com/p4square/fmfacade/json/ClientException.java20
-rw-r--r--src/main/java/com/p4square/fmfacade/json/JsonRequestClient.java109
-rw-r--r--src/main/java/com/p4square/fmfacade/json/JsonResponse.java87
6 files changed, 515 insertions, 0 deletions
diff --git a/src/main/java/com/p4square/fmfacade/FMFacade.java b/src/main/java/com/p4square/fmfacade/FMFacade.java
new file mode 100644
index 0000000..0e552b0
--- /dev/null
+++ b/src/main/java/com/p4square/fmfacade/FMFacade.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2013 Jesse Morgan <jesse@jesterpm.net>
+ */
+
+package com.p4square.fmfacade;
+
+import java.io.IOException;
+
+import org.restlet.Application;
+import org.restlet.Component;
+import org.restlet.data.Protocol;
+import org.restlet.Restlet;
+import org.restlet.routing.Router;
+
+import freemarker.template.Configuration;
+import freemarker.template.DefaultObjectWrapper;
+import freemarker.template.Template;
+
+import org.apache.log4j.Logger;
+
+import com.p4square.grow.config.Config;
+
+/**
+ *
+ * @author Jesse Morgan <jesse@jesterpm.net>
+ */
+public class FMFacade extends Application {
+ private static final Logger cLog = Logger.getLogger(FMFacade.class);
+ private final Configuration mFMConfig;
+
+ public FMFacade() {
+ mFMConfig = new Configuration();
+ mFMConfig.setClassForTemplateLoading(getClass(), "/templates");
+ mFMConfig.setObjectWrapper(new DefaultObjectWrapper());
+ }
+
+ /**
+ * @return a Config object.
+ */
+ public Config getConfig() {
+ return null;
+ }
+
+ @Override
+ public synchronized Restlet createInboundRoot() {
+ return createRouter();
+ }
+
+ /**
+ * Retrieve a template.
+ *
+ * @param name The template name.
+ * @return A FreeMarker template or null on error.
+ */
+ public Template getTemplate(String name) {
+ try {
+ return mFMConfig.getTemplate(name);
+
+ } catch (IOException e) {
+ cLog.error("Could not load template \"" + name + "\"", e);
+ return null;
+ }
+ }
+
+ /**
+ * Create the router to be used by this application. This can be overriden
+ * by sub-classes to add additional routes.
+ *
+ * @return The router.
+ */
+ protected Router createRouter() {
+ Router router = new Router(getContext());
+ router.attachDefault(FreeMarkerPageResource.class);
+
+ return router;
+ }
+
+ /**
+ * Stand-alone main for testing.
+ */
+ public static void main(String[] args) {
+ // Start the HTTP Server
+ final Component component = new Component();
+ component.getServers().add(Protocol.HTTP, 8085);
+ component.getClients().add(Protocol.HTTP);
+ component.getDefaultHost().attach(new FMFacade());
+
+ // Setup shutdown hook
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ public void run() {
+ try {
+ component.stop();
+ } catch (Exception e) {
+ cLog.error("Exception during cleanup", e);
+ }
+ }
+ });
+
+ cLog.info("Starting server...");
+
+ try {
+ component.start();
+ } catch (Exception e) {
+ cLog.fatal("Could not start: " + e.getMessage(), e);
+ }
+ }
+}
diff --git a/src/main/java/com/p4square/fmfacade/FreeMarkerPageResource.java b/src/main/java/com/p4square/fmfacade/FreeMarkerPageResource.java
new file mode 100644
index 0000000..8c8948a
--- /dev/null
+++ b/src/main/java/com/p4square/fmfacade/FreeMarkerPageResource.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2013 Jesse Morgan
+ */
+
+package com.p4square.fmfacade;
+
+import java.util.Map;
+import java.util.HashMap;
+
+import freemarker.template.Template;
+
+import org.restlet.Context;
+import org.restlet.data.MediaType;
+import org.restlet.data.Status;
+import org.restlet.ext.freemarker.TemplateRepresentation;
+import org.restlet.representation.Representation;
+import org.restlet.resource.ServerResource;
+import org.restlet.security.User;
+
+import org.apache.log4j.Logger;
+
+import com.p4square.fmfacade.ftl.GetMethod;
+
+import com.p4square.session.Session;
+import com.p4square.session.Sessions;
+
+/**
+ *
+ * @author Jesse Morgan <jesse@jesterpm.net>
+ */
+public class FreeMarkerPageResource extends ServerResource {
+ private static Logger cLog = Logger.getLogger(FreeMarkerPageResource.class);
+
+ public static Map<String, Object> baseRootObject(final Context context, final FMFacade fmf) {
+ Map<String, Object> root = new HashMap<String, Object>();
+
+ root.put("get", new GetMethod(context.getClientDispatcher()));
+ root.put("config", fmf.getConfig());
+
+ return root;
+ }
+
+ private FMFacade mFMF;
+ private String mCurrentPage;
+
+ @Override
+ public void doInit() {
+ mFMF = (FMFacade) getApplication();
+ mCurrentPage = getReference().getRemainingPart(false, false);
+ }
+
+ protected Representation get() {
+ try {
+ Template t = mFMF.getTemplate("pages" + mCurrentPage + ".ftl");
+
+ if (t == null) {
+ setStatus(Status.CLIENT_ERROR_NOT_FOUND);
+ return null;
+ }
+
+ return new TemplateRepresentation(t, getRootObject(),
+ MediaType.TEXT_HTML);
+
+ } catch (Exception e) {
+ cLog.fatal("Could not render page: " + e.getMessage(), e);
+ setStatus(Status.SERVER_ERROR_INTERNAL);
+ return null;
+ }
+ }
+
+ /**
+ * Build and return the root object to pass to the FTL Template.
+ * @return A map of objects and methods for the template to access.
+ */
+ protected Map<String, Object> getRootObject() {
+ Map<String, Object> root = baseRootObject(getContext(), mFMF);
+
+ root.put("attributes", getRequestAttributes());
+ root.put("query", getQuery().getValuesMap());
+
+ if (getClientInfo().isAuthenticated()) {
+ final User user = getClientInfo().getUser();
+ final Map<String, String> userMap = new HashMap<String, String>();
+ userMap.put("id", user.getIdentifier());
+ userMap.put("firstName", user.getFirstName());
+ userMap.put("lastName", user.getLastName());
+ userMap.put("email", user.getEmail());
+ root.put("user", userMap);
+ }
+
+ Session s = Sessions.getInstance().get(getRequest());
+ if (s != null) {
+ root.put("session", s.getMap());
+ }
+
+ return root;
+ }
+}
diff --git a/src/main/java/com/p4square/fmfacade/ftl/GetMethod.java b/src/main/java/com/p4square/fmfacade/ftl/GetMethod.java
new file mode 100644
index 0000000..a47c4b0
--- /dev/null
+++ b/src/main/java/com/p4square/fmfacade/ftl/GetMethod.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2013 Jesse Morgan
+ */
+
+package com.p4square.fmfacade.ftl;
+
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+
+import java.io.IOException;
+
+import freemarker.core.Environment;
+import freemarker.template.SimpleScalar;
+import freemarker.template.TemplateMethodModel;
+import freemarker.template.TemplateModel;
+import freemarker.template.TemplateModelException;
+
+import org.apache.log4j.Logger;
+
+import org.restlet.data.Status;
+import org.restlet.data.Method;
+import org.restlet.representation.Representation;
+import org.restlet.Request;
+import org.restlet.Response;
+import org.restlet.Restlet;
+
+import org.restlet.ext.jackson.JacksonRepresentation;
+
+/**
+ * This method allows templates to make GET requests.
+ *
+ * @author Jesse Morgan <jesse@jesterpm.net>
+ */
+public class GetMethod implements TemplateMethodModel {
+ private static final Logger cLog = Logger.getLogger(GetMethod.class);
+
+ private final Restlet mDispatcher;
+
+ public GetMethod(Restlet dispatcher) {
+ mDispatcher = dispatcher;
+ }
+
+ /**
+ * @param args List with exactly two arguments:
+ * * The variable in which to put the result.
+ * * The URI to GET.
+ */
+ public TemplateModel exec(List args) throws TemplateModelException {
+ final Environment env = Environment.getCurrentEnvironment();
+
+ if (args.size() != 2) {
+ throw new TemplateModelException(
+ "Expecting exactly one argument containing the URI");
+ }
+
+ Request request = new Request(Method.GET, (String) args.get(1));
+ Response response = mDispatcher.handle(request);
+ Status status = response.getStatus();
+ Representation representation = response.getEntity();
+
+ try {
+ if (response.getStatus().isSuccess()) {
+ JacksonRepresentation<Map> mapRepresentation;
+ if (representation instanceof JacksonRepresentation) {
+ mapRepresentation = (JacksonRepresentation<Map>) representation;
+ } else {
+ mapRepresentation = new JacksonRepresentation<Map>(
+ representation, Map.class);
+ }
+ try {
+ TemplateModel mapModel = env.getObjectWrapper().wrap(mapRepresentation.getObject());
+
+ env.setVariable((String) args.get(0), mapModel);
+
+ } catch (IOException e) {
+ cLog.warn("Exception occurred when calling getObject(): "
+ + e.getMessage(), e);
+ status = Status.SERVER_ERROR_INTERNAL;
+ }
+ }
+
+ Map statusMap = new HashMap();
+ statusMap.put("code", status.getCode());
+ statusMap.put("reason", status.getReasonPhrase());
+ statusMap.put("succeeded", status.isSuccess());
+ return env.getObjectWrapper().wrap(statusMap);
+ } finally {
+ if (representation != null) {
+ representation.release();
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/p4square/fmfacade/json/ClientException.java b/src/main/java/com/p4square/fmfacade/json/ClientException.java
new file mode 100644
index 0000000..c233193
--- /dev/null
+++ b/src/main/java/com/p4square/fmfacade/json/ClientException.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2013 Jesse Morgan
+ */
+
+package com.p4square.fmfacade.json;
+
+/**
+ *
+ * @author Jesse Morgan <jesse@jesterpm.net>
+ */
+public class ClientException extends Exception {
+
+ public ClientException(final String msg) {
+ super(msg);
+ }
+
+ public ClientException(final String msg, final Exception cause) {
+ super(msg, cause);
+ }
+}
diff --git a/src/main/java/com/p4square/fmfacade/json/JsonRequestClient.java b/src/main/java/com/p4square/fmfacade/json/JsonRequestClient.java
new file mode 100644
index 0000000..19a394f
--- /dev/null
+++ b/src/main/java/com/p4square/fmfacade/json/JsonRequestClient.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2013 Jesse Morgan
+ */
+
+package com.p4square.fmfacade.json;
+
+import java.util.Map;
+
+import java.io.IOException;
+
+import org.apache.log4j.Logger;
+
+import org.restlet.data.Status;
+import org.restlet.data.Method;
+import org.restlet.representation.Representation;
+import org.restlet.Request;
+import org.restlet.Response;
+import org.restlet.Restlet;
+
+import org.restlet.ext.jackson.JacksonRepresentation;
+
+/**
+ *
+ * @author Jesse Morgan <jesse@jesterpm.net>
+ */
+public class JsonRequestClient {
+ private final Restlet mDispatcher;
+
+ public JsonRequestClient(Restlet dispatcher) {
+ mDispatcher = dispatcher;
+ }
+
+ /**
+ * Perform a GET request for the given URI and parse the response as a
+ * JSON map.
+ *
+ * @return A JsonResponse object which can be used to retrieve the
+ * response as a JSON map.
+ */
+ public JsonResponse get(final String uri) {
+ final Request request = new Request(Method.GET, uri);
+ final Response response = mDispatcher.handle(request);
+
+ return new JsonResponse(response);
+ }
+
+ /**
+ * Perform a PUT request for the given URI and parse the response as a
+ * JSON map.
+ *
+ * @return A JsonResponse object which can be used to retrieve the
+ * response as a JSON map.
+ */
+ public JsonResponse put(final String uri, Representation entity) {
+ final Request request = new Request(Method.PUT, uri);
+ request.setEntity(entity);
+
+ final Response response = mDispatcher.handle(request);
+ return new JsonResponse(response);
+ }
+
+ /**
+ * Perform a PUT request for the given URI and parse the response as a
+ * JSON map.
+ *
+ * @return A JsonResponse object which can be used to retrieve the
+ * response as a JSON map.
+ */
+ public JsonResponse put(final String uri, Map map) {
+ return put(uri, new JacksonRepresentation<Map>(map));
+ }
+
+ /**
+ * Perform a POST request for the given URI and parse the response as a
+ * JSON map.
+ *
+ * @return A JsonResponse object which can be used to retrieve the
+ * response as a JSON map.
+ */
+ public JsonResponse post(final String uri, Representation entity) {
+ final Request request = new Request(Method.POST, uri);
+ request.setEntity(entity);
+
+ final Response response = mDispatcher.handle(request);
+ return new JsonResponse(response);
+ }
+
+ /**
+ * Perform a POST request for the given URI and parse the response as a
+ * JSON map.
+ *
+ * @return A JsonResponse object which can be used to retrieve the
+ * response as a JSON map.
+ */
+ public JsonResponse post(final String uri, Map map) {
+ return post(uri, new JacksonRepresentation<Map>(map));
+ }
+
+ /**
+ * Perform a DELETE request for the given URI.
+ *
+ * @return A JsonResponse object with the status of the request.
+ */
+ public JsonResponse delete(final String uri) {
+ final Request request = new Request(Method.DELETE, uri);
+ final Response response = mDispatcher.handle(request);
+ return new JsonResponse(response);
+ }
+}
diff --git a/src/main/java/com/p4square/fmfacade/json/JsonResponse.java b/src/main/java/com/p4square/fmfacade/json/JsonResponse.java
new file mode 100644
index 0000000..b9cb587
--- /dev/null
+++ b/src/main/java/com/p4square/fmfacade/json/JsonResponse.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2013 Jesse Morgan
+ */
+
+package com.p4square.fmfacade.json;
+
+import java.util.Map;
+
+import java.io.IOException;
+
+import org.restlet.data.Status;
+import org.restlet.data.Reference;
+import org.restlet.representation.Representation;
+import org.restlet.Response;
+
+import org.restlet.ext.jackson.JacksonRepresentation;
+
+/**
+ * JsonResponse wraps a Restlet Response object and parses the entity, if any,
+ * as a JSON map.
+ *
+ * @author Jesse Morgan <jesse@jesterpm.net>
+ */
+public class JsonResponse {
+ private final Response mResponse;
+ private final Representation mRepresentation;
+
+ private Map<String, Object> mMap;
+
+ JsonResponse(Response response) {
+ mResponse = response;
+ mRepresentation = response.getEntity();
+ mMap = null;
+
+ if (!response.getStatus().isSuccess()) {
+ if (mRepresentation != null) {
+ mRepresentation.release();
+ }
+ }
+ }
+
+ /**
+ * @return the Status info from the response.
+ */
+ public Status getStatus() {
+ return mResponse.getStatus();
+ }
+
+ /**
+ * @return the Reference for a redirect.
+ */
+ public Reference getRedirectLocation() {
+ return mResponse.getLocationRef();
+ }
+
+ /**
+ * Return the parsed json map from the response.
+ */
+ public Map<String, Object> getMap() throws ClientException {
+ if (mMap == null) {
+ Representation representation = mRepresentation;
+
+ // Parse response
+ if (representation == null) {
+ return null;
+ }
+
+ JacksonRepresentation<Map> mapRepresentation;
+ if (representation instanceof JacksonRepresentation) {
+ mapRepresentation = (JacksonRepresentation<Map>) representation;
+ } else {
+ mapRepresentation = new JacksonRepresentation<Map>(
+ representation, Map.class);
+ }
+
+ try {
+ mMap = (Map<String, Object>) mapRepresentation.getObject();
+
+ } catch (IOException e) {
+ throw new ClientException("Failed to parse response: " + e.getMessage(), e);
+ }
+ }
+
+ return mMap;
+ }
+
+}