diff options
Diffstat (limited to 'src/main/java/com/p4square/fmfacade')
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; + } + +} |