diff options
Diffstat (limited to 'src/com')
124 files changed, 0 insertions, 12256 deletions
| diff --git a/src/com/p4square/f1oauth/Attribute.java b/src/com/p4square/f1oauth/Attribute.java deleted file mode 100644 index 64f2507..0000000 --- a/src/com/p4square/f1oauth/Attribute.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.f1oauth; - -import java.util.Date; - -/** - * F1 Attribute Data. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class Attribute { -    private final String mAttributeName; -    private String mId; -    private Date mStartDate; -    private Date mEndDate; -    private String mComment; - -    /** -     * @param name The attribute name. -     */ -    public Attribute(final String name) { -        mAttributeName = name; -    } - -    /** -     * @return the Attribute name. -     */ -    public String getAttributeName() { -        return mAttributeName; -    } - -    /** -     * @return the id of this specific attribute instance. -     */ -    public String getId() { -        return mId; -    } - -    /** -     * Set the attribute id to id. -     */ -    public void setId(final String id) { -        mId = id; -    } - -    /** -     * @return the start date for the attribute. -     */ -    public Date getStartDate() { -        return mStartDate; -    } - -    /** -     * Set the start date for the attribute. -     */ -    public void setStartDate(final Date date) { -        mStartDate = date; -    } - -    /** -     * @return the end date for the attribute. -     */ -    public Date getEndDate() { -        return mEndDate; -    } - -    /** -     * Set the end date for the attribute. -     */ -    public void setEndDate(final Date date) { -        mEndDate = date; -    } - -    /** -     * @return The comment on the Attribute. -     */ -    public String getComment() { -        return mComment; -    } - -    /** -     * Set the comment on the attribute. -     */ -    public void setComment(final String comment) { -        mComment = comment; -    } -} diff --git a/src/com/p4square/f1oauth/F1API.java b/src/com/p4square/f1oauth/F1API.java deleted file mode 100644 index a525c3f..0000000 --- a/src/com/p4square/f1oauth/F1API.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.f1oauth; - -import java.io.IOException; -import java.util.List; -import java.util.Map; - -import com.p4square.restlet.oauth.OAuthException; -import com.p4square.restlet.oauth.OAuthUser; - -/** - * F1 API methods which require an authenticated user. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public interface F1API { -    /** -     * Fetch information about a user. -     * -     * @param user The user to fetch information about. -     * @return An F1User object. -     */ -    F1User getF1User(OAuthUser user) throws OAuthException, IOException; - -    /** -     * Fetch a list of all attributes ids and names. -     * -     * @return A Map of attribute name to attribute id. -     */ -    Map<String, String> getAttributeList() throws F1Exception; - -    /** -     * Add an attribute to the user. -     * -     * @param user The user to add the attribute to. -     * @param attributeName The attribute to add. -     * @param attribute The attribute to add. -     */ -    boolean addAttribute(String userId, Attribute attribute) throws F1Exception; - -    /** -     * Return attributes assigned to user. -     * -     * A user may be assigned multiple attributes with the same name, thus even if -     * attributeName is specified, multiple attributes may be returned. -     * -     * @param userId The user to query. -     * @param attributeName A specific attribute to return, null for all. -     * @return A list of Attributes -     */ -    List<Attribute> getAttribute(String userId, String attributeName) throws F1Exception; - -} diff --git a/src/com/p4square/f1oauth/F1Access.java b/src/com/p4square/f1oauth/F1Access.java deleted file mode 100644 index c3307f1..0000000 --- a/src/com/p4square/f1oauth/F1Access.java +++ /dev/null @@ -1,594 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.f1oauth; - -import java.io.IOException; -import java.net.URLEncoder; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.codahale.metrics.Counter; -import com.codahale.metrics.MetricRegistry; -import com.codahale.metrics.Timer; - -import org.apache.log4j.Logger; - -import org.restlet.Context; -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.data.ChallengeResponse; -import org.restlet.data.ChallengeScheme; -import org.restlet.data.MediaType; -import org.restlet.data.Method; -import org.restlet.data.Status; -import org.restlet.engine.util.Base64; -import org.restlet.ext.jackson.JacksonRepresentation; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; - -import com.p4square.restlet.oauth.OAuthException; -import com.p4square.restlet.oauth.OAuthHelper; -import com.p4square.restlet.oauth.OAuthUser; -import com.p4square.restlet.oauth.Token; - -/** - * F1 API Access. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class F1Access { -    public enum UserType { -        WEBLINK, PORTAL; -    } - -    private static final Logger LOG = Logger.getLogger(F1Access.class); - -    private static final String VERSION_STRING = "/v1/"; -    private static final String REQUESTTOKEN_URL = "Tokens/RequestToken"; -    private static final String AUTHORIZATION_URL = "Login"; -    private static final String ACCESSTOKEN_URL= "Tokens/AccessToken"; -    private static final String TRUSTED_ACCESSTOKEN_URL = "/AccessToken"; - -    private static final SimpleDateFormat DATE_FORMAT = -        new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); - -    private final String mBaseUrl; -    private final String mMethod; - -    private final OAuthHelper mOAuthHelper; - -    private final Map<String, String> mAttributeIdByName; - -    private MetricRegistry mMetricRegistry; - -    /** -     */ -    public F1Access(Context context, String consumerKey, String consumerSecret, -            String baseUrl, String churchCode, UserType userType) { - -        switch (userType) { -            case WEBLINK: -                mMethod = "WeblinkUser"; -                break; -            case PORTAL: -                mMethod = "PortalUser"; -                break; -            default: -                throw new IllegalArgumentException("Unknown UserType"); -        } - -        mBaseUrl = "https://" + churchCode + "." + baseUrl + VERSION_STRING; - -        // Create the OAuthHelper. This implicitly registers the helper to -        // handle outgoing requests which need OAuth authentication. -        mOAuthHelper = new OAuthHelper(context, consumerKey, consumerSecret) { -            @Override -            protected String getRequestTokenUrl() { -                return mBaseUrl + REQUESTTOKEN_URL; -            } - -            @Override -            public String getLoginUrl(Token requestToken, String callback) { -                String loginUrl = mBaseUrl + mMethod + AUTHORIZATION_URL -                                    + "?oauth_token=" + URLEncoder.encode(requestToken.getToken()); - -                if (callback != null) { -                    loginUrl += "&oauth_callback=" + URLEncoder.encode(callback); -                } - -                return loginUrl; -            } - -            @Override -            protected String getAccessTokenUrl() { -                return mBaseUrl + ACCESSTOKEN_URL; -            } -        }; - -        mAttributeIdByName = new HashMap<>(); -    } - -    /** -     * Set the MetricRegistry to get metrics recorded. -     */ -    public void setMetricRegistry(MetricRegistry metrics) { -        mMetricRegistry = metrics; -    } - -    /** -     * Request an AccessToken for a particular username and password. -     * -     * This is an F1 extension to OAuth: -     * http://developer.fellowshipone.com/docs/v1/Util/AuthDocs.help#2creds -     */ -    public OAuthUser getAccessToken(String username, String password) throws OAuthException { -        Timer.Context timer = getTimer("F1Access.getAccessToken.time"); -        boolean success = true; - -        try { -            Request request = new Request(Method.POST, -                                          mBaseUrl +  mMethod + TRUSTED_ACCESSTOKEN_URL); -            request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_OAUTH)); - -            String base64String = Base64.encode((username + " " + password).getBytes(), false); -            request.setEntity(new StringRepresentation(base64String)); - -            return mOAuthHelper.processAccessTokenRequest(request); - -        } catch (Exception e) { -            success = false; -            throw e; - -        } finally { -            if (timer != null) { -                timer.stop(); -            } -            if (success) { -                incrementCounter("F1Access.getAccessToken.success"); -            } else { -                incrementCounter("F1Access.getAccessToken.failure"); -            } -        } -    } - -    /** -     * Create a new Account. -     * -     * @param firstname The user's first name. -     * @param lastname The user's last name. -     * @param email The user's email address. -     * @param redirect The URL to send the user to after confirming his address. -     * -     * @return true if created, false if the account already exists. -     */ -    public boolean createAccount(String firstname, String lastname, String email, String redirect) -            throws OAuthException { -        Timer.Context timer = getTimer("F1Access.createAccount.time"); -        boolean success = true; - -        try { -            String req = String.format("{\n\"account\":{\n\"firstName\":\"%s\",\n" -                                     + "\"lastName\":\"%s\",\n\"email\":\"%s\",\n" -                                     + "\"urlRedirect\":\"%s\"\n}\n}", -                                     firstname, lastname, email, redirect); - -            Request request = new Request(Method.POST, mBaseUrl + "Accounts"); -            request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_OAUTH)); -            request.setEntity(new StringRepresentation(req, MediaType.APPLICATION_JSON)); - -            Response response = mOAuthHelper.getResponse(request); - -            Status status = response.getStatus(); -            if (Status.SUCCESS_NO_CONTENT.equals(status)) { -                return true; - -            } else if (Status.CLIENT_ERROR_CONFLICT.equals(status)) { -                return false; - -            } else { -                throw new OAuthException(status); -            } - -        } catch (Exception e) { -            success = false; -            throw e; - -        } finally { -            if (timer != null) { -                timer.stop(); -            } -            if (success) { -                incrementCounter("F1Access.createAccount.success"); -            } else { -                incrementCounter("F1Access.createAccount.failure"); -            } -        } -    } - -    /** -     * @return An F1API authenticated by the given user. -     */ -    public F1API getAuthenticatedApi(OAuthUser user) { -        return new AuthenticatedApi(user); -    } - -    private class AuthenticatedApi implements F1API { -        private final OAuthUser mUser; - -        public AuthenticatedApi(OAuthUser user) { -            mUser = user; -        } - -        /** -         * Fetch information about a user. -         * -         * @param user The user to fetch information about. -         * @return An F1User object. -         */ -        @Override -        public F1User getF1User(OAuthUser user) throws OAuthException, IOException { -            Timer.Context timer = getTimer("F1Access.getF1User.time"); -            boolean success = true; - -            try { -                Request request = new Request(Method.GET, user.getLocation() + ".json"); -                request.setChallengeResponse(mUser.getChallengeResponse()); -                Response response = mOAuthHelper.getResponse(request); - -                try { -                    Status status = response.getStatus(); -                    if (status.isSuccess()) { -                        JacksonRepresentation<Map> entity = -                            new JacksonRepresentation<Map>(response.getEntity(), Map.class); -                        Map data = entity.getObject(); -                        return new F1User(user, data); - -                    } else { -                        throw new OAuthException(status); -                    } - -                } finally { -                    if (response.getEntity() != null) { -                        response.release(); -                    } -                } - -            } catch (Exception e) { -                success = false; -                throw e; - -            } finally { -                if (timer != null) { -                    timer.stop(); -                } -                if (success) { -                    incrementCounter("F1Access.getF1User.success"); -                } else { -                    incrementCounter("F1Access.getF1User.failure"); -                } -            } -        } - -        @Override -        public Map<String, String> getAttributeList() throws F1Exception { -            // Note: this list is shared by all F1 users. -            synchronized (mAttributeIdByName) { -                if (mAttributeIdByName.size() == 0) { -                    Timer.Context timer = getTimer("F1Access.getAttributeList.time"); -                    boolean success = true; - -                    try { -                        // Reload attributes. Maybe it will be there now... -                        Request request = new Request(Method.GET, -                                mBaseUrl + "People/AttributeGroups.json"); -                        request.setChallengeResponse(mUser.getChallengeResponse()); -                        Response response = mOAuthHelper.getResponse(request); - -                        Representation representation = response.getEntity(); -                        try { -                            Status status = response.getStatus(); -                            if (status.isSuccess()) { -                                JacksonRepresentation<Map> entity = -                                    new JacksonRepresentation<Map>(response.getEntity(), Map.class); - -                                Map attributeGroups = (Map) entity.getObject().get("attributeGroups"); -                                List<Map> groups = (List<Map>) attributeGroups.get("attributeGroup"); - -                                for (Map group : groups) { -                                    List<Map> attributes = (List<Map>) group.get("attribute"); -                                    if (attributes != null) { -                                        for (Map attribute : attributes) { -                                            String id = (String) attribute.get("@id"); -                                            String name = ((String) attribute.get("name")); -                                            mAttributeIdByName.put(name.toLowerCase(), id); -                                            LOG.debug("Caching attribute '" + name -                                                    + "' with id '" + id + "'"); -                                        } -                                    } -                                } -                            } - -                        } catch (IOException e) { -                            throw new F1Exception("Could not parse AttributeGroups.", e); - -                        } finally { -                            if (representation != null) { -                                representation.release(); -                            } -                        } - -                    } catch (Exception e) { -                        success = false; -                        throw e; - -                    } finally { -                        if (timer != null) { -                            timer.stop(); -                        } -                        if (success) { -                            incrementCounter("F1Access.getAttributeList.success"); -                        } else { -                            incrementCounter("F1Access.getAttributeList.failure"); -                        } -                    } -                } - -                return mAttributeIdByName; -            } -        } - -        /** -         * Add an attribute to the user. -         * -         * @param user The user to add the attribute to. -         * @param attributeName The attribute to add. -         * @param attribute The attribute to add. -         */ -        public boolean addAttribute(String userId, Attribute attribute) -                throws F1Exception { - -            // Get the attribute id. -            String attributeId = getAttributeId(attribute.getAttributeName()); -            if (attributeId == null) { -                throw new F1Exception("Could not find id for " + attribute.getAttributeName()); -            } - -            // Get Attribute Template -            Map attributeTemplate = null; - -            Timer.Context timer = getTimer("F1Access.addAttribute.GET.time"); -            boolean success = true; - -            try { -                Request request = new Request(Method.GET, -                        mBaseUrl + "People/" + userId + "/Attributes/new.json"); -                request.setChallengeResponse(mUser.getChallengeResponse()); -                Response response = mOAuthHelper.getResponse(request); - -                Representation representation = response.getEntity(); -                try { -                    Status status = response.getStatus(); -                    if (status.isSuccess()) { -                        JacksonRepresentation<Map> entity = -                            new JacksonRepresentation<Map>(response.getEntity(), Map.class); -                        attributeTemplate = entity.getObject(); - -                    } else { -                        throw new F1Exception("Failed to retrieve attribute template: " -                                + status); -                    } - -                } catch (IOException e) { -                    throw new F1Exception("Could not parse attribute template.", e); - -                } finally { -                    if (representation != null) { -                        representation.release(); -                    } -                } -            } catch (Exception e) { -                success = false; -                throw e; - -            } finally { -                if (timer != null) { -                    timer.stop(); -                } -                if (success) { -                    incrementCounter("F1Access.addAttribute.GET.success"); -                } else { -                    incrementCounter("F1Access.addAttribute.GET.failure"); -                } -            } - -            if (attributeTemplate == null) { -                throw new F1Exception("Could not retrieve attribute template."); -            } - -            // Populate Attribute Template -            Map attributeMap = (Map) attributeTemplate.get("attribute"); -            Map attributeGroup = (Map) attributeMap.get("attributeGroup"); - -            Map<String, String> attributeIdMap = new HashMap<>(); -            attributeIdMap.put("@id", attributeId); -            attributeGroup.put("attribute", attributeIdMap); - -            if (attribute.getStartDate() != null) { -                attributeMap.put("startDate", DATE_FORMAT.format(attribute.getStartDate())); -            } - -            if (attribute.getStartDate() != null) { -                attributeMap.put("endDate", DATE_FORMAT.format(attribute.getStartDate())); -            } - -            attributeMap.put("comment", attribute.getComment()); - -            // POST new attribute -            Status status; -            timer = getTimer("F1Access.addAttribute.POST.time"); -            success = true; - -            try { -                Request request = new Request(Method.POST, -                        mBaseUrl + "People/" + userId + "/Attributes.json"); -                request.setChallengeResponse(mUser.getChallengeResponse()); -                request.setEntity(new JacksonRepresentation<Map>(attributeTemplate)); -                Response response = mOAuthHelper.getResponse(request); - -                Representation representation = response.getEntity(); -                try { -                    status = response.getStatus(); - -                    if (status.isSuccess()) { -                        return true; -                    } - -                } finally { -                    if (representation != null) { -                        representation.release(); -                    } -                } -            } catch (Exception e) { -                success = false; -                throw e; - -            } finally { -                if (timer != null) { -                    timer.stop(); -                } -                if (success) { -                    incrementCounter("F1Access.addAttribute.POST.success"); -                } else { -                    incrementCounter("F1Access.getAccessToken.POST.failure"); -                } -            } - -            LOG.debug("addAttribute failed POST: " + status); -            return false; -        } - -        @Override -        public List<Attribute> getAttribute(String userId, String attributeNameFilter) -                throws F1Exception { - -            Map attributesResponse; - -            // Get Attributes -            Timer.Context timer = getTimer("F1Access.getAttribute.time"); -            boolean success = true; - -            try { -                Request request = new Request(Method.GET, -                        mBaseUrl + "People/" + userId + "/Attributes.json"); -                request.setChallengeResponse(mUser.getChallengeResponse()); -                Response response = mOAuthHelper.getResponse(request); - -                Representation representation = response.getEntity(); -                try { -                    Status status = response.getStatus(); -                    if (status.isSuccess()) { -                        JacksonRepresentation<Map> entity = -                            new JacksonRepresentation<Map>(response.getEntity(), Map.class); -                        attributesResponse = entity.getObject(); - -                    } else { -                        throw new F1Exception("Failed to retrieve attributes: " -                                + status); -                    } - -                } catch (IOException e) { -                    throw new F1Exception("Could not parse attributes.", e); - -                } finally { -                    if (representation != null) { -                        representation.release(); -                    } -                } -            } catch (Exception e) { -                success = false; -                throw e; - -            } finally { -                if (timer != null) { -                    timer.stop(); -                } -                if (success) { -                    incrementCounter("F1Access.getAttribute.success"); -                } else { -                    incrementCounter("F1Access.getAttribute.failure"); -                } -            } - -            // Parse Response -            List<Attribute> result = new ArrayList<>(); - -            try { -                // I feel like I'm writing lisp here... -                Map attributesMap = (Map) attributesResponse.get("attributes"); -                if (attributesMap == null) { -                    return result; -                } - -                List<Map> attributes = (List<Map>) (attributesMap).get("attribute"); -                for (Map attributeMap : attributes) { -                    String id = (String) attributeMap.get("@id"); -                    String startDate = (String) attributeMap.get("startDate"); -                    String endDate = (String) attributeMap.get("endDate"); -                    String comment = (String) attributeMap.get("comment"); - -                    Map attributeIdMap = (Map) ((Map) attributeMap.get("attributeGroup")) -                        .get("attribute"); -                    String attributeName = (String) attributeIdMap.get("name"); - -                    if (attributeNameFilter == null -                            || attributeNameFilter.equalsIgnoreCase(attributeName)) { - -                        Attribute attribute = new Attribute(attributeName); -                        attribute.setId(id); -                        if (startDate != null) { -                            attribute.setStartDate(DATE_FORMAT.parse(startDate)); -                        } -                        if (endDate != null) { -                            attribute.setEndDate(DATE_FORMAT.parse(endDate)); -                        } -                        attribute.setComment(comment); -                        result.add(attribute); -                    } -                } -            } catch (Exception e) { -                throw new F1Exception("Failed to parse attributes response.", e); -            } - -            return result; -        } - -        /** -         * @return an attribute id for the given attribute name. -         */ -        private String getAttributeId(String attributeName) throws F1Exception { -            Map<String, String> attributeMap = getAttributeList(); - -            return attributeMap.get(attributeName.toLowerCase()); -        } - -    } - -    private Timer.Context getTimer(String name) { -        if (mMetricRegistry != null) { -            return mMetricRegistry.timer(name).time(); -        } else { -            return null; -        } -    } - -    private void incrementCounter(String name) { -        if (mMetricRegistry != null) { -            mMetricRegistry.counter(name).inc(); -        } -    } -} diff --git a/src/com/p4square/f1oauth/F1Exception.java b/src/com/p4square/f1oauth/F1Exception.java deleted file mode 100644 index 54c1a77..0000000 --- a/src/com/p4square/f1oauth/F1Exception.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.f1oauth; - -public class F1Exception extends Exception { -    public F1Exception(String message) { -        super(message); -    } - -    public F1Exception(String message, Exception cause) { -        super(message, cause); -    } -} diff --git a/src/com/p4square/f1oauth/F1ProgressReporter.java b/src/com/p4square/f1oauth/F1ProgressReporter.java deleted file mode 100644 index 8382020..0000000 --- a/src/com/p4square/f1oauth/F1ProgressReporter.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.p4square.f1oauth; - -import com.p4square.grow.frontend.ProgressReporter; -import org.apache.log4j.Logger; -import org.restlet.security.User; - -import java.util.Date; - -/** - * A ProgressReporter implementation to record progress in F1. - */ -public class F1ProgressReporter implements ProgressReporter { - -    private static final Logger LOG = Logger.getLogger(F1ProgressReporter.class); - -    private F1Access mF1Access; - -    public F1ProgressReporter(final F1Access f1access) { -        mF1Access = f1access; -    } - -    @Override -    public void reportAssessmentComplete(final User user, final String level, final Date date, final String results) { -        String attributeName = "Assessment Complete - " + level; -        Attribute attribute = new Attribute(attributeName); -        attribute.setStartDate(date); -        attribute.setComment(results); -        addAttribute(user, attribute); -    } - -    @Override -    public void reportChapterComplete(final User user, final String chapter, final Date date) { -        final String attributeName = "Training Complete - " + chapter; -        final Attribute attribute = new Attribute(attributeName); -        attribute.setStartDate(date); -        addAttribute(user, attribute); -    } - -    private void addAttribute(final User user, final Attribute attribute) { -        if (!(user instanceof F1User)) { -            throw new IllegalArgumentException("User must be an F1User, but got " + user.getClass().getName()); -        } - -        try { -            final F1User f1User = (F1User) user; -            final F1API f1 = mF1Access.getAuthenticatedApi(f1User); - -            if (!f1.addAttribute(user.getIdentifier(), attribute)) { -                LOG.error("addAttribute failed for " + user.getIdentifier() + " with attribute " -                          + attribute.getAttributeName()); -            } -        } catch (Exception e) { -            LOG.error("addAttribute failed for " + user.getIdentifier() + " with attribute " -                      + attribute.getAttributeName(), e); -        } -    } -} diff --git a/src/com/p4square/f1oauth/F1User.java b/src/com/p4square/f1oauth/F1User.java deleted file mode 100644 index e5ab487..0000000 --- a/src/com/p4square/f1oauth/F1User.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.f1oauth; - -import java.util.Map; - -import com.p4square.restlet.oauth.OAuthException; -import com.p4square.restlet.oauth.OAuthUser; - -/** - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class F1User extends OAuthUser { -    public static final String ID = "@id"; -    public static final String FIRST_NAME = "firstName"; -    public static final String LAST_NAME = "lastName"; -    public static final String ICODE = "@iCode"; - -    private final Map mData; - -    /** -     * Copy the user information from user into a new F1User. -     * -     * @param user Original user. -     * @param data F1 Person Record. -     * @throws IllegalStateException if data.get("person") is null. -     */ -    public F1User(OAuthUser user, Map data) { -        super(user.getLocation(), user.getToken()); - -        mData = (Map) data.get("person"); -        if (mData == null) { -            throw new IllegalStateException("Bad data"); -        } - -        setIdentifier(getString(ID)); -        setFirstName(getString(FIRST_NAME)); -        setLastName(getString(LAST_NAME)); -    } - -    /** -     * Get a String from the map. -     * -     * @param key The map key. -     * @return The value associated with the key, or null. -     */ -    public String getString(String key) { -        Object blob = get(key); - -        if (blob instanceof String) { -            return (String) blob; - -        } else { -            return null; -        } -    } - -    /** -     * Fetch an object from the F1 record. -     * -     * @param key The map key -     * @return The object in the map or null. -     */ -    public Object get(String key) { -        return mData.get(key); -    } -} diff --git a/src/com/p4square/f1oauth/FellowshipOneIntegrationDriver.java b/src/com/p4square/f1oauth/FellowshipOneIntegrationDriver.java deleted file mode 100644 index 865f5d6..0000000 --- a/src/com/p4square/f1oauth/FellowshipOneIntegrationDriver.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.p4square.f1oauth; - -import com.codahale.metrics.MetricRegistry; -import com.p4square.grow.config.Config; -import com.p4square.grow.frontend.IntegrationDriver; -import com.p4square.grow.frontend.ProgressReporter; -import org.restlet.Context; -import org.restlet.security.Verifier; - -/** - * The FellowshipOneIntegrationDriver creates implementations of various - * objects to support integration with Fellowship One. - */ -public class FellowshipOneIntegrationDriver implements IntegrationDriver { - -    private final Context mContext; -    private final MetricRegistry mMetricRegistry; -    private final Config mConfig; -    private final F1Access mAPI; - -    private final ProgressReporter mProgressReporter; - -    public FellowshipOneIntegrationDriver(final Context context) { -        mContext = context; -        mConfig = (Config) context.getAttributes().get("com.p4square.grow.config"); -        mMetricRegistry = (MetricRegistry) context.getAttributes().get("com.p4square.grow.metrics"); - -        mAPI = new F1Access(context, -                            mConfig.getString("f1ConsumerKey", ""), -                            mConfig.getString("f1ConsumerSecret", ""), -                            mConfig.getString("f1BaseUrl", "staging.fellowshiponeapi.com"), -                            mConfig.getString("f1ChurchCode", "pfseawa"), -                            F1Access.UserType.WEBLINK); -        mAPI.setMetricRegistry(mMetricRegistry); - -        mProgressReporter = new F1ProgressReporter(mAPI); -    } - -    /** -     * @return An F1Access instance. -     */ -    public F1Access getF1Access() { -        return mAPI; -    } - -    @Override -    public Verifier newUserAuthenticationVerifier() { -        return new SecondPartyVerifier(mContext, mAPI); -    } - -    @Override -    public ProgressReporter getProgressReporter() { -        return mProgressReporter; -    } -} diff --git a/src/com/p4square/f1oauth/SecondPartyAuthenticator.java b/src/com/p4square/f1oauth/SecondPartyAuthenticator.java deleted file mode 100644 index 8deefec..0000000 --- a/src/com/p4square/f1oauth/SecondPartyAuthenticator.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.f1oauth; - -import org.apache.log4j.Logger; - -import com.p4square.restlet.oauth.OAuthException; -import com.p4square.restlet.oauth.OAuthUser; - -import org.restlet.Context; -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.security.Authenticator; - -/** - * Restlet Authenticator for 2nd - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class SecondPartyAuthenticator extends Authenticator { -    private static final Logger LOG = Logger.getLogger(SecondPartyAuthenticator.class); - -    private final F1Access mHelper; - -    public SecondPartyAuthenticator(Context context, boolean optional, F1Access helper) { -        super(context, optional); - -        mHelper = helper; -    } - -    protected boolean authenticate(Request request, Response response) { -        if (request.getChallengeResponse() == null) { -            return false; // no credentials -        } - -        String username = request.getChallengeResponse().getIdentifier(); -        String password = new String(request.getChallengeResponse().getSecret()); - -        try { -            OAuthUser user = mHelper.getAccessToken(username, password); -            request.getClientInfo().setUser(user); - -            return true; - -        } catch (OAuthException e) { -            LOG.info("OAuth Exception: " + e); -        } - -        return false; // Invalid credentials -    } -} diff --git a/src/com/p4square/f1oauth/SecondPartyVerifier.java b/src/com/p4square/f1oauth/SecondPartyVerifier.java deleted file mode 100644 index 882c7e7..0000000 --- a/src/com/p4square/f1oauth/SecondPartyVerifier.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.f1oauth; - -import java.io.IOException; -import java.util.Map; - -import org.apache.log4j.Logger; - -import com.p4square.restlet.oauth.OAuthException; -import com.p4square.restlet.oauth.OAuthUser; - -import org.restlet.Context; -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.Restlet; -import org.restlet.data.Method; -import org.restlet.data.Status; -import org.restlet.ext.jackson.JacksonRepresentation; -import org.restlet.security.Verifier; - -/** - * Restlet Verifier for F1 2nd Party Authentication - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class SecondPartyVerifier implements Verifier { -    private static final Logger LOG = Logger.getLogger(SecondPartyVerifier.class); - -    private final Restlet mDispatcher; -    private final F1Access mHelper; - -    public SecondPartyVerifier(Context context, F1Access helper) { -        if (helper == null) { -            throw new IllegalArgumentException("Helper can not be null."); -        } - -        mDispatcher = context.getClientDispatcher(); -        mHelper = helper; -    } - -    @Override -    public int verify(Request request, Response response) { -        if (request.getChallengeResponse() == null) { -            return RESULT_MISSING; // no credentials -        } - -        String username = request.getChallengeResponse().getIdentifier(); -        String password = new String(request.getChallengeResponse().getSecret()); - -        try { -            OAuthUser ouser = mHelper.getAccessToken(username, password); - -            // Once we have a user, fetch the people record to get the user id. -            F1User user = mHelper.getAuthenticatedApi(ouser).getF1User(ouser); -            user.setEmail(username); - -            // This seems like a hack... but it'll work -            request.getClientInfo().setUser(user); - -            return RESULT_VALID; - -        } catch (Exception e) { -            LOG.info("OAuth Exception: " + e, e); -        } - -        return RESULT_INVALID; // Invalid credentials -    } - -} diff --git a/src/com/p4square/fmfacade/FMFacade.java b/src/com/p4square/fmfacade/FMFacade.java deleted file mode 100644 index 0e552b0..0000000 --- a/src/com/p4square/fmfacade/FMFacade.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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/com/p4square/fmfacade/FreeMarkerPageResource.java b/src/com/p4square/fmfacade/FreeMarkerPageResource.java deleted file mode 100644 index 8c8948a..0000000 --- a/src/com/p4square/fmfacade/FreeMarkerPageResource.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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/com/p4square/fmfacade/ftl/GetMethod.java b/src/com/p4square/fmfacade/ftl/GetMethod.java deleted file mode 100644 index a47c4b0..0000000 --- a/src/com/p4square/fmfacade/ftl/GetMethod.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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/com/p4square/fmfacade/json/ClientException.java b/src/com/p4square/fmfacade/json/ClientException.java deleted file mode 100644 index c233193..0000000 --- a/src/com/p4square/fmfacade/json/ClientException.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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/com/p4square/fmfacade/json/JsonRequestClient.java b/src/com/p4square/fmfacade/json/JsonRequestClient.java deleted file mode 100644 index 19a394f..0000000 --- a/src/com/p4square/fmfacade/json/JsonRequestClient.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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/com/p4square/fmfacade/json/JsonResponse.java b/src/com/p4square/fmfacade/json/JsonResponse.java deleted file mode 100644 index b9cb587..0000000 --- a/src/com/p4square/fmfacade/json/JsonResponse.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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; -    } - -} diff --git a/src/com/p4square/grow/GrowProcessComponent.java b/src/com/p4square/grow/GrowProcessComponent.java deleted file mode 100644 index f63538c..0000000 --- a/src/com/p4square/grow/GrowProcessComponent.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow; - -import java.io.File; -import java.io.IOException; -import java.util.concurrent.TimeUnit; - -import com.codahale.metrics.ConsoleReporter; -import com.codahale.metrics.MetricRegistry; - -import org.apache.log4j.Logger; - -import org.restlet.Application; -import org.restlet.Component; -import org.restlet.Restlet; -import org.restlet.data.ChallengeScheme; -import org.restlet.data.Protocol; -import org.restlet.resource.Directory; -import org.restlet.security.ChallengeAuthenticator; - -import com.p4square.grow.backend.BackendVerifier; -import com.p4square.grow.backend.GrowBackend; -import com.p4square.grow.config.Config; -import com.p4square.grow.frontend.GrowFrontend; -import com.p4square.restlet.metrics.MetricsApplication; - -/** - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class GrowProcessComponent extends Component { -    private static Logger LOG = Logger.getLogger(GrowProcessComponent.class); - -    private static final String BACKEND_REALM = "Grow Backend"; - -    private final Config mConfig; -    private final MetricRegistry mMetricRegistry; - -    /** -     * Create a new Grow Process website component combining a frontend and backend. -     */ -    public GrowProcessComponent() throws Exception { -        this(new Config()); -    } - -    public GrowProcessComponent(Config config) throws Exception { -        // Clients -        getClients().add(Protocol.FILE); -        getClients().add(Protocol.HTTP); -        getClients().add(Protocol.HTTPS); - -        // Prepare mConfig -        mConfig = config; -        mConfig.updateConfig(this.getClass().getResourceAsStream("/grow.properties")); - -        // Prepare Metrics -        mMetricRegistry = new MetricRegistry(); - -        // Frontend -        GrowFrontend frontend = new GrowFrontend(mConfig, mMetricRegistry); -        getDefaultHost().attach(frontend); - -        // Backend -        GrowBackend backend = new GrowBackend(mConfig, mMetricRegistry); -        getInternalRouter().attach("/backend", backend); - -        // Authenticated access to the backend -        BackendVerifier verifier = new BackendVerifier(backend.getUserRecordProvider()); -        ChallengeAuthenticator auth = new ChallengeAuthenticator(getContext().createChildContext(), -                false, ChallengeScheme.HTTP_BASIC, BACKEND_REALM, verifier); -        auth.setNext(backend); -        getDefaultHost().attach("/backend", auth); - -        // Authenticated access to metrics -        ChallengeAuthenticator metricAuth = new ChallengeAuthenticator( -                getContext().createChildContext(), false, -                ChallengeScheme.HTTP_BASIC, BACKEND_REALM, verifier); -        metricAuth.setNext(new MetricsApplication(mMetricRegistry)); -        getDefaultHost().attach("/metrics", metricAuth); -    } - - -    @Override -    public void start() throws Exception { -        String configDomain = getContext().getParameters().getFirstValue("com.p4square.grow.configDomain"); -        if (configDomain != null) { -            mConfig.setDomain(configDomain); -        } - -        String configFilename = getContext().getParameters().getFirstValue("com.p4square.grow.configFile"); -        if (configFilename != null) { -            mConfig.updateConfig(configFilename); -        } - -        super.start(); -    } - -    /** -     * Stand-alone main for testing. -     */ -    public static void main(String[] args) throws Exception { -        // Load an optional config file from the first argument. -        Config config = new Config(); -        config.setDomain("dev"); -        if (args.length >= 1) { -            config.updateConfig(args[0]); -        } - -        // Override domain -        if (args.length == 2) { -            config.setDomain(args[1]); -        } - -        // Start the HTTP Server -        final GrowProcessComponent component = new GrowProcessComponent(config); -        component.getServers().add(Protocol.HTTP, 8085); - -        // Static content -        try { -            component.getDefaultHost().attach("/images/", new FileServingApp("./build/root/images/")); -            component.getDefaultHost().attach("/scripts", new FileServingApp("./build/root/scripts")); -            component.getDefaultHost().attach("/style.css", new FileServingApp("./build/root/style.css")); -            component.getDefaultHost().attach("/favicon.ico", new FileServingApp("./build/root/favicon.ico")); -            component.getDefaultHost().attach("/notfound.html", new FileServingApp("./build/root/notfound.html")); -            component.getDefaultHost().attach("/error.html", new FileServingApp("./build/root/error.html")); -        } catch (IOException e) { -            LOG.error("Could not create directory for static resources: " -                    + e.getMessage(), e); -        } - -        // Setup shutdown hook -        Runtime.getRuntime().addShutdownHook(new Thread() { -            public void run() { -                try { -                    component.stop(); -                } catch (Exception e) { -                    LOG.error("Exception during cleanup", e); -                } -            } -        }); - -        LOG.info("Starting server..."); - -        try { -            component.start(); -        } catch (Exception e) { -            LOG.fatal("Could not start: " + e.getMessage(), e); -        } -    } - -    private static class FileServingApp extends Application { -        private final String mPath; - -        public FileServingApp(String path) throws IOException { -            mPath = new File(path).getAbsolutePath(); -        } - -        @Override -        public Restlet createInboundRoot() { -            return new Directory(getContext(), "file://" + mPath); -        } -    } -} diff --git a/src/com/p4square/grow/backend/BackendVerifier.java b/src/com/p4square/grow/backend/BackendVerifier.java deleted file mode 100644 index 83160a9..0000000 --- a/src/com/p4square/grow/backend/BackendVerifier.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.backend; - -import java.io.IOException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -import org.apache.commons.codec.binary.Hex; - -import org.restlet.security.SecretVerifier; - -import com.p4square.grow.model.UserRecord; -import com.p4square.grow.provider.Provider; - -/** - * Verify the given credentials against the users with backend access. - */ -public class BackendVerifier extends SecretVerifier { - -    private final Provider<String, UserRecord> mUserProvider; - -    public BackendVerifier(Provider<String, UserRecord> userProvider) { -        mUserProvider = userProvider; -    } - -    @Override -    public int verify(String identifier, char[] secret) { -        if (identifier == null) { -            throw new IllegalArgumentException("Null identifier"); -        } - -        if (secret == null) { -            throw new IllegalArgumentException("Null secret"); -        } - -        // Does the user exist? -        UserRecord user; -        try { -            user = mUserProvider.get(identifier); -            if (user == null) { -                return RESULT_UNKNOWN; -            } - -        } catch (IOException e) { -            return RESULT_UNKNOWN; -        } - -        // Does the user have a backend password? -        String storedHash = user.getBackendPasswordHash(); -        if (storedHash == null) { -            // This user doesn't have access -            return RESULT_INVALID; -        } - -        // Validate the password. -        try { -            String hashedInput = hashPassword(secret); -            if (hashedInput.equals(storedHash)) { -                return RESULT_VALID; -            } - -        } catch (NoSuchAlgorithmException e) { -            return RESULT_UNSUPPORTED; -        } - -        // If all else fails, fail. -        return RESULT_INVALID; -    } - -    /** -     * Hash the given secret. -     */ -    public static String hashPassword(char[] secret) throws NoSuchAlgorithmException { -        MessageDigest md = MessageDigest.getInstance("SHA-1"); - -        // Convert the char[] to byte[] -        // FIXME This approach is incorrectly truncating multibyte -        // characters. -        byte[] b = new byte[secret.length]; -        for (int i = 0; i < secret.length; i++) { -            b[i] = (byte) secret[i]; -        } - -        md.update(b); - -        byte[] hash = md.digest(); -        return new String(Hex.encodeHex(hash)); -    } -} diff --git a/src/com/p4square/grow/backend/CassandraGrowData.java b/src/com/p4square/grow/backend/CassandraGrowData.java deleted file mode 100644 index 22a7716..0000000 --- a/src/com/p4square/grow/backend/CassandraGrowData.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.backend; - -import java.io.IOException; - -import com.p4square.grow.config.Config; - -import com.p4square.grow.backend.db.CassandraDatabase; -import com.p4square.grow.backend.db.CassandraKey; -import com.p4square.grow.backend.db.CassandraProviderImpl; -import com.p4square.grow.backend.db.CassandraCollectionProvider; -import com.p4square.grow.backend.db.CassandraTrainingRecordProvider; - -import com.p4square.grow.model.Message; -import com.p4square.grow.model.MessageThread; -import com.p4square.grow.model.Playlist; -import com.p4square.grow.model.Question; -import com.p4square.grow.model.TrainingRecord; -import com.p4square.grow.model.UserRecord; - -import com.p4square.grow.provider.CollectionProvider; -import com.p4square.grow.provider.DelegateCollectionProvider; -import com.p4square.grow.provider.DelegateProvider; -import com.p4square.grow.provider.Provider; - -/** - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -class CassandraGrowData implements GrowData { -    private static final String DEFAULT_COLUMN = "value"; - -    private final Config mConfig; -    private final CassandraDatabase mDatabase; - -    private final Provider<String, UserRecord> mUserRecordProvider; - -    private final Provider<String, Question> mQuestionProvider; -    private final CassandraTrainingRecordProvider mTrainingRecordProvider; -    private final CollectionProvider<String, String, String> mVideoProvider; - -    private final CollectionProvider<String, String, MessageThread> mFeedThreadProvider; -    private final CollectionProvider<String, String, Message> mFeedMessageProvider; - -    private final Provider<String, String> mStringProvider; - -    private final CollectionProvider<String, String, String> mAnswerProvider; - -    public CassandraGrowData(final Config config) { -        mConfig = config; -        mDatabase = new CassandraDatabase(); - -        mUserRecordProvider = new DelegateProvider<String, CassandraKey, UserRecord>( -                new CassandraProviderImpl<UserRecord>(mDatabase, UserRecord.class)) { -            @Override -            public CassandraKey makeKey(String userid) { -                return new CassandraKey("accounts", userid, DEFAULT_COLUMN); -            } -        }; - -        mQuestionProvider = new DelegateProvider<String, CassandraKey, Question>( -                new CassandraProviderImpl<Question>(mDatabase, Question.class)) { -            @Override -            public CassandraKey makeKey(String questionId) { -                return new CassandraKey("strings", "/questions/" + questionId, DEFAULT_COLUMN); -            } -        }; - -        mFeedThreadProvider = new CassandraCollectionProvider<MessageThread>(mDatabase, -                "feedthreads", MessageThread.class); -        mFeedMessageProvider = new CassandraCollectionProvider<Message>(mDatabase, -                "feedmessages", Message.class); - -        mTrainingRecordProvider = new CassandraTrainingRecordProvider(mDatabase); - -        mVideoProvider = new DelegateCollectionProvider<String, String, String, String, String>( -                new CassandraCollectionProvider<String>(mDatabase, "strings", String.class)) { -            @Override -            public String makeCollectionKey(String key) { -                return "/training/" + key; -            } - -            @Override -            public String makeKey(String key) { -                return key; -            } - -            @Override -            public String unmakeKey(String key) { -                return key; -            } -        }; - -        mStringProvider = new DelegateProvider<String, CassandraKey, String>( -                new CassandraProviderImpl<String>(mDatabase, String.class)) { -            @Override -            public CassandraKey makeKey(String id) { -                return new CassandraKey("strings", id, DEFAULT_COLUMN); -            } -        }; - -        mAnswerProvider = new CassandraCollectionProvider<String>( -                mDatabase, "assessments", String.class); -    } - -    @Override -    public void start() throws Exception { -        mDatabase.setClusterName(mConfig.getString("clusterName", "Dev Cluster")); -        mDatabase.setKeyspaceName(mConfig.getString("keyspace", "GROW")); -        mDatabase.init(); -    } - -    @Override -    public void stop() throws Exception { -        mDatabase.close(); -    } - -    /** -     * @return the current database. -     */ -    public CassandraDatabase getDatabase() { -        return mDatabase; -    } - -    @Override -    public Provider<String, UserRecord> getUserRecordProvider() { -        return mUserRecordProvider; -    } - -    @Override -    public Provider<String, Question> getQuestionProvider() { -        return mQuestionProvider; -    } - -    @Override -    public Provider<String, TrainingRecord> getTrainingRecordProvider() { -        return mTrainingRecordProvider; -    } - -    @Override -    public CollectionProvider<String, String, String> getVideoProvider() { -        return mVideoProvider; -    } - -    @Override -    public Playlist getDefaultPlaylist() throws IOException { -        return mTrainingRecordProvider.getDefaultPlaylist(); -    } - -    @Override -    public CollectionProvider<String, String, MessageThread> getThreadProvider() { -        return mFeedThreadProvider; -    } - -    @Override -    public CollectionProvider<String, String, Message> getMessageProvider() { -        return mFeedMessageProvider; -    } - -    @Override -    public Provider<String, String> getStringProvider() { -        return mStringProvider; -    } - -    @Override -    public CollectionProvider<String, String, String> getAnswerProvider() { -        return mAnswerProvider; -    } -} diff --git a/src/com/p4square/grow/backend/DynamoGrowData.java b/src/com/p4square/grow/backend/DynamoGrowData.java deleted file mode 100644 index 3b38eac..0000000 --- a/src/com/p4square/grow/backend/DynamoGrowData.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.backend; - -import java.io.IOException; - -import com.amazonaws.auth.AWSCredentials; - -import com.p4square.grow.backend.dynamo.DynamoDatabase; -import com.p4square.grow.backend.dynamo.DynamoKey; -import com.p4square.grow.backend.dynamo.DynamoProviderImpl; -import com.p4square.grow.backend.dynamo.DynamoCollectionProviderImpl; - -import com.p4square.grow.config.Config; - -import com.p4square.grow.model.Message; -import com.p4square.grow.model.MessageThread; -import com.p4square.grow.model.Playlist; -import com.p4square.grow.model.Question; -import com.p4square.grow.model.TrainingRecord; -import com.p4square.grow.model.UserRecord; - -import com.p4square.grow.provider.CollectionProvider; -import com.p4square.grow.provider.DelegateCollectionProvider; -import com.p4square.grow.provider.DelegateProvider; -import com.p4square.grow.provider.Provider; -import com.p4square.grow.provider.JsonEncodedProvider; - -/** - *  - * @author Jesse Morgan <jesse@jesterpm.net> - */ -class DynamoGrowData implements GrowData { -    private static final String DEFAULT_COLUMN = "value"; -    private static final String DEFAULT_PLAYLIST_KEY = "/training/defaultplaylist"; - -    private final Config mConfig; -    private final DynamoDatabase mDatabase; - -    private final Provider<String, UserRecord> mUserRecordProvider; - -    private final Provider<String, Question> mQuestionProvider; -    private final Provider<String, TrainingRecord> mTrainingRecordProvider; -    private final CollectionProvider<String, String, String> mVideoProvider; - -    private final CollectionProvider<String, String, MessageThread> mFeedThreadProvider; -    private final CollectionProvider<String, String, Message> mFeedMessageProvider; - -    private final Provider<String, String> mStringProvider; - -    private final CollectionProvider<String, String, String> mAnswerProvider; - -    public DynamoGrowData(final Config config) { -        mConfig = config; - -        mDatabase = new DynamoDatabase(config); - -        mUserRecordProvider = new DelegateProvider<String, DynamoKey, UserRecord>( -                new DynamoProviderImpl<UserRecord>(mDatabase, UserRecord.class)) { -            @Override -            public DynamoKey makeKey(String userid) { -                return DynamoKey.newAttributeKey("accounts", userid, DEFAULT_COLUMN); -            } -        }; - -        mQuestionProvider = new DelegateProvider<String, DynamoKey, Question>( -                new DynamoProviderImpl<Question>(mDatabase, Question.class)) { -            @Override -            public DynamoKey makeKey(String questionId) { -                return DynamoKey.newAttributeKey("strings", -                                                 "/questions/" + questionId, -                                                 DEFAULT_COLUMN); -            } -        }; - -        mFeedThreadProvider = new DynamoCollectionProviderImpl<MessageThread>( -                mDatabase, "feedthreads", MessageThread.class); -        mFeedMessageProvider = new DynamoCollectionProviderImpl<Message>( -                mDatabase, "feedmessages", Message.class); - -        mTrainingRecordProvider = new DelegateProvider<String, DynamoKey, TrainingRecord>( -                new DynamoProviderImpl<TrainingRecord>(mDatabase, TrainingRecord.class)) { -            @Override -            public DynamoKey makeKey(String userId) { -                return DynamoKey.newAttributeKey("training", -                                                 userId, -                                                 DEFAULT_COLUMN); -            } -        }; - -        mVideoProvider = new DelegateCollectionProvider<String, String, String, String, String>( -                new DynamoCollectionProviderImpl<String>(mDatabase, "strings", String.class)) { -            @Override -            public String makeCollectionKey(String key) { -                return "/training/" + key; -            } - -            @Override -            public String makeKey(String key) { -                return key; -            } - -            @Override -            public String unmakeKey(String key) { -                return key; -            } -        }; - -        mStringProvider = new DelegateProvider<String, DynamoKey, String>( -                new DynamoProviderImpl<String>(mDatabase, String.class)) { -            @Override -            public DynamoKey makeKey(String id) { -                return DynamoKey.newAttributeKey("strings", id, DEFAULT_COLUMN); -            } -        }; - -        mAnswerProvider = new DynamoCollectionProviderImpl<String>( -                mDatabase, "assessments", String.class); -    } - -    @Override -    public void start() throws Exception { -    } - -    @Override -    public void stop() throws Exception { -    } - -    @Override -    public Provider<String, UserRecord> getUserRecordProvider() { -        return mUserRecordProvider; -    } - -    @Override -    public Provider<String, Question> getQuestionProvider() { -        return mQuestionProvider; -    } - -    @Override -    public Provider<String, TrainingRecord> getTrainingRecordProvider() { -        return mTrainingRecordProvider; -    } - -    @Override -    public CollectionProvider<String, String, String> getVideoProvider() { -        return mVideoProvider; -    } - -    @Override -    public Playlist getDefaultPlaylist() throws IOException { -        String blob = mStringProvider.get(DEFAULT_PLAYLIST_KEY); -        if (blob == null) { -            return null; -        } - -        return JsonEncodedProvider.MAPPER.readValue(blob, Playlist.class); -    } - -    @Override -    public CollectionProvider<String, String, MessageThread> getThreadProvider() { -        return mFeedThreadProvider; -    } - -    @Override -    public CollectionProvider<String, String, Message> getMessageProvider() { -        return mFeedMessageProvider; -    } - -    @Override -    public Provider<String, String> getStringProvider() { -        return mStringProvider; -    } - -    @Override -    public CollectionProvider<String, String, String> getAnswerProvider() { -        return mAnswerProvider; -    } -} diff --git a/src/com/p4square/grow/backend/GrowBackend.java b/src/com/p4square/grow/backend/GrowBackend.java deleted file mode 100644 index 4091138..0000000 --- a/src/com/p4square/grow/backend/GrowBackend.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright 2012 Jesse Morgan - */ - -package com.p4square.grow.backend; - -import java.io.IOException; - -import com.codahale.metrics.MetricRegistry; - -import org.apache.log4j.Logger; - -import org.restlet.Application; -import org.restlet.Component; -import org.restlet.Restlet; -import org.restlet.data.Protocol; -import org.restlet.data.Reference; -import org.restlet.resource.Directory; -import org.restlet.routing.Router; - -import com.p4square.grow.config.Config; - -import com.p4square.grow.model.Message; -import com.p4square.grow.model.MessageThread; -import com.p4square.grow.model.Playlist; -import com.p4square.grow.model.Question; -import com.p4square.grow.model.TrainingRecord; -import com.p4square.grow.model.UserRecord; - -import com.p4square.grow.provider.CollectionProvider; -import com.p4square.grow.provider.Provider; -import com.p4square.grow.provider.ProvidesQuestions; -import com.p4square.grow.provider.ProvidesTrainingRecords; -import com.p4square.grow.provider.ProvidesUserRecords; - -import com.p4square.grow.backend.resources.AccountResource; -import com.p4square.grow.backend.resources.BannerResource; -import com.p4square.grow.backend.resources.SurveyResource; -import com.p4square.grow.backend.resources.SurveyResultsResource; -import com.p4square.grow.backend.resources.TrainingRecordResource; -import com.p4square.grow.backend.resources.TrainingResource; - -import com.p4square.grow.backend.feed.FeedDataProvider; -import com.p4square.grow.backend.feed.ThreadResource; -import com.p4square.grow.backend.feed.TopicResource; - -import com.p4square.restlet.metrics.MetricRouter; - -/** - * Main class for the backend application. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class GrowBackend extends Application implements GrowData { - -    private final static Logger LOG = Logger.getLogger(GrowBackend.class); - -    private final MetricRegistry mMetricRegistry; - -    private final Config mConfig; -    private final GrowData mGrowData; - -    public GrowBackend() { -        this(new Config(), new MetricRegistry()); -    } - -    public GrowBackend(Config config, MetricRegistry metricRegistry) { -        mConfig = config; - -        mMetricRegistry = metricRegistry; - -        mGrowData = new DynamoGrowData(config); -    } - -    public MetricRegistry getMetrics() { -        return mMetricRegistry; -    } - -    @Override -    public Restlet createInboundRoot() { -        Router router = new MetricRouter(getContext(), mMetricRegistry); - -        // Account API -        router.attach("/accounts/{userId}", AccountResource.class); - -        // Survey API -        router.attach("/assessment/question/{questionId}", SurveyResource.class); - -        router.attach("/accounts/{userId}/assessment", SurveyResultsResource.class); -        router.attach("/accounts/{userId}/assessment/answers/{questionId}", -                SurveyResultsResource.class); - -        // Training API -        router.attach("/training/{level}", TrainingResource.class); -        router.attach("/training/{level}/videos/{videoId}", TrainingResource.class); - -        router.attach("/accounts/{userId}/training", TrainingRecordResource.class); -        router.attach("/accounts/{userId}/training/videos/{videoId}", -                TrainingRecordResource.class); - -        // Misc. -        router.attach("/banner", BannerResource.class); - -        // Feed -        router.attach("/feed/{topic}", TopicResource.class); -        router.attach("/feed/{topic}/{thread}", ThreadResource.class); -        //router.attach("/feed/{topic/{thread}/{message}", MessageResource.class); - -        router.attachDefault(new Directory(getContext(), new Reference(getClass().getResource("apiinfo.html")))); - -        return router; -    } - -    /** -     * Open the database. -     */ -    @Override -    public void start() throws Exception { -        super.start(); - -        mGrowData.start(); -    } - -    /** -     * Close the database. -     */ -    @Override -    public void stop() throws Exception { -        LOG.info("Shutting down..."); -        mGrowData.stop(); - -        super.stop(); -    } - -    @Override -    public Provider<String, UserRecord> getUserRecordProvider() { -        return mGrowData.getUserRecordProvider(); -    } - -    @Override -    public Provider<String, Question> getQuestionProvider() { -        return mGrowData.getQuestionProvider(); -    } - -    @Override -    public CollectionProvider<String, String, String> getVideoProvider() { -        return mGrowData.getVideoProvider(); -    } - -    @Override -    public Provider<String, TrainingRecord> getTrainingRecordProvider() { -        return mGrowData.getTrainingRecordProvider(); -    } - -    /** -     * @return the Default Playlist. -     */ -    public Playlist getDefaultPlaylist() throws IOException { -        return mGrowData.getDefaultPlaylist(); -    } - -    @Override -    public CollectionProvider<String, String, MessageThread> getThreadProvider() { -        return mGrowData.getThreadProvider(); -    } - -    @Override -    public CollectionProvider<String, String, Message> getMessageProvider() { -        return mGrowData.getMessageProvider(); -    } - -    @Override -    public Provider<String, String> getStringProvider() { -        return mGrowData.getStringProvider(); -    } - -    @Override -    public CollectionProvider<String, String, String> getAnswerProvider() { -        return mGrowData.getAnswerProvider(); -    } - -    /** -     * Stand-alone main for testing. -     */ -    public static void main(String[] args) throws Exception { -        // Start the HTTP Server -        final Component component = new Component(); -        component.getServers().add(Protocol.HTTP, 9095); -        component.getClients().add(Protocol.HTTP); -        component.getDefaultHost().attach(new GrowBackend()); - -        // Setup shutdown hook -        Runtime.getRuntime().addShutdownHook(new Thread() { -            public void run() { -                try { -                    component.stop(); -                } catch (Exception e) { -                    LOG.error("Exception during cleanup", e); -                } -            } -        }); - -        LOG.info("Starting server..."); - -        try { -            component.start(); -        } catch (Exception e) { -            LOG.fatal("Could not start: " + e.getMessage(), e); -        } -    } -} diff --git a/src/com/p4square/grow/backend/GrowData.java b/src/com/p4square/grow/backend/GrowData.java deleted file mode 100644 index 293bb88..0000000 --- a/src/com/p4square/grow/backend/GrowData.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.backend; - -import com.p4square.grow.backend.feed.FeedDataProvider; -import com.p4square.grow.model.Playlist; -import com.p4square.grow.provider.ProvidesAssessments; -import com.p4square.grow.provider.ProvidesQuestions; -import com.p4square.grow.provider.ProvidesStrings; -import com.p4square.grow.provider.ProvidesTrainingRecords; -import com.p4square.grow.provider.ProvidesUserRecords; -import com.p4square.grow.provider.ProvidesVideos; - -/** - * Aggregate of the data provider interfaces. - * - * Used by GrowBackend to swap out implementations of the providers. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -interface GrowData extends ProvidesQuestions, ProvidesTrainingRecords, ProvidesVideos, -                                   FeedDataProvider, ProvidesUserRecords, ProvidesStrings, -                                   ProvidesAssessments { - -    /** -     * Start the data provider. -     */ -    void start() throws Exception; - -    /** -     * Stop the data provider. -     */ -    void stop() throws Exception; -} diff --git a/src/com/p4square/grow/backend/apiinfo.html b/src/com/p4square/grow/backend/apiinfo.html deleted file mode 100644 index a3637c9..0000000 --- a/src/com/p4square/grow/backend/apiinfo.html +++ /dev/null @@ -1,41 +0,0 @@ -<html> -<head> -<title>API Info</title> -</head> -<body> -<dl> -<dt>/backend/accounts/{userId}</dt> -<dd>GET information about <em>userId</em> or PUT new information.</dd> - -<dt>/backend/assessment/question/{questionId}</dt> -<dd>GET information about <em>questionId</em>. Special <em>questionId</em>s: first identifies first question. count returns total number of questions.</dd> - -<dt>/backend/accounts/{userId}/assessment</dt> -<dd>GET the assessment summary for <em>userId</em> or DELETE <em>userId</em>'s assessment.</dd> - -<dt>/backend/accounts/{userId}/assessment/answers/{questionId}</dt> -<dd>GET <em>userId</em>'s answer to <em>questionId</em>, PUT a new answer, or DELETE an answer.</dd> - -<dt>/backend/training/{level}</dt> -<dd>GET all video information for <em>level</em>.</dd> - -<dt>/backend/training/{level}/videos/{videoId}</dt> -<dd>GET video information for <em>videoId</em> in <em>level</em>.</dd> - -<dt>/backend/accounts/{userId}/training</dt> -<dd>GET training record summary for <em>userId</em>.</dd> - -<dt>/backend/accounts/{userId}/training/videos/{videoId}</dt> -<dd>GET training record for <em>userId</em> and <em>videoId</em> or PUT a new record.</dd> - -<dt>/backend/banner</dt> -<dd>GET the info banner or PUT new banner info.</dd> - -<dt>/backend/feed/{topic}</dt> -<dd>Get all threads for forum <em>topic</em>.</dd> - -<dt>/backend/feed/{topic}/{thread}</dt> -<dd>Get all responses to question <em>thread</em> on forum <em>topic</em>.</dd> -</dl> -</body> -</html> diff --git a/src/com/p4square/grow/backend/db/CassandraCollectionProvider.java b/src/com/p4square/grow/backend/db/CassandraCollectionProvider.java deleted file mode 100644 index bfcb48d..0000000 --- a/src/com/p4square/grow/backend/db/CassandraCollectionProvider.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.db; - -import java.io.IOException; - -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; - -import com.netflix.astyanax.model.Column; -import com.netflix.astyanax.model.ColumnList; - -import com.p4square.grow.provider.CollectionProvider; -import com.p4square.grow.provider.JsonEncodedProvider; - -/** - * CollectionProvider implementation backed by a Cassandra ColumnFamily. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class CassandraCollectionProvider<V> implements CollectionProvider<String, String, V> { -    private final CassandraDatabase mDb; -    private final String mCF; -    private final Class<V> mClazz; - -    public CassandraCollectionProvider(CassandraDatabase db, String columnFamily, Class<V> clazz) { -        mDb = db; -        mCF = columnFamily; -        mClazz = clazz; -    } - -    @Override -    public V get(String collection, String key) throws IOException { -        String blob = mDb.getKey(mCF, collection, key); -        return decode(blob); -    } - -    @Override -    public Map<String, V> query(String collection) throws IOException { -        return query(collection, -1); -    } - -    @Override -    public Map<String, V> query(String collection, int limit) throws IOException { -        Map<String, V> result = new LinkedHashMap<>(); - -        ColumnList<String> row = mDb.getRow(mCF, collection); -        if (!row.isEmpty()) { -            int count = 0; -            for (Column<String> c : row) { -                if (limit >= 0 && ++count > limit) { -                    break; // Limit reached. -                } - -                String key = c.getName(); -                String blob = c.getStringValue(); -                V obj = decode(blob); - -                result.put(key, obj); -            } -        } - -        return Collections.unmodifiableMap(result); -    } - -    @Override -    public void put(String collection, String key, V obj) throws IOException { -        String blob = encode(obj); -        mDb.putKey(mCF, collection, key, blob); -    } - -    /** -     * Encode the object as JSON. -     * -     * @param obj The object to encode. -     * @return The JSON encoding of obj. -     * @throws IOException if the object cannot be encoded. -     */ -    protected String encode(V obj) throws IOException { -        if (mClazz == String.class) { -            return (String) obj; -        } else { -            return JsonEncodedProvider.MAPPER.writeValueAsString(obj); -        } -    } - -    /** -     * Decode the JSON string as an object. -     * -     * @param blob The JSON data to decode. -     * @return The decoded object or null if blob is null. -     * @throws IOException If an object cannot be decoded. -     */ -    protected V decode(String blob) throws IOException { -        if (blob == null) { -            return null; -        } - -        if (mClazz == String.class) { -            return (V) blob; -        } - -        V obj = JsonEncodedProvider.MAPPER.readValue(blob, mClazz); -        return obj; -    } -} diff --git a/src/com/p4square/grow/backend/db/CassandraDatabase.java b/src/com/p4square/grow/backend/db/CassandraDatabase.java deleted file mode 100644 index b8cb6df..0000000 --- a/src/com/p4square/grow/backend/db/CassandraDatabase.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.db; - -import com.netflix.astyanax.AstyanaxContext; -import com.netflix.astyanax.connectionpool.exceptions.ConnectionException; -import com.netflix.astyanax.connectionpool.impl.ConnectionPoolConfigurationImpl; -import com.netflix.astyanax.connectionpool.impl.CountingConnectionPoolMonitor; -import com.netflix.astyanax.connectionpool.NodeDiscoveryType; -import com.netflix.astyanax.connectionpool.OperationResult; -import com.netflix.astyanax.impl.AstyanaxConfigurationImpl; -import com.netflix.astyanax.Keyspace; -import com.netflix.astyanax.ColumnMutation; -import com.netflix.astyanax.model.Column; -import com.netflix.astyanax.model.ColumnFamily; -import com.netflix.astyanax.model.ColumnList; -import com.netflix.astyanax.ColumnListMutation; -import com.netflix.astyanax.MutationBatch; -import com.netflix.astyanax.serializers.StringSerializer; -import com.netflix.astyanax.thrift.ThriftFamilyFactory; - -import org.apache.log4j.Logger; - -/** - * Cassandra Database Abstraction for the Backend. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class CassandraDatabase { -    private static Logger cLog = Logger.getLogger(CassandraDatabase.class); - -    // Configuration fields. -    private String mClusterName; -    private String mKeyspaceName; -    private String mSeedEndpoint   = "127.0.0.1:9160"; -    private int    mPort           = 9160; - -    private AstyanaxContext<Keyspace>  mContext; -    private Keyspace mKeyspace; - -    /** -     * Connect to Cassandra. -     * -     * Cluster and Keyspace must be set before calling init(). -     */ -    public void init() { -        mContext = new AstyanaxContext.Builder() -            .forCluster(mClusterName) -            .forKeyspace(mKeyspaceName) -            .withAstyanaxConfiguration(new AstyanaxConfigurationImpl() -                .setDiscoveryType(NodeDiscoveryType.RING_DESCRIBE) -            ) -            .withConnectionPoolConfiguration(new ConnectionPoolConfigurationImpl("MyConnectionPool") -                .setPort(mPort) -                .setMaxConnsPerHost(1) -                .setSeeds(mSeedEndpoint) -            ) -            .withConnectionPoolMonitor(new CountingConnectionPoolMonitor()) -            .buildKeyspace(ThriftFamilyFactory.getInstance()); - -        mContext.start(); -        mKeyspace = mContext.getClient(); -    } - -    /** -     * Close the database connection. -     */ -    public void close() { -        mContext.shutdown(); -    } - -    /** -     * Set the cluster name to connect to. -     */ -    public void setClusterName(final String cluster) { -        mClusterName = cluster; -    } - -    /** -     * Set the name of the keyspace to open. -     */ -    public void setKeyspaceName(final String keyspace) { -        mKeyspaceName = keyspace; -    } - -    /** -     * Change the seed endpoint. -     * The default is 127.0.0.1:9160. -     */ -    public void setSeedEndpoint(final String endpoint) { -        mSeedEndpoint = endpoint; -    } - -    /** -     * Change the port to connect to. -     * The default is 9160. -     */ -    public void setPort(final int port) { -        mPort = port; -    } - -    /** -     * @return The entire row associated with this key. -     */ -    public ColumnList<String> getRow(final String cfName, final String key) { -        try { -            ColumnFamily<String, String> cf = new ColumnFamily(cfName, -                StringSerializer.get(), -                StringSerializer.get()); - -            OperationResult<ColumnList<String>> result = -                mKeyspace.prepareQuery(cf) -                    .getKey(key) -                    .execute(); - -            return result.getResult(); - -        } catch (ConnectionException e) { -            cLog.error("getRow failed due to Connection Exception", e); -            throw new RuntimeException(e); -        } -    } - -    /** -     * @return The value associated with the given key. -     */ -    public String getKey(final String cfName, final String key) { -        return getKey(cfName, key, "value"); -    } - -    /** -     * @return The value associated with the given key, column pair. -     */ -    public String getKey(final String cfName, final String key, final String column) { -        final ColumnList<String> row = getRow(cfName, key); - -        if (row != null) { -            final Column rowColumn = row.getColumnByName(column); -            if (rowColumn != null) { -                return rowColumn.getStringValue(); -            } -        } - -        return null; -    } - -    /** -     * Assign value to key. -     */ -    public void putKey(final String cfName, final String key, final String value) { -        putKey(cfName, key, "value", value); -    } - -    /** -     * Assign value to the key, column pair. -     */ -    public void putKey(final String cfName, final String key, -            final String column, final String value) { - -        ColumnFamily<String, String> cf = new ColumnFamily(cfName, -            StringSerializer.get(), -            StringSerializer.get()); - -        MutationBatch m = mKeyspace.prepareMutationBatch(); -        m.withRow(cf, key).putColumn(column, value); - -        try { -            m.execute(); -        } catch (ConnectionException e) { -            cLog.error("putKey failed due to Connection Exception", e); -            throw new RuntimeException(e); -        } -    } - -    /** -     * Remove a key, column pair. -     */ -    public void deleteKey(final String cfName, final String key, final String column) { -        ColumnFamily<String, String> cf = new ColumnFamily(cfName, -            StringSerializer.get(), -            StringSerializer.get()); - -        try { -            ColumnMutation m = mKeyspace.prepareColumnMutation(cf, key, column); -            m.deleteColumn().execute(); -        } catch (ConnectionException e) { -            cLog.error("deleteKey failed due to Connection Exception", e); -            throw new RuntimeException(e); -        } -    } - -    /** -     * Remove a row -     */ -    public void deleteRow(final String cfName, final String key) { -        ColumnFamily<String, String> cf = new ColumnFamily(cfName, -            StringSerializer.get(), -            StringSerializer.get()); - -        try { -            MutationBatch batch = mKeyspace.prepareMutationBatch(); -            ColumnListMutation<String> cfm = batch.withRow(cf, key).delete(); -            batch.execute(); - -        } catch (ConnectionException e) { -            cLog.error("deleteRow failed due to Connection Exception", e); -            throw new RuntimeException(e); -        } -    } -} diff --git a/src/com/p4square/grow/backend/db/CassandraKey.java b/src/com/p4square/grow/backend/db/CassandraKey.java deleted file mode 100644 index 853fe96..0000000 --- a/src/com/p4square/grow/backend/db/CassandraKey.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.db; - -/** - * CassandraKey represents a Cassandra key / column pair. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class CassandraKey { -    private final String mColumnFamily; -    private final String mId; -    private final String mColumn; - -    public CassandraKey(String columnFamily, String id, String column) { -        mColumnFamily = columnFamily; -        mId = id; -        mColumn = column; -    } - -    public String getColumnFamily() { -        return mColumnFamily; -    } - -    public String getId() { -        return mId; -    } - -    public String getColumn() { -        return mColumn; -    } -} diff --git a/src/com/p4square/grow/backend/db/CassandraProviderImpl.java b/src/com/p4square/grow/backend/db/CassandraProviderImpl.java deleted file mode 100644 index da5a9f2..0000000 --- a/src/com/p4square/grow/backend/db/CassandraProviderImpl.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.db; - -import java.io.IOException; - -import com.p4square.grow.provider.Provider; -import com.p4square.grow.provider.JsonEncodedProvider; - -/** - * Provider implementation backed by a Cassandra ColumnFamily. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class CassandraProviderImpl<V> extends JsonEncodedProvider<V> implements Provider<CassandraKey, V> { -    private final CassandraDatabase mDb; - -    public CassandraProviderImpl(CassandraDatabase db, Class<V> clazz) { -        super(clazz); - -        mDb = db; -    } - -    @Override -    public V get(CassandraKey key) throws IOException { -        String blob = mDb.getKey(key.getColumnFamily(), key.getId(), key.getColumn()); -        return decode(blob); -    } - -    @Override -    public void put(CassandraKey key, V obj) throws IOException { -        String blob = encode(obj); -        mDb.putKey(key.getColumnFamily(), key.getId(), key.getColumn(), blob); -    } -} diff --git a/src/com/p4square/grow/backend/db/CassandraTrainingRecordProvider.java b/src/com/p4square/grow/backend/db/CassandraTrainingRecordProvider.java deleted file mode 100644 index 4face52..0000000 --- a/src/com/p4square/grow/backend/db/CassandraTrainingRecordProvider.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.db; - -import java.io.IOException; - -import com.p4square.grow.model.Playlist; -import com.p4square.grow.model.TrainingRecord; - -import com.p4square.grow.provider.JsonEncodedProvider; -import com.p4square.grow.provider.Provider; - -/** - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class CassandraTrainingRecordProvider implements Provider<String, TrainingRecord> { -    private static final CassandraKey DEFAULT_PLAYLIST_KEY = new CassandraKey("strings", "defaultPlaylist", "value"); - -    private static final String COLUMN_FAMILY = "training"; -    private static final String PLAYLIST_KEY = "playlist"; -    private static final String LAST_VIDEO_KEY = "lastVideo"; - -    private final CassandraDatabase mDb; -    private final Provider<CassandraKey, Playlist> mPlaylistProvider; - -    public CassandraTrainingRecordProvider(CassandraDatabase db) { -        mDb = db; -        mPlaylistProvider = new CassandraProviderImpl<>(db, Playlist.class); -    } - -    @Override -    public TrainingRecord get(String userid) throws IOException { -        Playlist playlist = mPlaylistProvider.get(new CassandraKey(COLUMN_FAMILY, userid, PLAYLIST_KEY)); - -        if (playlist == null) { -            // We consider no playlist to mean no record whatsoever. -            return null; -        } - -        TrainingRecord r = new TrainingRecord(); -        r.setPlaylist(playlist); -        r.setLastVideo(mDb.getKey(COLUMN_FAMILY, userid, LAST_VIDEO_KEY)); - -        return r; -    } - -    @Override -    public void put(String userid, TrainingRecord record) throws IOException { -        String lastVideo = record.getLastVideo(); -        Playlist playlist = record.getPlaylist(); - -        mDb.putKey(COLUMN_FAMILY, userid, LAST_VIDEO_KEY, lastVideo); -        mPlaylistProvider.put(new CassandraKey(COLUMN_FAMILY, userid, PLAYLIST_KEY), playlist); -    } - -    /** -     * @return the default playlist stored in the database. -     */ -    public Playlist getDefaultPlaylist() throws IOException { -        Playlist playlist = mPlaylistProvider.get(DEFAULT_PLAYLIST_KEY); - -        if (playlist == null) { -            playlist = new Playlist(); -        } - -        return playlist; -    } -} diff --git a/src/com/p4square/grow/backend/dynamo/DbTool.java b/src/com/p4square/grow/backend/dynamo/DbTool.java deleted file mode 100644 index 374fa83..0000000 --- a/src/com/p4square/grow/backend/dynamo/DbTool.java +++ /dev/null @@ -1,481 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.backend.dynamo; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.io.File; -import java.io.FilenameFilter; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; - -import com.p4square.grow.backend.dynamo.DynamoDatabase; -import com.p4square.grow.backend.dynamo.DynamoKey; -import com.p4square.grow.config.Config; -import com.p4square.grow.model.UserRecord; -import com.p4square.grow.provider.Provider; - -/** - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class DbTool { -    private static final FilenameFilter JSON_FILTER = new JsonFilter(); - -    private static Config mConfig; -    private static DynamoDatabase mDatabase; - -    public static void usage() { -        System.out.println("java com.p4square.grow.backend.dynamo.DbTool <command>...\n"); -        System.out.println("Commands:"); -        System.out.println("\t--domain <domain>                           Set config domain"); -        System.out.println("\t--dev                                       Set config domain to dev"); -        System.out.println("\t--config <file>                             Merge in config file"); -        System.out.println("\t--list                                      List all tables"); -        System.out.println("\t--create <table> <reads> <writes>           Create a table"); -        System.out.println("\t--update <table> <reads> <writes>           Update table throughput"); -        System.out.println("\t--drop   <table>                            Delete a table"); -        System.out.println("\t--get    <table> <key> <attribute>          Get a value"); -        System.out.println("\t--put    <table> <key> <attribute> <value>  Put a value"); -        System.out.println("\t--delete <table> <key> <attribute>          Delete a value"); -        System.out.println("\t--scan   <table>                            List all rows"); -        System.out.println("\t--scanf  <table> <attribute>                List all rows"); -        System.out.println(); -        System.out.println("Bootstrap Commands:"); -        System.out.println("\t--bootstrap <data>          Create all tables and import all data"); -        System.out.println("\t--loadStrings <data>        Load all videos and questions"); -        System.out.println("\t--destroy                   Drop all tables"); -        System.out.println("\t--addadmin <user> <pass>    Add a backend account"); -        System.out.println("\t--import   <table> <file>   Backfill a table"); -    } - -    public static void main(String... args) { -        if (args.length == 0) { -            usage(); -            System.exit(1); -        } - -        mConfig = new Config(); - -        try { -            mConfig.updateConfig(DbTool.class.getResourceAsStream("/grow.properties")); - -            int offset = 0; -            while (offset < args.length) { -                if ("--domain".equals(args[offset])) { -                    mConfig.setDomain(args[offset + 1]); -                    mDatabase = null; -                    offset += 2; - -                } else if ("--dev".equals(args[offset])) { -                    mConfig.setDomain("dev"); -                    mDatabase = null; -                    offset += 1; - -                } else if ("--config".equals(args[offset])) { -                    mConfig.updateConfig(args[offset + 1]); -                    mDatabase = null; -                    offset += 2; - -                } else if ("--list".equals(args[offset])) { -                    //offset = list(args, ++offset); - -                } else if ("--create".equals(args[offset])) { -                    offset = create(args, ++offset); - -                } else if ("--update".equals(args[offset])) { -                    offset = update(args, ++offset); - -                } else if ("--drop".equals(args[offset])) { -                    offset = drop(args, ++offset); - -                } else if ("--get".equals(args[offset])) { -                    offset = get(args, ++offset); - -                } else if ("--put".equals(args[offset])) { -                    offset = put(args, ++offset); - -                } else if ("--delete".equals(args[offset])) { -                    offset = delete(args, ++offset); - -                } else if ("--scan".equals(args[offset])) { -                    offset = scan(args, ++offset); - -                } else if ("--scanf".equals(args[offset])) { -                    offset = scanf(args, ++offset); - -                /* Bootstrap Commands */ -                } else if ("--bootstrap".equals(args[offset])) { -                    if ("dev".equals(mConfig.getDomain())) { -                        offset = bootstrapDevTables(args, ++offset); -                    } else { -                        offset = bootstrapTables(args, ++offset); -                    } -                    offset = loadStrings(args, offset); - -                } else if ("--loadStrings".equals(args[offset])) { -                    offset = loadStrings(args, ++offset); - -                } else if ("--destroy".equals(args[offset])) { -                    offset = destroy(args, ++offset); - -                } else if ("--addadmin".equals(args[offset])) { -                    offset = addAdmin(args, ++offset); - -                } else if ("--import".equals(args[offset])) { -                    offset = importTable(args, ++offset); - -                } else { -                    throw new IllegalArgumentException("Unknown command " + args[offset]); -                } -            } -        } catch (Exception e) { -            e.printStackTrace(); -            System.exit(2); -        } -    } - -    private static DynamoDatabase getDatabase() { -        if (mDatabase == null) { -            mDatabase = new DynamoDatabase(mConfig); -        } - -        return mDatabase; -    } - -    private static int create(String[] args, int offset) { -        String name = args[offset++]; -        long reads  = Long.parseLong(args[offset++]); -        long writes = Long.parseLong(args[offset++]); - -        DynamoDatabase db = getDatabase(); - -        db.createTable(name, reads, writes); - -        return offset; -    } - -    private static int update(String[] args, int offset) { -        String name = args[offset++]; -        long reads  = Long.parseLong(args[offset++]); -        long writes = Long.parseLong(args[offset++]); - -        DynamoDatabase db = getDatabase(); - -        db.updateTable(name, reads, writes); - -        return offset; -    } - -    private static int drop(String[] args, int offset) { -        String name = args[offset++]; - -        DynamoDatabase db = getDatabase(); - -        db.deleteTable(name); - -        return offset; -    } - -    private static int get(String[] args, int offset) { -        String table     = args[offset++]; -        String key       = args[offset++]; -        String attribute = args[offset++]; - -        DynamoDatabase db = getDatabase(); - -        String value = db.getAttribute(DynamoKey.newAttributeKey(table, key, attribute)); - -        if (value == null) { -            value = "<null>"; -        } - -        System.out.printf("%s %s:%s\n%s\n\n", table, key, attribute, value); - -        return offset; -    } - -    private static int put(String[] args, int offset) { -        String table     = args[offset++]; -        String key       = args[offset++]; -        String attribute = args[offset++]; -        String value     = args[offset++]; - -        DynamoDatabase db = getDatabase(); - -        db.putAttribute(DynamoKey.newAttributeKey(table, key, attribute), value); - -        return offset; -    } - -    private static int delete(String[] args, int offset) { -        String table     = args[offset++]; -        String key       = args[offset++]; -        String attribute = args[offset++]; - -        DynamoDatabase db = getDatabase(); - -        db.deleteAttribute(DynamoKey.newAttributeKey(table, key, attribute)); - -        System.out.printf("Deleted %s %s:%s\n\n", table, key, attribute); - -        return offset; -    } - -    private static int scan(String[] args, int offset) { -        String table     = args[offset++]; - -        DynamoKey key = DynamoKey.newKey(table, null); - -        doScan(key); - -        return offset; -    } - -    private static int scanf(String[] args, int offset) { -        String table     = args[offset++]; -        String attribute = args[offset++]; - -        DynamoKey key = DynamoKey.newAttributeKey(table, null, attribute); - -        doScan(key); - -        return offset; -    } - -    private static void doScan(DynamoKey key) { -        DynamoDatabase db = getDatabase(); - -        String attributeFilter = key.getAttribute(); - -        while (key != null) { -            Map<DynamoKey, Map<String, String>> result = db.getAll(key); - -            key = null; // If there are no results, exit - -            for (Map.Entry<DynamoKey, Map<String, String>> entry : result.entrySet()) { -                key = entry.getKey(); // Save the last key - -                for (Map.Entry<String, String> attribute : entry.getValue().entrySet()) { -                    if (attributeFilter == null || attributeFilter.equals(attribute.getKey())) { -                        String keyString = key.getHashKey(); -                        if (key.getRangeKey() != null) { -                            keyString += "(" + key.getRangeKey() + ")"; -                        } -                        System.out.printf("%s %s:%s\n%s\n\n", -                                key.getTable(), keyString, attribute.getKey(), -                                attribute.getValue()); -                    } -                } -            } -        } -    } - - -    private static int bootstrapTables(String[] args, int offset) { -        DynamoDatabase db = getDatabase(); - -        db.createTable("strings",      5,  1); -        db.createTable("accounts",     5,  1); -        db.createTable("assessments",  5,  5); -        db.createTable("training",     5,  5); -        db.createTable("feedthreads",  5,  1); -        db.createTable("feedmessages", 5,  1); - -        return offset; -    } - -    private static int bootstrapDevTables(String[] args, int offset) { -        DynamoDatabase db = getDatabase(); - -        db.createTable("strings",      1,  1); -        db.createTable("accounts",     1,  1); -        db.createTable("assessments",  1,  1); -        db.createTable("training",     1,  1); -        db.createTable("feedthreads",  1,  1); -        db.createTable("feedmessages", 1,  1); - -        return offset; -    } - -    private static int loadStrings(String[] args, int offset) throws IOException { -        String data = args[offset++]; -        File baseDir = new File(data); - -        DynamoDatabase db = getDatabase(); - -        insertQuestions(baseDir); -        insertVideos(baseDir); -        insertDefaultPlaylist(baseDir); - -        return offset; -    } - -    private static int destroy(String[] args, int offset) { -        DynamoDatabase db = getDatabase(); - -        final String[] tables = { "strings", -                                  "accounts", -                                  "assessments", -                                  "training", -                                  "feedthreads", -                                  "feedmessages" -                                }; - -        for (String table : tables) { -            try { -                db.deleteTable(table); -            } catch (Exception e) { -                System.err.println("Deleting " + table + ": " + e.getMessage()); -            } -        } - -        return offset; -    } - -    private static int addAdmin(String[] args, int offset) throws IOException { -        String user = args[offset++]; -        String pass = args[offset++]; - -        DynamoDatabase db = getDatabase(); - -        UserRecord record = new UserRecord(); -        record.setId(user); -        record.setBackendPassword(pass); - -        Provider<DynamoKey, UserRecord> provider = new DynamoProviderImpl(db, UserRecord.class); -        provider.put(DynamoKey.newAttributeKey("accounts", user, "value"), record); - -        return offset; -    } - -    private static int importTable(String[] args, int offset) throws IOException { -        String table = args[offset++]; -        String filename = args[offset++]; - -        DynamoDatabase db = getDatabase(); - -        List<String> lines = Files.readAllLines(new File(filename).toPath(), -                StandardCharsets.UTF_8); - -        int count = 0; - -        String key = null; -        Map<String, String> attributes = new HashMap<>(); -        for (String line : lines) { -            if (line.length() == 0) { -                if (attributes.size() > 0) { -                    db.putKey(DynamoKey.newKey(table, key), attributes); -                    count++; - -                    if (count % 50 == 0) { -                        System.out.printf("Imported %d records into %s...\n", count, table); -                    } -                } -                key = null; -                attributes = new HashMap<>(); -                continue; -            } - -            if (key == null) { -                key = line; -                continue; -            } - -            int space = line.indexOf(' '); -            String attribute = line.substring(0, space); -            String value = line.substring(space + 1); - -            attributes.put(attribute, value); -        } - -        // Finish up the remaining attributes. -        if (key != null && attributes.size() > 0) { -            db.putKey(DynamoKey.newKey(table, key), attributes); -            count++; -        } - -        System.out.printf("Imported %d records into %s.\n", count, table); - -        return offset; -    } - -    private static void insertQuestions(File baseDir) throws IOException { -        DynamoDatabase db = getDatabase(); -        File questions = new File(baseDir, "questions"); - -        File[] files = questions.listFiles(JSON_FILTER); -        Arrays.sort(files); - -        for (File file : files) { -            String filename = file.getName(); -            String questionId = filename.substring(0, filename.lastIndexOf('.')); - -            byte[] encoded = Files.readAllBytes(file.toPath()); -            String value = new String(encoded, StandardCharsets.UTF_8); -            db.putAttribute(DynamoKey.newAttributeKey("strings", -                        "/questions/" + questionId, "value"), value); -            System.out.println("Inserted /questions/" + questionId); -        } - -        String filename = files[0].getName(); -        String first    = filename.substring(0, filename.lastIndexOf('.')); -        int    count    = files.length; -        String summary  = "{\"first\": \"" + first + "\", \"count\": " + count + "}"; -        db.putAttribute(DynamoKey.newAttributeKey("strings", "/questions", "value"), summary); -        System.out.println("Inserted /questions"); -    } - -    private static void insertVideos(File baseDir) throws IOException { -        DynamoDatabase db = getDatabase(); -        File videos = new File(baseDir, "videos"); - -        for (File topic : videos.listFiles()) { -            if (!topic.isDirectory()) { -                continue; -            } - -            String topicName = topic.getName(); - -            Map<String, String> attributes = new HashMap<>(); -            File[] files = topic.listFiles(JSON_FILTER); -            for (File file : files) { -                String filename = file.getName(); -                String videoId = filename.substring(0, filename.lastIndexOf('.')); - -                byte[] encoded = Files.readAllBytes(file.toPath()); -                String value = new String(encoded, StandardCharsets.UTF_8); - -                attributes.put(videoId, value); -                System.out.println("Found /training/" + topicName + ":" + videoId); -            } - -            db.putKey(DynamoKey.newKey("strings", -                        "/training/" + topicName), attributes); -            System.out.println("Inserted /training/" + topicName); -        } -    } - -    private static void insertDefaultPlaylist(File baseDir) throws IOException { -        DynamoDatabase db = getDatabase(); -        File file = new File(baseDir, "videos/playlist.json"); - -        byte[] encoded = Files.readAllBytes(file.toPath()); -        String value = new String(encoded, StandardCharsets.UTF_8); -        db.putAttribute(DynamoKey.newAttributeKey("strings", -                    "/training/defaultplaylist", "value"), value); -        System.out.println("Inserted /training/defaultplaylist"); -    } - -    private static class JsonFilter implements FilenameFilter { -        @Override -        public boolean accept(File dir, String name) { -            return name.endsWith(".json"); -        } -    } -} diff --git a/src/com/p4square/grow/backend/dynamo/DynamoCollectionProviderImpl.java b/src/com/p4square/grow/backend/dynamo/DynamoCollectionProviderImpl.java deleted file mode 100644 index b53e9f7..0000000 --- a/src/com/p4square/grow/backend/dynamo/DynamoCollectionProviderImpl.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.backend.dynamo; - -import java.io.IOException; - -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; - -import com.p4square.grow.provider.CollectionProvider; -import com.p4square.grow.provider.JsonEncodedProvider; - -/** - *  - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class DynamoCollectionProviderImpl<V> implements CollectionProvider<String, String, V> { -    private final DynamoDatabase mDb; -    private final String mTable; -    private final Class<V> mClazz; - -    public DynamoCollectionProviderImpl(DynamoDatabase db, String table, Class<V> clazz) { -        mDb = db; -        mTable = table; -        mClazz = clazz; -    } - -    @Override -    public V get(String collection, String key) throws IOException { -        String blob = mDb.getAttribute(DynamoKey.newAttributeKey(mTable, collection, key)); -        return decode(blob); -    } - -    @Override -    public Map<String, V> query(String collection) throws IOException { -        return query(collection, -1); -    } - -    @Override -    public Map<String, V> query(String collection, int limit) throws IOException { -        Map<String, V> result = new LinkedHashMap<>(); - -        Map<String, String> row = mDb.getKey(DynamoKey.newKey(mTable, collection)); -        if (row.size() > 0) { -            int count = 0; -            for (Map.Entry<String, String> c : row.entrySet()) { -                if (limit >= 0 && ++count > limit) { -                    break; // Limit reached. -                } - -                String key = c.getKey(); -                String blob = c.getValue(); -                V obj = decode(blob); - -                result.put(key, obj); -            } -        } - -        return Collections.unmodifiableMap(result); -    } - -    @Override -    public void put(String collection, String key, V obj) throws IOException { -        if (obj == null) { -            mDb.deleteAttribute(DynamoKey.newAttributeKey(mTable, collection, key)); -        } else { -            String blob = encode(obj); -            mDb.putAttribute(DynamoKey.newAttributeKey(mTable, collection, key), blob); -        } -    } - -    /** -     * Encode the object as JSON. -     * -     * @param obj The object to encode. -     * @return The JSON encoding of obj. -     * @throws IOException if the object cannot be encoded. -     */ -    protected String encode(V obj) throws IOException { -        if (mClazz == String.class) { -            return (String) obj; -        } else { -            return JsonEncodedProvider.MAPPER.writeValueAsString(obj); -        } -    } - -    /** -     * Decode the JSON string as an object. -     * -     * @param blob The JSON data to decode. -     * @return The decoded object or null if blob is null. -     * @throws IOException If an object cannot be decoded. -     */ -    protected V decode(String blob) throws IOException { -        if (blob == null) { -            return null; -        } - -        if (mClazz == String.class) { -            return (V) blob; -        } - -        V obj = JsonEncodedProvider.MAPPER.readValue(blob, mClazz); -        return obj; -    } -} diff --git a/src/com/p4square/grow/backend/dynamo/DynamoDatabase.java b/src/com/p4square/grow/backend/dynamo/DynamoDatabase.java deleted file mode 100644 index 68a165d..0000000 --- a/src/com/p4square/grow/backend/dynamo/DynamoDatabase.java +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.backend.dynamo; - -import java.util.Arrays; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; - -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; -import com.amazonaws.regions.Region; -import com.amazonaws.regions.Regions; -import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient; -import com.amazonaws.services.dynamodbv2.model.AttributeAction; -import com.amazonaws.services.dynamodbv2.model.AttributeDefinition; -import com.amazonaws.services.dynamodbv2.model.AttributeValue; -import com.amazonaws.services.dynamodbv2.model.AttributeValueUpdate; -import com.amazonaws.services.dynamodbv2.model.CreateTableRequest; -import com.amazonaws.services.dynamodbv2.model.CreateTableResult; -import com.amazonaws.services.dynamodbv2.model.DeleteItemRequest; -import com.amazonaws.services.dynamodbv2.model.DeleteItemResult; -import com.amazonaws.services.dynamodbv2.model.DeleteTableRequest; -import com.amazonaws.services.dynamodbv2.model.DeleteTableResult; -import com.amazonaws.services.dynamodbv2.model.GetItemRequest; -import com.amazonaws.services.dynamodbv2.model.GetItemResult; -import com.amazonaws.services.dynamodbv2.model.KeySchemaElement; -import com.amazonaws.services.dynamodbv2.model.KeyType; -import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput; -import com.amazonaws.services.dynamodbv2.model.PutItemRequest; -import com.amazonaws.services.dynamodbv2.model.PutItemResult; -import com.amazonaws.services.dynamodbv2.model.ScanRequest; -import com.amazonaws.services.dynamodbv2.model.ScanResult; -import com.amazonaws.services.dynamodbv2.model.UpdateItemRequest; -import com.amazonaws.services.dynamodbv2.model.UpdateItemResult; -import com.amazonaws.services.dynamodbv2.model.UpdateTableRequest; -import com.amazonaws.services.dynamodbv2.model.UpdateTableResult; - -import com.p4square.grow.config.Config; - -/** - * A wrapper around the Dynamo API. - */ -public class DynamoDatabase { -    private final AmazonDynamoDBClient mClient; -    private final String mTablePrefix; - -    public DynamoDatabase(final Config config) { -        AWSCredentials creds; - -        String awsAccessKey = config.getString("awsAccessKey"); -        if (awsAccessKey != null) { -            creds = new AWSCredentials() { -                @Override -                public String getAWSAccessKeyId() { -                    return config.getString("awsAccessKey"); -                } -                @Override -                public String getAWSSecretKey() { -                    return config.getString("awsSecretKey"); -                } -            }; -        } else { -            creds = new DefaultAWSCredentialsProviderChain().getCredentials(); -        } - -        mClient = new AmazonDynamoDBClient(creds); - -        String endpoint = config.getString("dynamoEndpoint"); -        if (endpoint != null) { -            mClient.setEndpoint(endpoint); -        } - -        String region = config.getString("awsRegion"); -        if (region != null) { -            mClient.setRegion(Region.getRegion(Regions.fromName(region))); -        } - -        mTablePrefix = config.getString("dynamoTablePrefix", ""); -    } - -    public void createTable(String name, long reads, long writes) { -        ArrayList<AttributeDefinition> attributeDefinitions = new ArrayList<>(); -        attributeDefinitions.add(new AttributeDefinition() -                .withAttributeName("id") -                .withAttributeType("S")); - -        ArrayList<KeySchemaElement> ks = new ArrayList<>(); -        ks.add(new KeySchemaElement().withAttributeName("id").withKeyType(KeyType.HASH)); - -        ProvisionedThroughput provisionedThroughput = new ProvisionedThroughput() -            .withReadCapacityUnits(reads) -            .withWriteCapacityUnits(writes); - -        CreateTableRequest request = new CreateTableRequest() -            .withTableName(mTablePrefix + name) -            .withAttributeDefinitions(attributeDefinitions) -            .withKeySchema(ks) -            .withProvisionedThroughput(provisionedThroughput); - -        CreateTableResult result = mClient.createTable(request); -    } - -    public void updateTable(String name, long reads, long writes) { -        ProvisionedThroughput provisionedThroughput = new ProvisionedThroughput() -            .withReadCapacityUnits(reads) -            .withWriteCapacityUnits(writes); - -        UpdateTableRequest request = new UpdateTableRequest() -            .withTableName(mTablePrefix + name) -            .withProvisionedThroughput(provisionedThroughput); - -        UpdateTableResult result = mClient.updateTable(request); -    } - -    public void deleteTable(String name) { -        DeleteTableRequest deleteTableRequest = new DeleteTableRequest() -            .withTableName(mTablePrefix + name); - -        DeleteTableResult result = mClient.deleteTable(deleteTableRequest); -    } - -    /** -     * Get all rows from a table. -     * -     * The key parameter must specify a table. If hash/range key is specified, -     * the scan will begin after that key. -     * -     * @param key Previous key to start with. -     * @return An ordered map of all results. -     */ -    public Map<DynamoKey, Map<String, String>> getAll(final DynamoKey key) { -        ScanRequest scanRequest = new ScanRequest().withTableName(mTablePrefix + key.getTable()); - -        if (key.getHashKey() != null) { -            scanRequest.setExclusiveStartKey(generateKey(key)); -        } - -        ScanResult scanResult = mClient.scan(scanRequest); - -        Map<DynamoKey, Map<String, String>> result = new LinkedHashMap<>(); -        for (Map<String, AttributeValue> map : scanResult.getItems()) { -            String id = null; -            String range = null; -            Map<String, String> row = new LinkedHashMap<>(); -            for (Map.Entry<String, AttributeValue> entry : map.entrySet()) { -                if ("id".equals(entry.getKey())) { -                    id = entry.getValue().getS(); -                } else if ("range".equals(entry.getKey())) { -                    range = entry.getValue().getS(); -                } else { -                    row.put(entry.getKey(), entry.getValue().getS()); -                } -            } -            result.put(DynamoKey.newRangeKey(key.getTable(), id, range), row); -        } - -        return result; -    } - -    public Map<String, String> getKey(final DynamoKey key) { -        GetItemRequest getItemRequest = new GetItemRequest() -            .withTableName(mTablePrefix + key.getTable()) -            .withKey(generateKey(key)); - -        GetItemResult getItemResult = mClient.getItem(getItemRequest); -        Map<String, AttributeValue> map = getItemResult.getItem(); - -        Map<String, String> result = new LinkedHashMap<>(); -        if (map != null) { -            for (Map.Entry<String, AttributeValue> entry : map.entrySet()) { -                if (!"id".equals(entry.getKey())) { -                    result.put(entry.getKey(), entry.getValue().getS()); -                } -            } -        } - -        return result; -    } - -    public String getAttribute(final DynamoKey key) { -        checkAttributeKey(key); - -        GetItemRequest getItemRequest = new GetItemRequest() -            .withTableName(mTablePrefix + key.getTable()) -            .withKey(generateKey(key)) -            .withAttributesToGet(key.getAttribute()); - -        GetItemResult result = mClient.getItem(getItemRequest); -        Map<String, AttributeValue> map = result.getItem(); - -        if (map == null) { -            return null; -        } - -        AttributeValue value = map.get(key.getAttribute()); -        if (value != null) { -            return value.getS(); - -        } else { -            return null; -        } -    } - -    /** -     * Set all attributes for the given key. -     * -     * @param key The key. -     * @param values Map of attributes to values. -     */ -    public void putKey(final DynamoKey key, final Map<String, String> values) { -        Map<String, AttributeValue> item = new HashMap<>(); -        for (Map.Entry<String, String> entry : values.entrySet()) { -            item.put(entry.getKey(), new AttributeValue().withS(entry.getValue())); -        } - -        // Set the Key -        item.putAll(generateKey(key)); - -        PutItemRequest putItemRequest = new PutItemRequest() -            .withTableName(mTablePrefix + key.getTable()) -            .withItem(item); - -        PutItemResult result = mClient.putItem(putItemRequest); -    } - -    /** -     * Set the particular attributes of the given key. -     * -     * @param key The key. -     * @param value The new value. -     */ -    public void putAttribute(final DynamoKey key, final String value) { -        checkAttributeKey(key); - -        Map<String, AttributeValueUpdate> updateItem = new HashMap<>(); -        updateItem.put(key.getAttribute(), -                new AttributeValueUpdate() -                .withAction(AttributeAction.PUT) -                .withValue(new AttributeValue().withS(value))); - -        UpdateItemRequest updateItemRequest = new UpdateItemRequest() -            .withTableName(mTablePrefix + key.getTable()) -            .withKey(generateKey(key)) -            .withAttributeUpdates(updateItem); -        // TODO: Check conditions. - -        UpdateItemResult result = mClient.updateItem(updateItemRequest); -    } - -    /** -     * Delete the given key. -     * -     * @param key The key. -     */ -    public void deleteKey(final DynamoKey key) { -        DeleteItemRequest deleteItemRequest = new DeleteItemRequest() -            .withTableName(mTablePrefix + key.getTable()) -            .withKey(generateKey(key)); - -        DeleteItemResult result = mClient.deleteItem(deleteItemRequest); -    } - -    /** -     * Delete an attribute from the given key. -     * -     * @param key The key. -     */ -    public void deleteAttribute(final DynamoKey key) { -        checkAttributeKey(key); - -        Map<String, AttributeValueUpdate> updateItem = new HashMap<>(); -        updateItem.put(key.getAttribute(), -                new AttributeValueUpdate().withAction(AttributeAction.DELETE)); - -        UpdateItemRequest updateItemRequest = new UpdateItemRequest() -            .withTableName(mTablePrefix + key.getTable()) -            .withKey(generateKey(key)) -            .withAttributeUpdates(updateItem); - -        UpdateItemResult result = mClient.updateItem(updateItemRequest); -    } - -    /** -     * Generate a DynamoDB Key Map from the DynamoKey. -     */ -    private Map<String, AttributeValue> generateKey(final DynamoKey key) { -        HashMap<String, AttributeValue> keyMap = new HashMap<>(); -        keyMap.put("id", new AttributeValue().withS(key.getHashKey())); - -        String range = key.getRangeKey(); -        if (range != null) { -            keyMap.put("range", new AttributeValue().withS(range)); -        } - -        return keyMap; -    } - -    private void checkAttributeKey(DynamoKey key) { -        if (null == key.getAttribute()) { -            throw new IllegalArgumentException("Attribute must be non-null"); -        } -    } -} diff --git a/src/com/p4square/grow/backend/dynamo/DynamoKey.java b/src/com/p4square/grow/backend/dynamo/DynamoKey.java deleted file mode 100644 index 5cdbacd..0000000 --- a/src/com/p4square/grow/backend/dynamo/DynamoKey.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.backend.dynamo; - -/** - * DynamoKey represents a table, hash key, and range key tupl. - */ -public class DynamoKey { -    private final String mTable; -    private final String mHashKey; -    private final String mRangeKey; -    private final String mAttribute; - -    public static DynamoKey newKey(final String table, final String hashKey) { -        return new DynamoKey(table, hashKey, null, null); -    } - -    public static DynamoKey newRangeKey(final String table, final String hashKey, -            final String rangeKey) { - -        return new DynamoKey(table, hashKey, rangeKey, null); -    } - -    public static DynamoKey newAttributeKey(final String table, final String hashKey, -            final String attribute) { - -        return new DynamoKey(table, hashKey, null, attribute); -    } - -    public DynamoKey(final String table, final String hashKey, final String rangeKey, -            final String attribute) { - -        mTable = table; -        mHashKey = hashKey; -        mRangeKey = rangeKey; -        mAttribute = attribute; -    } - -    public String getTable() { -        return mTable; -    } - -    public String getHashKey() { -        return mHashKey; -    } - -    public String getRangeKey() { -        return mRangeKey; -    } - -    public String getAttribute() { -        return mAttribute; -    } -} diff --git a/src/com/p4square/grow/backend/dynamo/DynamoProviderImpl.java b/src/com/p4square/grow/backend/dynamo/DynamoProviderImpl.java deleted file mode 100644 index 93a535f..0000000 --- a/src/com/p4square/grow/backend/dynamo/DynamoProviderImpl.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.dynamo; - -import java.io.IOException; - -import com.p4square.grow.provider.Provider; -import com.p4square.grow.provider.JsonEncodedProvider; - -/** - * Provider implementation backed by a DynamoDB Table. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class DynamoProviderImpl<V> extends JsonEncodedProvider<V> implements Provider<DynamoKey, V> { -    private final DynamoDatabase mDb; - -    public DynamoProviderImpl(DynamoDatabase db, Class<V> clazz) { -        super(clazz); - -        mDb = db; -    } - -    @Override -    public V get(DynamoKey key) throws IOException { -        String blob = mDb.getAttribute(key); -        return decode(blob); -    } - -    @Override -    public void put(DynamoKey key, V obj) throws IOException { -        String blob = encode(obj); -        mDb.putAttribute(key, blob); -    } -} diff --git a/src/com/p4square/grow/backend/feed/FeedDataProvider.java b/src/com/p4square/grow/backend/feed/FeedDataProvider.java deleted file mode 100644 index 6f090c0..0000000 --- a/src/com/p4square/grow/backend/feed/FeedDataProvider.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.feed; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; - -import com.p4square.grow.model.MessageThread; -import com.p4square.grow.model.Message; -import com.p4square.grow.provider.CollectionProvider; - -/** - * Implementing this interface indicates you can provide a data source for the Feed. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public interface FeedDataProvider { -    public static final Collection<String> TOPICS = Collections.unmodifiableCollection( -            Arrays.asList(new String[] { "seeker", "believer", "disciple", "teacher", "leader" })); - -    /** -     * @return a CollectionProvider of Threads. -     */ -    CollectionProvider<String, String, MessageThread> getThreadProvider(); - -    /** -     * @return a CollectionProvider of Messages. -     */ -    CollectionProvider<String, String, Message> getMessageProvider(); -} diff --git a/src/com/p4square/grow/backend/feed/ThreadResource.java b/src/com/p4square/grow/backend/feed/ThreadResource.java deleted file mode 100644 index e8f46c2..0000000 --- a/src/com/p4square/grow/backend/feed/ThreadResource.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.feed; - -import java.io.IOException; - -import java.util.Date; -import java.util.Map; - -import org.restlet.data.Status; -import org.restlet.resource.ServerResource; -import org.restlet.representation.Representation; - -import org.restlet.ext.jackson.JacksonRepresentation; - -import org.apache.log4j.Logger; - -import com.p4square.grow.model.Message; - -/** - * ThreadResource manages the messages that make up a thread. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class ThreadResource extends ServerResource { -    private static final Logger LOG = Logger.getLogger(ThreadResource.class); - -    private FeedDataProvider mBackend; -    private String mTopic; -    private String mThreadId; - -    @Override -    public void doInit() { -        super.doInit(); - -        mBackend = (FeedDataProvider) getApplication(); -        mTopic = getAttribute("topic"); -        mThreadId = getAttribute("thread"); -    } - -    /** -     * GET a list of messages in a thread. -     */ -    @Override -    protected Representation get() { -        // If the topic or threadId are missing, return a 404. -        if (mTopic == null || mTopic.length() == 0 || -                mThreadId == null || mThreadId.length() == 0) { -            setStatus(Status.CLIENT_ERROR_NOT_FOUND); -            return null; -        } - -        // TODO: Support limit query parameter. - -        try { -            String collectionKey = mTopic + "/" + mThreadId; -            Map<String, Message> messages = mBackend.getMessageProvider().query(collectionKey); -            return new JacksonRepresentation(messages.values()); - -        } catch (IOException e) { -            LOG.error("Unexpected exception: " + e.getMessage(), e); -            setStatus(Status.SERVER_ERROR_INTERNAL); -            return null; -        } -    } - -    /** -     * POST a new message to the thread. -     */ -    @Override -    protected Representation post(Representation entity) { -        // If the topic and thread are not provided, respond with not allowed. -        // TODO: Check if the thread exists. -        if (mTopic == null || !mBackend.TOPICS.contains(mTopic) || -                mThreadId == null || mThreadId.length() == 0) { -            setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); -            return null; -        } - -        try { -            JacksonRepresentation<Message> jsonRep = new JacksonRepresentation<Message>(entity, Message.class); -            Message message = jsonRep.getObject(); - -            // Force the thread id and message to be what we expect. -            message.setThreadId(mThreadId); -            message.setId(Message.generateId()); - -            if (message.getCreated() == null) { -                message.setCreated(new Date()); -            } - -            String collectionKey = mTopic + "/" + mThreadId; -            mBackend.getMessageProvider().put(collectionKey, message.getId(), message); - -            setLocationRef(mThreadId + "/" + message.getId()); -            return new JacksonRepresentation(message); - -        } catch (IOException e) { -            LOG.error("Unexpected exception: " + e.getMessage(), e); -            setStatus(Status.SERVER_ERROR_INTERNAL); -            return null; -        } -    } -} diff --git a/src/com/p4square/grow/backend/feed/TopicResource.java b/src/com/p4square/grow/backend/feed/TopicResource.java deleted file mode 100644 index 24b6a92..0000000 --- a/src/com/p4square/grow/backend/feed/TopicResource.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.feed; - -import java.io.IOException; - -import java.util.Date; -import java.util.Map; - -import org.restlet.data.Status; -import org.restlet.resource.ServerResource; -import org.restlet.representation.Representation; - -import org.restlet.ext.jackson.JacksonRepresentation; - -import org.apache.log4j.Logger; - -import com.p4square.grow.model.Message; -import com.p4square.grow.model.MessageThread; - -/** - * TopicResource manages the threads contained in a topic. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class TopicResource extends ServerResource { -    private static final Logger LOG = Logger.getLogger(TopicResource.class); - -    private FeedDataProvider mBackend; -    private String mTopic; - -    @Override -    public void doInit() { -        super.doInit(); - -        mBackend = (FeedDataProvider) getApplication(); -        mTopic = getAttribute("topic"); -    } - -    /** -     * GET a list of threads in the topic. -     */ -    @Override -    protected Representation get() { -        // If no topic is provided, return a list of topics. -        if (mTopic == null || mTopic.length() == 0) { -            return new JacksonRepresentation(FeedDataProvider.TOPICS); -        } - -        // Parse limit query parameter. -        int limit = -1; -        String limitString = getQueryValue("limit"); -        if (limitString != null) { -           try { -               limit = Integer.parseInt(limitString); -           } catch (NumberFormatException e) { -               setStatus(Status.CLIENT_ERROR_BAD_REQUEST); -               return null; -           } -        } - -        try { -            Map<String, MessageThread> threads = mBackend.getThreadProvider().query(mTopic, limit); -            return new JacksonRepresentation(threads.values()); - -        } catch (IOException e) { -            LOG.error("Unexpected exception: " + e.getMessage(), e); -            setStatus(Status.SERVER_ERROR_INTERNAL); -            return null; -        } -    } - -    /** -     * POST a new thread to the topic. -     */ -    @Override -    protected Representation post(Representation entity) { -        // If no topic is provided, respond with not allowed. -        if (mTopic == null || !mBackend.TOPICS.contains(mTopic)) { -            setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); -            return null; -        } - -        try { -            // Deserialize the incoming message. -            JacksonRepresentation<MessageThread> jsonRep = -                new JacksonRepresentation<MessageThread>(entity, MessageThread.class); - -            // Get the message from the request. -            // Throw away the wrapping MessageThread because we'll create our own later. -            Message message = jsonRep.getObject().getMessage(); -            if (message.getCreated() == null) { -                message.setCreated(new Date()); -            } - -            // Create the new thread. -            MessageThread newThread = MessageThread.createNew(); - -            // Force the thread id and message to be what we expect. -            message.setId(Message.generateId()); -            message.setThreadId(newThread.getId()); -            newThread.setMessage(message); - -            mBackend.getThreadProvider().put(mTopic, newThread.getId(), newThread); - -            setLocationRef(mTopic + "/" + newThread.getId()); -            return new JacksonRepresentation(newThread); - -        } catch (IOException e) { -            LOG.error("Unexpected exception: " + e.getMessage(), e); -            setStatus(Status.SERVER_ERROR_INTERNAL); -            return null; -        } -    } -} diff --git a/src/com/p4square/grow/backend/resources/AccountResource.java b/src/com/p4square/grow/backend/resources/AccountResource.java deleted file mode 100644 index 2ac7061..0000000 --- a/src/com/p4square/grow/backend/resources/AccountResource.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.resources; - -import java.io.IOException; - -import org.restlet.data.Status; -import org.restlet.resource.ServerResource; -import org.restlet.representation.Representation; - -import org.restlet.ext.jackson.JacksonRepresentation; - -import org.apache.log4j.Logger; - -import com.p4square.grow.model.UserRecord; -import com.p4square.grow.provider.Provider; -import com.p4square.grow.provider.ProvidesUserRecords; -import com.p4square.grow.provider.JsonEncodedProvider; - -/** - * Stores a document about a user. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class AccountResource extends ServerResource { -    private static final Logger LOG = Logger.getLogger(AccountResource.class); - -    private Provider<String, UserRecord> mUserRecordProvider; - -    private String mUserId; - -    @Override -    public void doInit() { -        super.doInit(); - -        final ProvidesUserRecords backend = (ProvidesUserRecords) getApplication(); -        mUserRecordProvider = backend.getUserRecordProvider(); - -        mUserId = getAttribute("userId"); -    } - -    /** -     * Handle GET Requests. -     */ -    @Override -    protected Representation get() { -        try { -            UserRecord result = mUserRecordProvider.get(mUserId); - -            if (result == null) { -                setStatus(Status.CLIENT_ERROR_NOT_FOUND); -                return null; -            } - -            JacksonRepresentation<UserRecord> rep = new JacksonRepresentation<UserRecord>(result); -            rep.setObjectMapper(JsonEncodedProvider.MAPPER); -            return rep; - -        } catch (IOException e) { -            setStatus(Status.SERVER_ERROR_INTERNAL); -            return null; -        } -    } - -    /** -     * Handle PUT requests -     */ -    @Override -    protected Representation put(Representation entity) { -        try { -            JacksonRepresentation<UserRecord> representation = -                new JacksonRepresentation<>(entity, UserRecord.class); -            representation.setObjectMapper(JsonEncodedProvider.MAPPER); -            UserRecord record = representation.getObject(); - -            mUserRecordProvider.put(mUserId, record); -            setStatus(Status.SUCCESS_NO_CONTENT); - -        } catch (IOException e) { -            setStatus(Status.SERVER_ERROR_INTERNAL); -        } - -        return null; -    } -} diff --git a/src/com/p4square/grow/backend/resources/BannerResource.java b/src/com/p4square/grow/backend/resources/BannerResource.java deleted file mode 100644 index 2b9c8e6..0000000 --- a/src/com/p4square/grow/backend/resources/BannerResource.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.resources; - -import java.io.IOException; - -import org.restlet.data.Status; -import org.restlet.ext.jackson.JacksonRepresentation; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; -import org.restlet.resource.ServerResource; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.apache.log4j.Logger; - -import com.p4square.grow.backend.GrowBackend; -import com.p4square.grow.model.Banner; -import com.p4square.grow.provider.JsonEncodedProvider; -import com.p4square.grow.provider.Provider; - -/** - * Fetches or sets the banner string. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class BannerResource extends ServerResource { -    private static final Logger LOG = Logger.getLogger(BannerResource.class); - -    public static final ObjectMapper MAPPER = JsonEncodedProvider.MAPPER; - -    private Provider<String, String> mStringProvider; - -    @Override -    public void doInit() { -        super.doInit(); - -        final GrowBackend backend = (GrowBackend) getApplication(); -        mStringProvider = backend.getStringProvider(); -    } - -    /** -     * Handle GET Requests. -     */ -    @Override -    protected Representation get() { -        String result = null; -        try { -            result = mStringProvider.get("banner"); - -        } catch (IOException e) { -            LOG.warn("Exception loading banner: " + e); -        } - -        if (result == null || result.length() == 0) { -            result = "{\"html\":null}"; -        } - -        return new StringRepresentation(result); -    } - -    /** -     * Handle PUT requests -     */ -    @Override -    protected Representation put(Representation entity) { -        try { -            JacksonRepresentation<Banner> representation = -                new JacksonRepresentation<>(entity, Banner.class); -            representation.setObjectMapper(MAPPER); - -            Banner banner = representation.getObject(); - -            mStringProvider.put("banner", MAPPER.writeValueAsString(banner)); -            setStatus(Status.SUCCESS_NO_CONTENT); - -        } catch (IOException e) { -            setStatus(Status.SERVER_ERROR_INTERNAL); -        } - -        return null; -    } -} diff --git a/src/com/p4square/grow/backend/resources/SurveyResource.java b/src/com/p4square/grow/backend/resources/SurveyResource.java deleted file mode 100644 index 8723ee2..0000000 --- a/src/com/p4square/grow/backend/resources/SurveyResource.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.resources; - -import java.io.IOException; - -import java.util.Map; -import java.util.HashMap; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.ext.jackson.JacksonRepresentation; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; -import org.restlet.resource.ServerResource; - -import org.apache.log4j.Logger; - -import com.p4square.grow.backend.GrowBackend; -import com.p4square.grow.model.Question; -import com.p4square.grow.provider.JsonEncodedProvider; -import com.p4square.grow.provider.Provider; - -/** - * This resource manages assessment questions. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class SurveyResource extends ServerResource { -    private static final Logger LOG = Logger.getLogger(SurveyResource.class); - -    private static final ObjectMapper MAPPER = JsonEncodedProvider.MAPPER; - -    private Provider<String, Question> mQuestionProvider; -    private Provider<String, String> mStringProvider; - -    private String mQuestionId; - -    @Override -    public void doInit() { -        super.doInit(); - -        final GrowBackend backend = (GrowBackend) getApplication(); -        mQuestionProvider = backend.getQuestionProvider(); -        mStringProvider = backend.getStringProvider(); - -        mQuestionId = getAttribute("questionId"); -    } - -    /** -     * Handle GET Requests. -     */ -    @Override -    protected Representation get() { -        String result = "{}"; - -        if (mQuestionId == null) { -            // TODO: List all question ids - -        } else if (mQuestionId.equals("first")) { -            // Get the first question id from db? -            Map<?, ?> questionSummary = getQuestionsSummary(); -            mQuestionId = (String) questionSummary.get("first"); - -        } else if (mQuestionId.equals("count")) { -            // Get the first question id from db? -            Map<?, ?> questionSummary = getQuestionsSummary(); - -            return new StringRepresentation("{\"count\":" + -                    String.valueOf((Integer) questionSummary.get("count")) + "}"); -        } - -        if (mQuestionId != null) { -            // Get a question by id -            Question question = null; -            try { -                question = mQuestionProvider.get(mQuestionId); -            } catch (IOException e) { -                LOG.error("IOException loading question: " + e); -            } - -            if (question == null) { -                // 404 -                setStatus(Status.CLIENT_ERROR_NOT_FOUND); -                return null; -            } - -            JacksonRepresentation<Question> rep = new JacksonRepresentation<>(question); -            rep.setObjectMapper(MAPPER); -            return rep; -        } - -        return new StringRepresentation(result); -    } - -    private Map<?, ?> getQuestionsSummary() { -        try { -            // TODO: This could be better. Quick fix for provider support. -            String json = mStringProvider.get("/questions"); - -            if (json != null) { -                return MAPPER.readValue(json, Map.class); -            } - -        } catch (IOException e) { -            LOG.info("Exception reading questions summary.", e); -        } - -        return null; -    } -} diff --git a/src/com/p4square/grow/backend/resources/SurveyResultsResource.java b/src/com/p4square/grow/backend/resources/SurveyResultsResource.java deleted file mode 100644 index 7c15cfd..0000000 --- a/src/com/p4square/grow/backend/resources/SurveyResultsResource.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.resources; - -import java.io.IOException; -import java.util.Map; -import java.util.HashMap; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; -import org.restlet.resource.ServerResource; - -import org.apache.log4j.Logger; - -import com.p4square.grow.backend.GrowBackend; -import com.p4square.grow.model.Answer; -import com.p4square.grow.model.Question; -import com.p4square.grow.model.RecordedAnswer; -import com.p4square.grow.model.Score; -import com.p4square.grow.model.UserRecord; -import com.p4square.grow.provider.CollectionProvider; -import com.p4square.grow.provider.Provider; - - -/** - * Store the user's answers to the assessment and generate their score. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class SurveyResultsResource extends ServerResource { -    private static final Logger LOG = Logger.getLogger(SurveyResultsResource.class); - -    private static final ObjectMapper MAPPER = new ObjectMapper(); - -    static enum RequestType { -        ASSESSMENT, ANSWER -    } - -    private CollectionProvider<String, String, String> mAnswerProvider; -    private Provider<String, Question> mQuestionProvider; -    private Provider<String, UserRecord> mUserRecordProvider; - -    private RequestType mRequestType; -    private String mUserId; -    private String mQuestionId; - -    @Override -    public void doInit() { -        super.doInit(); - -        final GrowBackend backend = (GrowBackend) getApplication(); -        mAnswerProvider = backend.getAnswerProvider(); -        mQuestionProvider = backend.getQuestionProvider(); -        mUserRecordProvider = backend.getUserRecordProvider(); - -        mUserId = getAttribute("userId"); -        mQuestionId = getAttribute("questionId"); - -        mRequestType = RequestType.ASSESSMENT; -        if (mQuestionId != null) { -            mRequestType = RequestType.ANSWER; -        } -    } - -    /** -     * Handle GET Requests. -     */ -    @Override -    protected Representation get() { -        try { -            String result = null; - -            switch (mRequestType) { -                case ANSWER: -                    result = mAnswerProvider.get(mUserId, mQuestionId); -                    break; - -                case ASSESSMENT: -                    result = mAnswerProvider.get(mUserId, "summary"); -                    if (result == null || result.length() == 0) { -                        result = buildAssessment(); -                    } -                    break; -            } - -            if (result == null) { -                setStatus(Status.CLIENT_ERROR_NOT_FOUND); -                return null; -            } - -            return new StringRepresentation(result); -        } catch (IOException e) { -            LOG.error("IOException getting answer: ", e); -            setStatus(Status.SERVER_ERROR_INTERNAL); -            return null; -        } -    } - -    /** -     * Handle PUT requests -     */ -    @Override -    protected Representation put(Representation entity) { -        boolean success = false; - -        switch (mRequestType) { -            case ANSWER: -                try { -                    mAnswerProvider.put(mUserId, mQuestionId, entity.getText()); -                    mAnswerProvider.put(mUserId, "lastAnswered", mQuestionId); -                    mAnswerProvider.put(mUserId, "summary", null); -                    success = true; - -                } catch (Exception e) { -                    LOG.warn("Caught exception putting answer: " + e.getMessage(), e); -                } -                break; - -            default: -                setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); -                return null; -        } - -        if (success) { -            setStatus(Status.SUCCESS_NO_CONTENT); - -        } else { -            setStatus(Status.SERVER_ERROR_INTERNAL); -        } - -        return null; -    } - -    /** -     * Clear assessment results. -     */ -    @Override -    protected Representation delete() { -        boolean success = false; - -        switch (mRequestType) { -            case ANSWER: -                try { -                    mAnswerProvider.put(mUserId, mQuestionId, null); -                    mAnswerProvider.put(mUserId, "summary", null); -                    success = true; - -                } catch (Exception e) { -                    LOG.warn("Caught exception putting answer: " + e.getMessage(), e); -                } -                break; - -            case ASSESSMENT: -                try { -                    mAnswerProvider.put(mUserId, "summary", null); -                    mAnswerProvider.put(mUserId, "lastAnswered", null); -                    // TODO Delete answers - -                    UserRecord record = mUserRecordProvider.get(mUserId); -                    if (record != null) { -                        record.setLanding("assessment"); -                        mUserRecordProvider.put(mUserId, record); -                    } - -                    success = true; - -                } catch (Exception e) { -                    LOG.warn("Caught exception putting answer: " + e.getMessage(), e); -                } -                break; - -            default: -                setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); -                return null; -        } - -        if (success) { -            setStatus(Status.SUCCESS_NO_CONTENT); - -        } else { -            setStatus(Status.SERVER_ERROR_INTERNAL); -        } - -        return null; - -    } - -    /** -     * This method compiles assessment results. -     */ -    private String buildAssessment() throws IOException { -        StringBuilder sb = new StringBuilder("{ "); - -        // Last question answered -        final String lastAnswered = mAnswerProvider.get(mUserId, "lastAnswered"); -        if (lastAnswered != null && lastAnswered.length() > 0) { -            sb.append("\"lastAnswered\": \"" + lastAnswered + "\", "); -        } - -        // Compute score -        Map<String, String> row = mAnswerProvider.query(mUserId); -        if (row.size() > 0) { -            Score score = new Score(); -            boolean scoringDone = false; -            int totalAnswers = 0; -            for (Map.Entry<String, String> c : row.entrySet()) { -                if (c.getKey().equals("lastAnswered") || c.getKey().equals("summary")) { -                    continue; -                } - -                try { -                    Question question = mQuestionProvider.get(c.getKey()); -                    RecordedAnswer userAnswer = MAPPER.readValue(c.getValue(), RecordedAnswer.class); - -                    if (question == null) { -                        LOG.warn("Answer for unknown question: " + c.getKey()); -                        continue; -                    } - -                    LOG.debug("Scoring questionId: " + c.getKey()); -                    scoringDone = !question.scoreAnswer(score, userAnswer); - -                } catch (Exception e) { -                    LOG.error("Failed to score question: {userid: \"" + mUserId + -                            "\", questionid:\"" + c.getKey() + -                            "\", userAnswer:\"" + c.getValue() + "\"}", e); -                } - -                totalAnswers++; -            } - -            sb.append("\"score\":" + score.getScore()); -            sb.append(", \"sum\":" + score.getSum()); -            sb.append(", \"count\":" + score.getCount()); -            sb.append(", \"totalAnswers\":" + totalAnswers); -            sb.append(", \"result\":\"" + score.toString() + "\""); -        } - -        sb.append(" }"); -        String summary = sb.toString(); - -        // Persist summary -        mAnswerProvider.put(mUserId, "summary", summary); - -        return summary; -    } -} diff --git a/src/com/p4square/grow/backend/resources/TrainingRecordResource.java b/src/com/p4square/grow/backend/resources/TrainingRecordResource.java deleted file mode 100644 index 51ba56a..0000000 --- a/src/com/p4square/grow/backend/resources/TrainingRecordResource.java +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.resources; - -import java.io.IOException; - -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.HashMap; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.resource.ServerResource; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; - -import org.restlet.ext.jackson.JacksonRepresentation; - -import org.apache.log4j.Logger; - -import com.p4square.grow.backend.GrowBackend; - -import com.p4square.grow.model.Chapter; -import com.p4square.grow.model.Playlist; -import com.p4square.grow.model.VideoRecord; -import com.p4square.grow.model.TrainingRecord; - -import com.p4square.grow.provider.CollectionProvider; -import com.p4square.grow.provider.JsonEncodedProvider; -import com.p4square.grow.provider.Provider; -import com.p4square.grow.provider.ProvidesAssessments; -import com.p4square.grow.provider.ProvidesTrainingRecords; - -import com.p4square.grow.model.Score; - -/** - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class TrainingRecordResource extends ServerResource { -    private static final Logger LOG = Logger.getLogger(TrainingRecordResource.class); -    private static final ObjectMapper MAPPER = JsonEncodedProvider.MAPPER; - -    static enum RequestType { -        SUMMARY, VIDEO -    } - -    private Provider<String, TrainingRecord> mTrainingRecordProvider; -    private CollectionProvider<String, String, String> mAnswerProvider; - -    private RequestType mRequestType; -    private String mUserId; -    private String mVideoId; -    private TrainingRecord mRecord; - -    @Override -    public void doInit() { -        super.doInit(); - -        mTrainingRecordProvider = ((ProvidesTrainingRecords) getApplication()).getTrainingRecordProvider(); -        mAnswerProvider = ((ProvidesAssessments) getApplication()).getAnswerProvider(); - -        mUserId = getAttribute("userId"); -        mVideoId = getAttribute("videoId"); - -        try { -            Playlist defaultPlaylist = ((ProvidesTrainingRecords) getApplication()).getDefaultPlaylist(); - -            mRecord = mTrainingRecordProvider.get(mUserId); -            if (mRecord == null) { -                mRecord = new TrainingRecord(); -                mRecord.setPlaylist(defaultPlaylist); -                skipAssessedChapters(mUserId, mRecord); -            } else { -                // Merge the playlist with the most recent version. -                mRecord.getPlaylist().merge(defaultPlaylist); -            } - -        } catch (IOException e) { -            LOG.error("IOException loading TrainingRecord: " + e.getMessage(), e); -            mRecord = null; -        } - -        mRequestType = RequestType.SUMMARY; -        if (mVideoId != null) { -            mRequestType = RequestType.VIDEO; -        } -    } - -    /** -     * Handle GET Requests. -     */ -    @Override -    protected Representation get() { -        JacksonRepresentation<?> rep = null; - -        if (mRecord == null) { -            setStatus(Status.SERVER_ERROR_INTERNAL); -            return null; -        } - -        switch (mRequestType) { -            case VIDEO: -                VideoRecord video = mRecord.getPlaylist().find(mVideoId); -                if (video == null) { -                    break; // Fall through and return 404 -                } -                rep = new JacksonRepresentation<VideoRecord>(video); -                break; - -            case SUMMARY: -                rep = new JacksonRepresentation<TrainingRecord>(mRecord); -                break; -        } - -        if (rep == null) { -            setStatus(Status.CLIENT_ERROR_NOT_FOUND); -            return null; - -        } else { -            rep.setObjectMapper(JsonEncodedProvider.MAPPER); -            return rep; -        } -    } - -    /** -     * Handle PUT requests -     */ -    @Override -    protected Representation put(Representation entity) { -        if (mRecord == null) { -            setStatus(Status.SERVER_ERROR_INTERNAL); -            return null; -        } - -        switch (mRequestType) { -            case VIDEO: -                try { -                    JacksonRepresentation<VideoRecord> representation = -                        new JacksonRepresentation<>(entity, VideoRecord.class); -                    representation.setObjectMapper(JsonEncodedProvider.MAPPER); -                    VideoRecord update = representation.getObject(); -                    VideoRecord video = mRecord.getPlaylist().find(mVideoId); - -                    if (video == null) { -                        // TODO: Video isn't on their playlist... -                        LOG.warn("Skipping video completion for video missing from playlist."); - -                    } else if (update.getComplete() && !video.getComplete()) { -                        // Video was newly completed -                        video.complete(); -                        mRecord.setLastVideo(mVideoId); - -                        mTrainingRecordProvider.put(mUserId, mRecord); -                    } - -                    setStatus(Status.SUCCESS_NO_CONTENT); - -                } catch (Exception e) { -                    LOG.warn("Caught exception updating training record: " + e.getMessage(), e); -                    setStatus(Status.SERVER_ERROR_INTERNAL); -                } -                break; - -            default: -                setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); -        } - -        return null; -    } - -    private Score getAssessedScore(String userId) throws IOException { -        // Get the user's score. -        Score assessedScore = new Score(0, 0); - -        String summaryString = mAnswerProvider.get(userId, "summary"); -        if (summaryString == null) { -            throw new IOException("Asked to create training record for unassessed user " + userId); -        } - -        Map<?,?> summary = MAPPER.readValue(summaryString, Map.class); - -        if (summary.containsKey("sum") && summary.containsKey("count")) { -            double sum = (Double) summary.get("sum"); -            int count = (Integer) summary.get("count"); -            assessedScore = new Score(sum, count); -        } - -        return assessedScore; -    } - -    /** -     * Mark the chapters which the user assessed through as not required. -     */ -    private void skipAssessedChapters(String userId, TrainingRecord record) { -        // Get the user's score. -        Score assessedScore = new Score(0, 0); - -        try { -            assessedScore = getAssessedScore(userId); -        } catch (IOException e) { -            LOG.error("IOException fetching assessment record for " + userId, e); -            return; -        } - -        // Mark the correct videos as not required. -        Playlist playlist = record.getPlaylist(); - -        for (Map.Entry<String, Chapter> entry : playlist.getChaptersMap().entrySet()) { -            String chapterId = entry.getKey(); -            Chapter chapter = entry.getValue(); -            boolean required; - -            if ("introduction".equals(chapter)) { -                // Introduction chapter is always required -                required = true; - -            } else { -                // Chapter required if the floor of the score is <= the chapter's numeric value. -                required = assessedScore.floor() <= Score.numericScore(chapterId); -            } - -            if (!required) { -                for (VideoRecord video : chapter.getVideos().values()) { -                    video.setRequired(required); -                } -            } -        } -    } -} diff --git a/src/com/p4square/grow/backend/resources/TrainingResource.java b/src/com/p4square/grow/backend/resources/TrainingResource.java deleted file mode 100644 index 6efdfab..0000000 --- a/src/com/p4square/grow/backend/resources/TrainingResource.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.resources; - -import java.io.IOException; -import java.util.Map; - -import org.restlet.data.Status; -import org.restlet.resource.ServerResource; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; - -import org.apache.log4j.Logger; - -import com.p4square.grow.backend.GrowBackend; -import com.p4square.grow.backend.db.CassandraDatabase; - -import com.p4square.grow.provider.CollectionProvider; -/** - * This resource returns a listing of training items for a particular level. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class TrainingResource extends ServerResource { -    private final static Logger LOG = Logger.getLogger(TrainingResource.class); - -    private CollectionProvider<String, String, String> mVideoProvider; - -    private String mLevel; -    private String mVideoId; - -    @Override -    public void doInit() { -        super.doInit(); - -        GrowBackend backend = (GrowBackend) getApplication(); -        mVideoProvider = backend.getVideoProvider(); - -        mLevel = getAttribute("level"); -        mVideoId = getAttribute("videoId"); -    } - -    /** -     * Handle GET Requests. -     */ -    @Override -    protected Representation get() { -        String result = null; - -        if (mLevel == null) { -            setStatus(Status.CLIENT_ERROR_NOT_FOUND); -            return null; -        } - -        try { -            if (mVideoId == null) { -                // Get all videos -                // TODO: This could be improved, but this is the quickest way to get -                // providers working. -                Map<String, String> videos = mVideoProvider.query(mLevel); -                if (videos.size() > 0) { -                    StringBuilder sb = new StringBuilder("{ \"level\": \"" + mLevel + "\""); -                    sb.append(", \"videos\": ["); -                    boolean first = true; -                    for (String value : videos.values()) { -                        if (!first) { -                            sb.append(", "); -                        } -                        sb.append(value); -                        first = false; -                    } -                    sb.append("] }"); -                    result = sb.toString(); -                } - -            } else { -                // Get single video -                result = mVideoProvider.get(mLevel, mVideoId); -            } - -            if (result == null) { -                // 404 -                setStatus(Status.CLIENT_ERROR_NOT_FOUND); -                return null; -            } - -            return new StringRepresentation(result); - -        } catch (IOException e) { -            LOG.error("IOException fetch video: " + e.getMessage(), e); -            setStatus(Status.SERVER_ERROR_INTERNAL); -            return null; -        } -    } -} diff --git a/src/com/p4square/grow/ccb/CCBProgressReporter.java b/src/com/p4square/grow/ccb/CCBProgressReporter.java deleted file mode 100644 index d2826eb..0000000 --- a/src/com/p4square/grow/ccb/CCBProgressReporter.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.p4square.grow.ccb; - -import com.p4square.ccbapi.CCBAPI; -import com.p4square.ccbapi.model.*; -import com.p4square.grow.frontend.ProgressReporter; -import com.p4square.grow.model.Score; -import org.apache.log4j.Logger; -import org.restlet.security.User; - -import java.io.IOException; -import java.time.LocalDate; -import java.time.ZoneId; -import java.util.Date; - -/** - * A ProgressReporter which records progress in CCB. - * - * Except not really, because it's not implemented yet. - * This is just a placeholder until ccb-api-client-java has support for updating an individual. - */ -public class CCBProgressReporter implements ProgressReporter { - -    private static final Logger LOG = Logger.getLogger(CCBProgressReporter.class); - -    private static final String GROW_LEVEL = "GrowLevelTrain"; -    private static final String GROW_ASSESSMENT = "GrowLevelAsmnt"; - -    private final CCBAPI mAPI; -    private final CustomFieldCache mCache; - -    public CCBProgressReporter(final CCBAPI api, final CustomFieldCache cache) { -        mAPI = api; -        mCache = cache; -    } - -    @Override -    public void reportAssessmentComplete(final User user, final String level, final Date date, final String results) { -        if (!(user instanceof CCBUser)) { -            throw new IllegalArgumentException("Expected CCBUser but got " + user.getClass().getCanonicalName()); -        } -        final CCBUser ccbuser = (CCBUser) user; - -        updateLevelAndDate(ccbuser, GROW_ASSESSMENT, level, date); -    } - -    @Override -    public void reportChapterComplete(final User user, final String chapter, final Date date) { -        if (!(user instanceof CCBUser)) { -            throw new IllegalArgumentException("Expected CCBUser but got " + user.getClass().getCanonicalName()); -        } -        final CCBUser ccbuser = (CCBUser) user; - -        // Only update the level if it is increasing. -        final CustomPulldownFieldValue currentLevel = ccbuser.getProfile() -                .getCustomPulldownFields().getByLabel(GROW_LEVEL); - -        if (currentLevel != null) { -            if (Score.numericScore(chapter) <= Score.numericScore(currentLevel.getSelection().getLabel())) { -                LOG.info("Not updating level for " + user.getIdentifier() -                        + " because current level (" + currentLevel.getSelection().getLabel() -                        + ") is greater than new level (" + chapter + ")"); -                return; -            } -        } - -        updateLevelAndDate(ccbuser, GROW_LEVEL, chapter, date); -    } - -    private void updateLevelAndDate(final CCBUser user, final String field, final String level, final Date date) { -        boolean modified = false; - -        final UpdateIndividualProfileRequest req = new UpdateIndividualProfileRequest() -                .withIndividualId(user.getProfile().getId()); - -        final CustomField pulldownField = mCache.getIndividualPulldownByLabel(field); -        if (pulldownField != null) { -            final LookupTableType type = LookupTableType.valueOf(pulldownField.getName().toUpperCase()); -            final LookupTableItem item = mCache.getPulldownItemByName(type, level); -            if (item != null) { -                req.withCustomPulldownField(pulldownField.getName(), item.getId()); -                modified = true; -            } -        } - -        final CustomField dateField = mCache.getDateFieldByLabel(field); -        if (dateField != null) { -            req.withCustomDateField(dateField.getName(), date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate()); -            modified = true; -        } - -        try { -            // Only update if a field exists. -            if (modified) { -                mAPI.updateIndividualProfile(req); -            } - -        } catch (IOException e) { -            LOG.error("updateIndividual failed for " + user.getIdentifier() -                    + ", field " + field -                    + ", level " + level -                    + ", date " + date.toString()); -        } -    } -} diff --git a/src/com/p4square/grow/ccb/CCBUser.java b/src/com/p4square/grow/ccb/CCBUser.java deleted file mode 100644 index 7313172..0000000 --- a/src/com/p4square/grow/ccb/CCBUser.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.p4square.grow.ccb; - -import com.p4square.ccbapi.model.IndividualProfile; -import org.restlet.security.User; - -/** - * CCBUser is an adapter between a CCB IndividualProfile and a Restlet User. - * - * Note: CCBUser prefixes the user's identifier with "CCB-". This is done to - *       ensure the identifier does not collide with identifiers from other - *       systems. - */ -public class CCBUser extends User { - -    private final IndividualProfile mProfile; - -    /** -     * Wrap an IndividualProfile inside a User object. -     * -     * @param profile The CCB IndividualProfile for the user. -     */ -    public CCBUser(final IndividualProfile profile) { -        mProfile = profile; - -        setIdentifier("CCB-" + mProfile.getId()); -        setFirstName(mProfile.getFirstName()); -        setLastName(mProfile.getLastName()); -        setEmail(mProfile.getEmail()); -    } - -    /** -     * @return The IndividualProfile of the user. -     */ -    public IndividualProfile getProfile() { -        return mProfile; -    } -} diff --git a/src/com/p4square/grow/ccb/CCBUserVerifier.java b/src/com/p4square/grow/ccb/CCBUserVerifier.java deleted file mode 100644 index db10b75..0000000 --- a/src/com/p4square/grow/ccb/CCBUserVerifier.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.p4square.grow.ccb; - -import com.p4square.ccbapi.CCBAPI; -import com.p4square.ccbapi.model.GetIndividualProfilesRequest; -import com.p4square.ccbapi.model.GetIndividualProfilesResponse; -import org.apache.log4j.Logger; -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.security.Verifier; - -/** - * CCBUserVerifier authenticates a user through the CCB individual_profile_from_login_password API. - */ -public class CCBUserVerifier implements Verifier { -    private static final Logger LOG = Logger.getLogger(CCBUserVerifier.class); - -    private final CCBAPI mAPI; - -    public CCBUserVerifier(final CCBAPI api) { -        mAPI = api; -    } - -    @Override -    public int verify(Request request, Response response) { -        if (request.getChallengeResponse() == null) { -            return RESULT_MISSING; // no credentials -        } - -        final String username = request.getChallengeResponse().getIdentifier(); -        final char[] password = request.getChallengeResponse().getSecret(); - -        try { -            GetIndividualProfilesResponse resp = mAPI.getIndividualProfiles( -                    new GetIndividualProfilesRequest().withLoginPassword(username, password)); - -            if (resp.getIndividuals().size() == 1) { -                // Wrap the IndividualProfile up in an User and update the user on the request. -                final CCBUser user = new CCBUser(resp.getIndividuals().get(0)); -                LOG.info("Successfully authenticated " + user.getIdentifier()); -                request.getClientInfo().setUser(user); -                return RESULT_VALID; -            } - -        } catch (Exception e) { -            LOG.error("CCB API Exception: " + e, e); -        } - -        return RESULT_INVALID; // Invalid credentials -    } -} diff --git a/src/com/p4square/grow/ccb/ChurchCommunityBuilderIntegrationDriver.java b/src/com/p4square/grow/ccb/ChurchCommunityBuilderIntegrationDriver.java deleted file mode 100644 index fc6148f..0000000 --- a/src/com/p4square/grow/ccb/ChurchCommunityBuilderIntegrationDriver.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.p4square.grow.ccb; - -import com.codahale.metrics.MetricRegistry; -import com.p4square.ccbapi.CCBAPI; -import com.p4square.ccbapi.CCBAPIClient; -import com.p4square.grow.config.Config; -import com.p4square.grow.frontend.IntegrationDriver; -import com.p4square.grow.frontend.ProgressReporter; -import org.restlet.Context; -import org.restlet.security.Verifier; - -import java.net.URI; -import java.net.URISyntaxException; - -/** - * The ChurchCommunityBuilderIntegrationDriver is used to integrate Grow with Church Community Builder. - */ -public class ChurchCommunityBuilderIntegrationDriver implements IntegrationDriver { - -    private final Context mContext; -    private final MetricRegistry mMetricRegistry; -    private final Config mConfig; - -    private final CCBAPI mAPI; - -    private final CCBProgressReporter mProgressReporter; - -    public ChurchCommunityBuilderIntegrationDriver(final Context context) { -        mContext = context; -        mConfig = (Config) context.getAttributes().get("com.p4square.grow.config"); -        mMetricRegistry = (MetricRegistry) context.getAttributes().get("com.p4square.grow.metrics"); - -        try { -            CCBAPI api = new CCBAPIClient(new URI(mConfig.getString("CCBAPIURL", "")), -                                          mConfig.getString("CCBAPIUser", ""), -                                          mConfig.getString("CCBAPIPassword", "")); - -            if (mMetricRegistry != null) { -                api = new MonitoredCCBAPI(api, mMetricRegistry); -            } - -            mAPI = api; - -            final CustomFieldCache cache = new CustomFieldCache(mAPI); -            mProgressReporter = new CCBProgressReporter(mAPI, cache); - -        } catch (URISyntaxException e) { -            throw new RuntimeException(e); -        } -    } - -    @Override -    public Verifier newUserAuthenticationVerifier() { -        return new CCBUserVerifier(mAPI); -    } - -    @Override -    public ProgressReporter getProgressReporter() { -        return mProgressReporter; -    } -} diff --git a/src/com/p4square/grow/ccb/CustomFieldCache.java b/src/com/p4square/grow/ccb/CustomFieldCache.java deleted file mode 100644 index d93e6d9..0000000 --- a/src/com/p4square/grow/ccb/CustomFieldCache.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.p4square.grow.ccb; - -import com.p4square.ccbapi.CCBAPI; -import com.p4square.ccbapi.model.*; -import org.apache.log4j.Logger; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; - -/** - * CustomFieldCache maintains an index from custom field labels to names. - */ -public class CustomFieldCache { - -    private static final Logger LOG = Logger.getLogger(CustomFieldCache.class); - -    private final CCBAPI mAPI; - -    private CustomFieldCollection<CustomField> mTextFields; -    private CustomFieldCollection<CustomField> mDateFields; -    private CustomFieldCollection<CustomField> mIndividualPulldownFields; -    private CustomFieldCollection<CustomField> mGroupPulldownFields; - -    private final Map<LookupTableType, Map<String, LookupTableItem>> mItemByNameTable; - -    public CustomFieldCache(final CCBAPI api) { -        mAPI = api; -        mTextFields = new CustomFieldCollection<>(); -        mDateFields = new CustomFieldCollection<>(); -        mIndividualPulldownFields = new CustomFieldCollection<>(); -        mGroupPulldownFields = new CustomFieldCollection<>(); -        mItemByNameTable = new HashMap<>(); -    } - -    public CustomField getTextFieldByLabel(final String label) { -        if (mTextFields.size() == 0) { -            refresh(); -        } -        return mTextFields.getByLabel(label); -    } - -    public CustomField getDateFieldByLabel(final String label) { -        if (mDateFields.size() == 0) { -            refresh(); -        } -        return mDateFields.getByLabel(label); -    } - -    public CustomField getIndividualPulldownByLabel(final String label) { -        if (mIndividualPulldownFields.size() == 0) { -            refresh(); -        } -        return mIndividualPulldownFields.getByLabel(label); -    } - -    public CustomField getGroupPulldownByLabel(final String label) { -        if (mGroupPulldownFields.size() == 0) { -            refresh(); -        } -        return mGroupPulldownFields.getByLabel(label); -    } - -    public LookupTableItem getPulldownItemByName(final LookupTableType type, final String name) { -        Map<String, LookupTableItem> items = mItemByNameTable.get(type); -        if (items == null) { -            if (!cacheLookupTable(type)) { -                return null; -            } -            items = mItemByNameTable.get(type); -        } - -        return items.get(name.toLowerCase()); -    } - -    private synchronized void refresh() { -        try { -            // Get all of the custom fields. -            final GetCustomFieldLabelsResponse resp = mAPI.getCustomFieldLabels(); - -            final CustomFieldCollection<CustomField> newTextFields = new CustomFieldCollection<>(); -            final CustomFieldCollection<CustomField> newDateFields = new CustomFieldCollection<>(); -            final CustomFieldCollection<CustomField> newIndPulldownFields = new CustomFieldCollection<>(); -            final CustomFieldCollection<CustomField> newGrpPulldownFields = new CustomFieldCollection<>(); - -            for (final CustomField field : resp.getCustomFields()) { -                if (field.getName().startsWith("udf_ind_text_")) { -                    newTextFields.add(field); -                } else if (field.getName().startsWith("udf_ind_date_")) { -                    newDateFields.add(field); -                } else if (field.getName().startsWith("udf_ind_pulldown_")) { -                    newIndPulldownFields.add(field); -                } else if (field.getName().startsWith("udf_grp_pulldown_")) { -                    newGrpPulldownFields.add(field); -                } else { -                    LOG.warn("Unknown custom field type " + field.getName()); -                } -            } - -            this.mTextFields = newTextFields; -            this.mDateFields = newDateFields; -            this.mIndividualPulldownFields = newIndPulldownFields; -            this.mGroupPulldownFields = newGrpPulldownFields; - -        } catch (IOException e) { -            // Error fetching labels. -            LOG.error("Error fetching custom fields: " + e.getMessage(), e); -        } -    } - -    private synchronized boolean cacheLookupTable(final LookupTableType type) { -        try { -            final GetLookupTableResponse resp = mAPI.getLookupTable(new GetLookupTableRequest().withType(type)); -            mItemByNameTable.put(type, resp.getItems().stream().collect( -                    Collectors.toMap(item -> item.getName().toLowerCase(), Function.identity()))); -            return true; - -        } catch (IOException e) { -            LOG.error("Exception caching lookup table of type " + type, e); -        } - -        return false; -    } -} diff --git a/src/com/p4square/grow/ccb/MonitoredCCBAPI.java b/src/com/p4square/grow/ccb/MonitoredCCBAPI.java deleted file mode 100644 index 43b6433..0000000 --- a/src/com/p4square/grow/ccb/MonitoredCCBAPI.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.p4square.grow.ccb; - -import com.codahale.metrics.MetricRegistry; -import com.codahale.metrics.Timer; -import com.p4square.ccbapi.CCBAPI; -import com.p4square.ccbapi.model.*; - -import java.io.IOException; - -/** - * MonitoredCCBAPI is a CCBAPI decorator which records metrics for each API call. - */ -public class MonitoredCCBAPI implements CCBAPI { - -    private final CCBAPI mAPI; -    private final MetricRegistry mMetricRegistry; - -    public MonitoredCCBAPI(final CCBAPI api, final MetricRegistry metricRegistry) { -        if (api == null) { -            throw new IllegalArgumentException("api must not be null."); -        } -        mAPI = api; - -        if (metricRegistry == null) { -            throw new IllegalArgumentException("metricRegistry must not be null."); -        } -        mMetricRegistry = metricRegistry; -    } - -    @Override -    public GetCustomFieldLabelsResponse getCustomFieldLabels() throws IOException { -        final Timer.Context timer = mMetricRegistry.timer("CCBAPI.getCustomFieldLabels.time").time(); -        boolean success = false; -        try { -            final GetCustomFieldLabelsResponse resp = mAPI.getCustomFieldLabels(); -            success = true; -            return resp; -        } finally { -            timer.stop(); -            mMetricRegistry.counter("CCBAPI.getCustomFieldLabels.success").inc(success ? 1 : 0); -            mMetricRegistry.counter("CCBAPI.getCustomFieldLabels.failure").inc(!success ? 1 : 0); -        } -    } - -    @Override -    public GetLookupTableResponse getLookupTable(final GetLookupTableRequest request) throws IOException { -        final Timer.Context timer = mMetricRegistry.timer("CCBAPI.getLookupTable.time").time(); -        boolean success = false; -        try { -            final GetLookupTableResponse resp = mAPI.getLookupTable(request); -            success = true; -            return resp; -        } finally { -            timer.stop(); -            mMetricRegistry.counter("CCBAPI.getLookupTable.success").inc(success ? 1 : 0); -            mMetricRegistry.counter("CCBAPI.getLookupTable.failure").inc(!success ? 1 : 0); -        } -    } - -    @Override -    public GetIndividualProfilesResponse getIndividualProfiles(GetIndividualProfilesRequest request) -            throws IOException { -        final Timer.Context timer = mMetricRegistry.timer("CCBAPI.getIndividualProfiles").time(); -        boolean success = false; -        try { -            final GetIndividualProfilesResponse resp = mAPI.getIndividualProfiles(request); -            mMetricRegistry.counter("CCBAPI.getIndividualProfiles.count").inc(resp.getIndividuals().size()); -            success = true; -            return resp; -        } finally { -            timer.stop(); -            mMetricRegistry.counter("CCBAPI.getIndividualProfiles.success").inc(success ? 1 : 0); -            mMetricRegistry.counter("CCBAPI.getIndividualProfiles.failure").inc(!success ? 1 : 0); -        } -    } - -    @Override -    public UpdateIndividualProfileResponse updateIndividualProfile(UpdateIndividualProfileRequest request) throws IOException { -        final Timer.Context timer = mMetricRegistry.timer("CCBAPI.updateIndividualProfile").time(); -        boolean success = false; -        try { -            final UpdateIndividualProfileResponse resp = mAPI.updateIndividualProfile(request); -            success = true; -            return resp; -        } finally { -            timer.stop(); -            mMetricRegistry.counter("CCBAPI.updateIndividualProfile.success").inc(success ? 1 : 0); -            mMetricRegistry.counter("CCBAPI.updateIndividualProfile.failure").inc(!success ? 1 : 0); -        } -    } - -    @Override -    public void close() throws IOException { -        mAPI.close(); -    } -} diff --git a/src/com/p4square/grow/config/Config.java b/src/com/p4square/grow/config/Config.java deleted file mode 100644 index 2fc2ea3..0000000 --- a/src/com/p4square/grow/config/Config.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.config; - -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; -import java.io.IOException; -import java.util.Properties; - -import org.apache.log4j.Logger; - -/** - * Manage configuration for an application. - * - * Config reads one or more property files as the application config. Duplicate - * properties loaded later override properties loaded earlier. Config has the - * concept of a domain to distinguish settings for development and production. - * The default domain is prod for production. Domain can be any String such as - * dev for development or test for testing. - * - * The property files are processed like java.util.Properties except that the - * keys are specified as DOMAIN.KEY. An asterisk (*) can be used in place of a - * domain to indicate it should apply to all domains. If a domain specific entry - * exists for the current domain, it will override any global config. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class Config { -    private static final Logger LOG = Logger.getLogger(Config.class); - -    private String mDomain; -    private Properties mProperties; - -    /** -     * Construct a new Config object. -     * -     * Sets the domain to the value of the system property CONFIG_DOMAIN, if present. -     * If the system property is not set then the environment variable CONFIG_DOMAIN is checked. -     * If neither are set the domain defaults to prod. -     */ -    public Config() { -        // Check the command line for a domain property. -        mDomain = System.getProperty("CONFIG_DOMAIN"); - -        // If the domain was not set with a property, check for an environment variable. -        if (mDomain == null) { -            mDomain = System.getenv("CONFIG_DOMAIN"); -        } - -        // If neither were set, default to prod -        if (mDomain == null) { -            mDomain = "prod"; -        } - -        mProperties = new Properties(); -    } - -    /** -     * Change the domain from the default string "prod". -     * -     * @param domain The new domain. -     */ -    public void setDomain(String domain) { -        LOG.info("Setting Config domain to " + domain); -        mDomain = domain; -    } - -    /** -     * @return the current domain. -     */ -    public String getDomain() { -        return mDomain; -    } - -    /** -     * Load properties from a file. -     * Any exception are logged and suppressed. -     */ -    public void updateConfig(String propertyFilename) { -        final File propFile = new File(propertyFilename); - -        LOG.info("Loading properties from " + propFile); - -        try { -            InputStream in = new FileInputStream(propFile); -            updateConfig(in); - -        } catch (IOException e) { -            LOG.error("Could not load properties file: " + e.getMessage(), e); -        } -    } - -    /** -     * Load properties from an InputStream. -     * This method closes the InputStream when it completes. -     * -     * @param in The InputStream -     */ -    public void updateConfig(InputStream in) throws IOException { -        LOG.info("Loading properties from InputStream"); -        mProperties.load(in); -        in.close(); -    } - -    /** -     * Get a String from the config. -     * -     * @return The config value or null if it is not found. -     */ -    public String getString(String key) { -        return getString(key, null); -    } - -    /** -     * Get a String from the config. -     * -     * @return The config value or defaultValue if it can not be found. -     */ -    public String getString(final String key, final String defaultValue) { -        String result; - -        // Command line properties trump all. -        result = System.getProperty(key); -        if (result != null) { -            LOG.debug("Reading System.getProperty(" + key + "). Got result = { " + result + " }"); -            return result; -        } - -        // Environment variables can also override configs -        result = System.getenv(key); -        if (result != null) { -            LOG.debug("Reading System.getenv(" + key + "). Got result = { " + result + " }"); -            return result; -        } - -        final String domainKey = mDomain + "." + key; -        result = mProperties.getProperty(domainKey); -        if (result != null) { -            LOG.debug("Reading config for key = { " + key + " }. Got result = { " + result + " }"); -            return result; -        } - -        final String globalKey = "*." + key; -        result = mProperties.getProperty(globalKey); -        if (result != null) { -            LOG.debug("Reading config for key = { " + key + " }. Got result = { " + result + " }"); -            return result; -        } - -        LOG.debug("Reading config for key = { " + key + " }. Got default value = { " + defaultValue + " }"); -        return defaultValue; -    } - -    /** -     * Get an integer from the config. -     * -     * @return The config value or Integer.MIN_VALUE if the key is not present or the -     *         config can not be parsed. -     */ -    public int getInt(String key) { -        return getInt(key, Integer.MIN_VALUE); -    } - -    /** -     * Get an integer from the config. -     * -     * @return The config value or defaultValue if the key is not present or the -     *         config can not be parsed. -     */ -    public int getInt(String key, int defaultValue) { -        final String propertyValue = getString(key); - -        if (propertyValue != null) { -            try { -                final int result = Integer.valueOf(propertyValue); -                return result; - -            } catch (NumberFormatException e) { -                LOG.warn("Expected property to be an integer: " -                        + key + " = { " + propertyValue + " }"); -            } -        } - -        return defaultValue; -    } - -    public boolean getBoolean(String key) { -        return getBoolean(key, false); -    } - -    public boolean getBoolean(String key, boolean defaultValue) { -        final String propertyValue = getString(key); - -        if (propertyValue != null) { -            return (propertyValue.charAt(0) & 0xDF) == 'T'; -        } - -        return defaultValue; -    } -} diff --git a/src/com/p4square/grow/frontend/AccountRedirectResource.java b/src/com/p4square/grow/frontend/AccountRedirectResource.java deleted file mode 100644 index be2ae65..0000000 --- a/src/com/p4square/grow/frontend/AccountRedirectResource.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import org.restlet.data.Status; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; -import org.restlet.resource.ServerResource; - -import org.apache.log4j.Logger; - -import com.p4square.fmfacade.FreeMarkerPageResource; - -import com.p4square.grow.config.Config; -import com.p4square.grow.model.UserRecord; -import com.p4square.grow.provider.Provider; -import com.p4square.grow.provider.DelegateProvider; -import com.p4square.grow.provider.JsonEncodedProvider; - -/** - * This resource simply redirects the user to either the assessment - * or the training page. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class AccountRedirectResource extends ServerResource { -    private static final Logger LOG = Logger.getLogger(AccountRedirectResource.class); - -    private Config mConfig; -    private Provider<String, UserRecord> mUserRecordProvider; - -    // Fields pertaining to this request. -    private String mUserId; - -    @Override -    public void doInit() { -        super.doInit(); - -        GrowFrontend growFrontend = (GrowFrontend) getApplication(); -        mConfig = growFrontend.getConfig(); - -        mUserRecordProvider = new DelegateProvider<String, String, UserRecord>( -                new JsonRequestProvider<UserRecord>(getContext().getClientDispatcher(), -                    UserRecord.class)) { -            @Override -            public String makeKey(String userid) { -                return getBackendEndpoint() + "/accounts/" + userid; -            } -        }; - -        mUserId = getRequest().getClientInfo().getUser().getIdentifier(); -    } - -    /** -     * Redirect to the correct landing. -     */ -    @Override -    protected Representation get() { -        if (mUserId == null || mUserId.length() == 0) { -            // This shouldn't happen, but I want to be safe because of the DB insert below. -            setStatus(Status.CLIENT_ERROR_FORBIDDEN); -            return new ErrorPage("Not Authenticated!"); -        } - -        try { -            // Fetch account Map. -            UserRecord user = null; -            try { -                user = mUserRecordProvider.get(mUserId); -            } catch (NotFoundException e) { -                // User record doesn't exist, so create a new one. -                user = new UserRecord(getRequest().getClientInfo().getUser()); -                mUserRecordProvider.put(mUserId, user); -            } - -            // Check for the new believers cookie -            String cookie = getRequest().getCookies().getFirstValue(NewBelieverResource.COOKIE_NAME); -            if (cookie != null && cookie.length() != 0) { -                user.setLanding("training"); -                user.setNewBeliever(true); -                mUserRecordProvider.put(mUserId, user); -            } - -            String landing = user.getLanding(); -            if (landing == null) { -                landing = "assessment"; -            } - -            String nextPage = mConfig.getString("dynamicRoot", ""); -            nextPage += "/account/" + landing; -            getResponse().redirectSeeOther(nextPage); -            return new StringRepresentation("Redirecting to " + nextPage); - -        } catch (Exception e) { -            LOG.fatal("Could not render page: " + e.getMessage(), e); -            setStatus(Status.SERVER_ERROR_INTERNAL); -            return ErrorPage.RENDER_ERROR; -        } -    } - -    /** -     * @return The backend endpoint URI -     */ -    private String getBackendEndpoint() { -        return mConfig.getString("backendUri", "riap://component/backend"); -    } -} diff --git a/src/com/p4square/grow/frontend/AssessmentResetPage.java b/src/com/p4square/grow/frontend/AssessmentResetPage.java deleted file mode 100644 index 519b135..0000000 --- a/src/com/p4square/grow/frontend/AssessmentResetPage.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.util.Map; - -import freemarker.template.Template; - -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; -import org.restlet.ext.freemarker.TemplateRepresentation; - -import org.apache.log4j.Logger; - -import com.p4square.fmfacade.FreeMarkerPageResource; - -import com.p4square.fmfacade.json.JsonRequestClient; -import com.p4square.fmfacade.json.JsonResponse; -import com.p4square.fmfacade.json.ClientException; - -import com.p4square.grow.config.Config; - -/** - * This page delete's the current user's assessment. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class AssessmentResetPage extends FreeMarkerPageResource { -    private static final Logger LOG = Logger.getLogger(AssessmentResetPage.class); - -    private GrowFrontend mGrowFrontend; -    private Config mConfig; -    private JsonRequestClient mJsonClient; - -    private String mUserId; - -    @Override -    public void doInit() { -        super.doInit(); - -        mGrowFrontend = (GrowFrontend) getApplication(); -        mConfig = mGrowFrontend.getConfig(); - -        mJsonClient = new JsonRequestClient(getContext().getClientDispatcher()); - -        mUserId = getRequest().getClientInfo().getUser().getIdentifier(); -    } - -    /** -     * Return the login page. -     */ -    @Override -    protected Representation get() { -        try { -            // Get the assessment results -            JsonResponse response = backendDelete("/accounts/" + mUserId + "/assessment"); -            if (!response.getStatus().isSuccess()) { -                setStatus(Status.SERVER_ERROR_INTERNAL); -                return ErrorPage.BACKEND_ERROR; -            } - -            String nextPage = mConfig.getString("dynamicRoot", "") -                    + "/account/assessment/question/first"; -            getResponse().redirectSeeOther(nextPage); -            return new StringRepresentation("Redirecting to " + nextPage); - -        } catch (Exception e) { -            LOG.fatal("Could not render page: " + e.getMessage(), e); -            setStatus(Status.SERVER_ERROR_INTERNAL); -            return ErrorPage.RENDER_ERROR; -        } -    } - -    /** -     * @return The backend endpoint URI -     */ -    private String getBackendEndpoint() { -        return mConfig.getString("backendUri", "riap://component/backend"); -    } - -    /** -     * Helper method to send a GET to the backend. -     */ -    private JsonResponse backendDelete(final String uri) { -        LOG.debug("Sending backend GET " + uri); - -        final JsonResponse response = mJsonClient.delete(getBackendEndpoint() + uri); -        final Status status = response.getStatus(); -        if (!status.isSuccess() && !Status.CLIENT_ERROR_NOT_FOUND.equals(status)) { -            LOG.warn("Error making backend request for '" + uri + "'. status = " + response.getStatus().toString()); -        } - -        return response; -    } -} diff --git a/src/com/p4square/grow/frontend/AssessmentResultsPage.java b/src/com/p4square/grow/frontend/AssessmentResultsPage.java deleted file mode 100644 index f1c924b..0000000 --- a/src/com/p4square/grow/frontend/AssessmentResultsPage.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.util.Date; -import java.util.Map; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.p4square.f1oauth.FellowshipOneIntegrationDriver; -import freemarker.template.Template; - -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; -import org.restlet.ext.freemarker.TemplateRepresentation; - -import org.apache.log4j.Logger; - -import com.p4square.fmfacade.FreeMarkerPageResource; - -import com.p4square.fmfacade.json.JsonRequestClient; -import com.p4square.fmfacade.json.JsonResponse; -import com.p4square.fmfacade.json.ClientException; - -import com.p4square.f1oauth.Attribute; -import com.p4square.f1oauth.F1API; -import com.p4square.f1oauth.F1User; - -import com.p4square.grow.config.Config; -import com.p4square.grow.provider.JsonEncodedProvider; -import org.restlet.security.User; - -/** - * This page fetches the user's final score and displays the transitional page between - * the assessment and the videos. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class AssessmentResultsPage extends FreeMarkerPageResource { -    private static final Logger LOG = Logger.getLogger(AssessmentResultsPage.class); - -    private GrowFrontend mGrowFrontend; -    private Config mConfig; -    private JsonRequestClient mJsonClient; - -    private String mUserId; - -    @Override -    public void doInit() { -        super.doInit(); - -        mGrowFrontend = (GrowFrontend) getApplication(); -        mConfig = mGrowFrontend.getConfig(); - -        mJsonClient = new JsonRequestClient(getContext().getClientDispatcher()); - -        mUserId = getRequest().getClientInfo().getUser().getIdentifier(); -    } - -    /** -     * Return the login page. -     */ -    @Override -    protected Representation get() { -        Template t = mGrowFrontend.getTemplate("templates/assessment-results.ftl"); - -        try { -            if (t == null) { -                setStatus(Status.CLIENT_ERROR_NOT_FOUND); -                return ErrorPage.TEMPLATE_NOT_FOUND; -            } - -            Map<String, Object> root = getRootObject(); - -            // Get the assessment results -            JsonResponse response = backendGet("/accounts/" + mUserId + "/assessment"); -            if (!response.getStatus().isSuccess()) { -                setStatus(Status.SERVER_ERROR_INTERNAL); -                return ErrorPage.BACKEND_ERROR; -            } - -            final String score = (String) response.getMap().get("result"); -            if (score == null) { -                // Odd... send them to the first questions -                String nextPage = mConfig.getString("dynamicRoot", "") -                    + "/account/assessment/question/first"; -                getResponse().redirectSeeOther(nextPage); -                return new StringRepresentation("Redirecting to " + nextPage); -            } - -            // Publish results in F1 -            publishScoreInF1(response.getMap()); - -            root.put("stage", score); -            return new TemplateRepresentation(t, root, MediaType.TEXT_HTML); - -        } catch (Exception e) { -            LOG.fatal("Could not render page: " + e.getMessage(), e); -            setStatus(Status.SERVER_ERROR_INTERNAL); -            return ErrorPage.RENDER_ERROR; -        } -    } - -    private void publishScoreInF1(Map results) { -        final ProgressReporter reporter = mGrowFrontend.getThirdPartyIntegrationFactory().getProgressReporter(); - -        try { -            final User user = getRequest().getClientInfo().getUser(); -            final String level = results.get("result").toString(); -            final Date completionDate = new Date(); -            final String data = JsonEncodedProvider.MAPPER.writeValueAsString(results); - -            reporter.reportAssessmentComplete(user, level, completionDate, data); - -        } catch (JsonProcessingException e) { -            LOG.error("Failed to generate json " + e.getMessage(), e); -        } -    } - -    /** -     * @return The backend endpoint URI -     */ -    private String getBackendEndpoint() { -        return mConfig.getString("backendUri", "riap://component/backend"); -    } - -    /** -     * Helper method to send a GET to the backend. -     */ -    private JsonResponse backendGet(final String uri) { -        LOG.debug("Sending backend GET " + uri); - -        final JsonResponse response = mJsonClient.get(getBackendEndpoint() + uri); -        final Status status = response.getStatus(); -        if (!status.isSuccess() && !Status.CLIENT_ERROR_NOT_FOUND.equals(status)) { -            LOG.warn("Error making backend request for '" + uri + "'. status = " -                    + response.getStatus().toString()); -        } - -        return response; -    } -} diff --git a/src/com/p4square/grow/frontend/AuthenticatedResource.java b/src/com/p4square/grow/frontend/AuthenticatedResource.java deleted file mode 100644 index 800eb83..0000000 --- a/src/com/p4square/grow/frontend/AuthenticatedResource.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import org.restlet.resource.ServerResource; -import org.restlet.representation.Representation; - -/** - *  - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class AuthenticatedResource extends ServerResource { -    protected Representation post() { -        return null; -    } -} diff --git a/src/com/p4square/grow/frontend/ChapterCompletePage.java b/src/com/p4square/grow/frontend/ChapterCompletePage.java deleted file mode 100644 index 35abc43..0000000 --- a/src/com/p4square/grow/frontend/ChapterCompletePage.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.util.Date; -import java.util.Map; - -import com.p4square.f1oauth.FellowshipOneIntegrationDriver; -import freemarker.template.Template; - -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; -import org.restlet.ext.freemarker.TemplateRepresentation; - -import org.apache.log4j.Logger; - -import com.p4square.fmfacade.FreeMarkerPageResource; - -import com.p4square.fmfacade.json.JsonRequestClient; -import com.p4square.fmfacade.json.JsonResponse; -import com.p4square.fmfacade.json.ClientException; - -import com.p4square.f1oauth.Attribute; -import com.p4square.f1oauth.F1API; -import com.p4square.f1oauth.F1User; - -import com.p4square.grow.config.Config; -import com.p4square.grow.model.TrainingRecord; -import com.p4square.grow.provider.Provider; -import com.p4square.grow.provider.TrainingRecordProvider; -import org.restlet.security.User; - -/** - * This resource displays the transitional page between chapters. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class ChapterCompletePage extends FreeMarkerPageResource { -    private static final Logger LOG = Logger.getLogger(ChapterCompletePage.class); - -    private GrowFrontend mGrowFrontend; -    private Config mConfig; -    private JsonRequestClient mJsonClient; -    private Provider<String, TrainingRecord> mTrainingRecordProvider; - -    private String mUserId; -    private String mChapter; - -    @Override -    public void doInit() { -        super.doInit(); - -        mGrowFrontend = (GrowFrontend) getApplication(); -        mConfig = mGrowFrontend.getConfig(); - -        mJsonClient = new JsonRequestClient(getContext().getClientDispatcher()); -        mTrainingRecordProvider = new TrainingRecordProvider<String>( -                new JsonRequestProvider<TrainingRecord>( -                    getContext().getClientDispatcher(), -                    TrainingRecord.class)) { -            @Override -            public String makeKey(String userid) { -                return getBackendEndpoint() + "/accounts/" + userid + "/training"; -            } -        }; - -        mUserId = getRequest().getClientInfo().getUser().getIdentifier(); - -        mChapter = getAttribute("chapter"); -    } - -    /** -     * Return the login page. -     */ -    @Override -    protected Representation get() { -        try { -            Map<String, Object> root = getRootObject(); - -            // Get the training summary -            TrainingRecord trainingRecord = mTrainingRecordProvider.get(mUserId); -            if (trainingRecord == null) { -                // Wait. What? Everyone has a training record... -                setStatus(Status.SERVER_ERROR_INTERNAL); -                return new ErrorPage("Could not retrieve your training record."); -            } - -            // Verify they completed the chapter. -            Map<String, Boolean> chapters = trainingRecord.getPlaylist().getChapterStatuses(); -            Boolean completed = chapters.get(mChapter); -            if (completed == null || !completed) { -                // Redirect back to training page... -                String nextPage = mConfig.getString("dynamicRoot", ""); -                nextPage += "/account/training/" + mChapter; -                getResponse().redirectSeeOther(nextPage); -                return new StringRepresentation("Redirecting to " + nextPage); -            } - -            // Publish the training chapter complete attribute. -            assignAttribute(); - -            // Find the next chapter -            String nextChapter = null; -            { -                int min = Integer.MAX_VALUE; -                for (Map.Entry<String, Boolean> chapter : chapters.entrySet()) { -                    int index = chapterIndex(chapter.getKey()); -                    if (!chapter.getValue() && index < min) { -                        min = index; -                        nextChapter = chapter.getKey(); -                    } -                } -            } - -            String nextOverride = getQueryValue("next"); -            if (nextOverride != null) { -                nextChapter = nextOverride; -            } - -            root.put("stage", mChapter); -            root.put("nextstage", nextChapter); - -            /* -             * We will display one of two transitional pages: -             *  -             * If the next chapter has a forward page, display the forward page. -             * Else, if this chapter is not "Introduction", display the chapter -             * complete message. -             */ -            Template t = mGrowFrontend.getTemplate("templates/stage-" -                    + nextChapter + "-forward.ftl"); - -            if (t == null) { -                // Skip the chapter complete message for "Introduction" -                if ("introduction".equals(mChapter)) { -                    String nextPage = mConfig.getString("dynamicRoot", ""); -                    nextPage += "/account/training/" + nextChapter; -                    getResponse().redirectSeeOther(nextPage); -                    return new StringRepresentation("Redirecting to " + nextPage); -                } - -                t = mGrowFrontend.getTemplate("templates/stage-complete.ftl"); -                if (t == null) { -                    setStatus(Status.CLIENT_ERROR_NOT_FOUND); -                    return ErrorPage.TEMPLATE_NOT_FOUND; -                } -            } - -            return new TemplateRepresentation(t, root, MediaType.TEXT_HTML); - -        } catch (Exception e) { -            LOG.fatal("Could not render page: " + e.getMessage(), e); -            setStatus(Status.SERVER_ERROR_INTERNAL); -            return ErrorPage.RENDER_ERROR; -        } -    } - -    private void assignAttribute() { -        final ProgressReporter reporter = mGrowFrontend.getThirdPartyIntegrationFactory().getProgressReporter(); - -        final User user = getRequest().getClientInfo().getUser(); -        final Date completionDate = new Date(); - -        reporter.reportChapterComplete(user, mChapter, completionDate); -    } - -    /** -     * @return The backend endpoint URI -     */ -    private String getBackendEndpoint() { -        return mConfig.getString("backendUri", "riap://component/backend"); -    } - -    /** -     * Helper method to send a GET to the backend. -     */ -    private JsonResponse backendGet(final String uri) { -        LOG.debug("Sending backend GET " + uri); - -        final JsonResponse response = mJsonClient.get(getBackendEndpoint() + uri); -        final Status status = response.getStatus(); -        if (!status.isSuccess() && !Status.CLIENT_ERROR_NOT_FOUND.equals(status)) { -            LOG.warn("Error making backend request for '" + uri -                    + "'. status = " + response.getStatus().toString()); -        } - -        return response; -    } - -    int chapterIndex(String chapter) { -        if ("leader".equals(chapter)) { -            return 5; -        } else if ("teacher".equals(chapter)) { -            return 4; -        } else if ("disciple".equals(chapter)) { -            return 3; -        } else if ("believer".equals(chapter)) { -            return 2; -        } else if ("seeker".equals(chapter)) { -            return 1; -        } else { -            return Integer.MAX_VALUE; -        } -    } -} diff --git a/src/com/p4square/grow/frontend/ErrorPage.java b/src/com/p4square/grow/frontend/ErrorPage.java deleted file mode 100644 index 81abe74..0000000 --- a/src/com/p4square/grow/frontend/ErrorPage.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.util.HashMap; -import java.util.Map; - -import java.io.IOException; -import java.io.Writer; - -import freemarker.template.Template; - -import org.restlet.data.MediaType; -import org.restlet.ext.freemarker.TemplateRepresentation; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; -import org.restlet.representation.WriterRepresentation; - -import com.p4square.fmfacade.FreeMarkerPageResource; - -/** - * ErrorPage wraps a String or Template Representation and displays the given - * error message. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class ErrorPage extends WriterRepresentation { -    public static final ErrorPage TEMPLATE_NOT_FOUND = -        new ErrorPage("Could not find the requested page template."); - -    public static final ErrorPage RENDER_ERROR = -        new ErrorPage("Error rendering page."); - -    public static final ErrorPage BACKEND_ERROR = -        new ErrorPage("Error communicating with backend."); - -    public static final ErrorPage NOT_FOUND = -        new ErrorPage("The requested URL could not be found."); - -    private static Template cTemplate = null; -    private static Map<String, Object> cRoot = null; - -    private final String mMessage; - -    public ErrorPage(String msg) { -        this(msg, MediaType.TEXT_HTML); -    } - -    public ErrorPage(String msg, MediaType mediaType) { -        super(mediaType); - -        mMessage = msg; -    } - -    public static synchronized void setTemplate(Template template, Map<String, Object> root) { -        cTemplate = template; -        cRoot = root; -    } - -    protected Representation getRepresentation() { -        if (cTemplate == null) { -            return new StringRepresentation(mMessage); - -        } else { -            Map<String, Object> root = new HashMap<String, Object>(cRoot); -            root.put("errorMessage", mMessage); -            return new TemplateRepresentation(cTemplate, root, MediaType.TEXT_HTML); -        } -    } - -    @Override -    public void write(Writer writer) throws IOException { -        getRepresentation().write(writer); -    } -} diff --git a/src/com/p4square/grow/frontend/FeedData.java b/src/com/p4square/grow/frontend/FeedData.java deleted file mode 100644 index feb03a1..0000000 --- a/src/com/p4square/grow/frontend/FeedData.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.io.IOException; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; - -import org.restlet.Context; -import org.restlet.Restlet; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; - -import com.p4square.grow.config.Config; -import com.p4square.grow.frontend.JsonRequestProvider; -import com.p4square.grow.model.Message; -import com.p4square.grow.model.MessageThread; -import com.p4square.grow.provider.JsonEncodedProvider; -import com.p4square.grow.provider.Provider; - -/** - * Fetch feed data for a topic. - */ -public class FeedData { - -    /** -     * Allowed Topics. -     */ -    public static final HashSet<String> TOPICS = new HashSet(Arrays.asList("seeker", "believer", -            "disciple", "teacher", "leader")); - - -    private final Config mConfig; -    private final String mBackendURI; - -    // TODO: Elegantly merge the List and individual providers. -    private final JsonRequestProvider<List<MessageThread>> mThreadsProvider; -    private final JsonRequestProvider<MessageThread> mThreadProvider; - -    private final JsonRequestProvider<List<Message>> mMessagesProvider; -    private final JsonRequestProvider<Message> mMessageProvider; - -    public FeedData(final Context context, final Config config) { -        mConfig = config; -        mBackendURI = mConfig.getString("backendUri", "riap://component/backend") + "/feed"; - -        Restlet clientDispatcher = context.getClientDispatcher(); - -        TypeFactory factory = JsonEncodedProvider.MAPPER.getTypeFactory(); - -        JavaType threadType = factory.constructCollectionType(List.class, MessageThread.class); -        mThreadsProvider = new JsonRequestProvider<List<MessageThread>>(clientDispatcher, threadType); -        mThreadProvider = new JsonRequestProvider<MessageThread>(clientDispatcher, MessageThread.class); - -        JavaType messageType = factory.constructCollectionType(List.class, Message.class); -        mMessagesProvider = new JsonRequestProvider<List<Message>>(clientDispatcher, messageType); -        mMessageProvider = new JsonRequestProvider<Message>(clientDispatcher, Message.class); -    } - -    /** -     * Get the threads for a topic. -     * -     * @param topic The topic to request threads for. -     * @param limit The maximum number of threads. -     * @return A list of MessageThread objects. -     */ -    public List<MessageThread> getThreads(final String topic, final int limit) throws IOException { -        return mThreadsProvider.get(makeUrl(limit, topic)); -    } - -    public List<Message> getMessages(final String topic, final String threadId) throws IOException { -        return mMessagesProvider.get(makeUrl(topic, threadId)); -    } - -    public void createThread(final String topic, final Message message) throws IOException { -        MessageThread thread = new MessageThread(); -        thread.setMessage(message); - -        mThreadProvider.post(makeUrl(topic), thread); -    } - -    public void createResponse(final String topic, final String thread, final Message message) -        throws IOException { - -        mMessageProvider.post(makeUrl(topic, thread), message); -    } - -    private String makeUrl(String... parts) { -        String url = mBackendURI; -        for (String part : parts) { -            url += "/" + part; -        } - -        return url; -    } - -    private String makeUrl(int limit, String... parts) { -        return makeUrl(parts) + "?limit=" + limit; -    } -} diff --git a/src/com/p4square/grow/frontend/FeedResource.java b/src/com/p4square/grow/frontend/FeedResource.java deleted file mode 100644 index 13d0fa0..0000000 --- a/src/com/p4square/grow/frontend/FeedResource.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.io.IOException; - -import org.restlet.data.Form; -import org.restlet.data.Status; -import org.restlet.representation.Representation; -import org.restlet.resource.ServerResource; - -import org.apache.log4j.Logger; - -import com.p4square.grow.config.Config; -import com.p4square.grow.model.Message; -import com.p4square.grow.model.UserRecord; - -/** - * This resource handles user interactions with the feed. - */ -public class FeedResource extends ServerResource { -    private static final Logger LOG = Logger.getLogger(FeedResource.class); - -    private Config mConfig; - -    private FeedData mFeedData; - -    // Fields pertaining to this request. -    protected String mTopic; -    protected String mThread; - -    @Override -    public void doInit() { -        super.doInit(); - -        GrowFrontend growFrontend = (GrowFrontend) getApplication(); -        mConfig = growFrontend.getConfig(); - -        mFeedData = new FeedData(getContext(), mConfig); - -        mTopic = getAttribute("topic"); -        if (mTopic != null) { -            mTopic = mTopic.trim(); -        } - -        mThread = getAttribute("thread"); -        if (mThread != null) { -            mThread = mThread.trim(); -        } -    } - -    /** -     * Create a new MessageThread. -     */ -    @Override -    protected Representation post(Representation entity) { -        try { -            if (mTopic == null || mTopic.length() == 0 || !FeedData.TOPICS.contains(mTopic)) { -                setStatus(Status.CLIENT_ERROR_NOT_FOUND); -                return ErrorPage.NOT_FOUND; -            } - -            Form form = new Form(entity); - -            String question = form.getFirstValue("question"); - -            Message message = new Message(); -            message.setMessage(question); - -            UserRecord user = new UserRecord(getRequest().getClientInfo().getUser()); -            message.setAuthor(user); - -            if (mThread != null && mThread.length() != 0) { -                // Post a response -                mFeedData.createResponse(mTopic, mThread, message); - -            } else { -                // Post a new thread -                mFeedData.createThread(mTopic, message); -            } - -            /* -             * Can't trust the referrer, so we'll send them to the -             * appropriate part of the training page -             * TODO: This could be better done. -             */ -            String nextPage = mConfig.getString("dynamicRoot", ""); -            nextPage += "/account/training/" + mTopic; -            getResponse().redirectSeeOther(nextPage); -            return null; - -        } catch (IOException e) { -            LOG.fatal("Could not save message: " + e.getMessage(), e); -            setStatus(Status.SERVER_ERROR_INTERNAL); -            return ErrorPage.BACKEND_ERROR; - -        } -    } -} diff --git a/src/com/p4square/grow/frontend/GroupLeaderTrainingPageResource.java b/src/com/p4square/grow/frontend/GroupLeaderTrainingPageResource.java deleted file mode 100644 index 3ab140e..0000000 --- a/src/com/p4square/grow/frontend/GroupLeaderTrainingPageResource.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -/** - * Display the Group Leader training videos. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class GroupLeaderTrainingPageResource extends TrainingPageResource { -    private static final String[] CHAPTERS = { "leader" }; - -    @Override -    public void doInit() { -        super.doInit(); - -        mChapter = "leader"; -    } - -    @Override -    public String[] getChaptersInOrder() { -        return CHAPTERS; -    } -} diff --git a/src/com/p4square/grow/frontend/GrowFrontend.java b/src/com/p4square/grow/frontend/GrowFrontend.java deleted file mode 100644 index b5f62fb..0000000 --- a/src/com/p4square/grow/frontend/GrowFrontend.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan <jesse@jesterpm.net> - */ - -package com.p4square.grow.frontend; - -import java.io.File; -import java.io.IOException; -import java.lang.reflect.Constructor; - -import freemarker.template.Template; - -import org.restlet.Application; -import org.restlet.Component; -import org.restlet.Context; -import org.restlet.Restlet; -import org.restlet.data.Protocol; -import org.restlet.resource.Directory; -import org.restlet.routing.Redirector; -import org.restlet.routing.Router; -import org.restlet.security.Authenticator; - -import com.codahale.metrics.MetricRegistry; - -import org.apache.log4j.Logger; - -import com.p4square.fmfacade.FMFacade; -import com.p4square.fmfacade.FreeMarkerPageResource; - -import com.p4square.grow.config.Config; - -import com.p4square.restlet.metrics.MetricRouter; - -import com.p4square.session.SessionCheckingAuthenticator; -import com.p4square.session.SessionCreatingAuthenticator; -import org.restlet.security.Verifier; - -/** - * This is the Restlet Application implementing the Grow project front-end. - * It's implemented as an extension of FMFacade that connects interactive pages - * with various ServerResources. This class provides a main method to start a - * Jetty instance for testing. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class GrowFrontend extends FMFacade { -    private static Logger LOG = Logger.getLogger(GrowFrontend.class); - -    private final Config mConfig; -    private final MetricRegistry mMetricRegistry; - -    private IntegrationDriver mIntegrationFactory; - -    public GrowFrontend() { -        this(new Config(), new MetricRegistry()); -    } - -    public GrowFrontend(Config config, MetricRegistry metricRegistry) { -        mConfig = config; -        mMetricRegistry = metricRegistry; -    } - -    public Config getConfig() { -        return mConfig; -    } - -    public MetricRegistry getMetrics() { -        return mMetricRegistry; -    } - -    @Override -    public synchronized void start() throws Exception { -        Template errorTemplate = getTemplate("templates/error.ftl"); -        if (errorTemplate != null) { -            ErrorPage.setTemplate(errorTemplate, -                    FreeMarkerPageResource.baseRootObject(getContext(), this)); -        } - -        getContext().getAttributes().put("com.p4square.grow.config", mConfig); -        getContext().getAttributes().put("com.p4square.grow.metrics", mMetricRegistry); - -        super.start(); -    } - -    public synchronized IntegrationDriver getThirdPartyIntegrationFactory() { -        if (mIntegrationFactory == null) { -            final String driverClassName = getConfig().getString("integrationDriver", -                                                                 "com.p4square.f1oauth.FellowshipOneIntegrationDriver"); -            try { -                Class<?> clazz = Class.forName(driverClassName); -                Constructor<?> constructor = clazz.getConstructor(Context.class); -                mIntegrationFactory = (IntegrationDriver) constructor.newInstance(getContext()); -            } catch (Exception e) { -                LOG.error("Failed to instantiate IntegrationDriver " + driverClassName); -            } -        } - -        return mIntegrationFactory; -    } - -    @Override -    protected Router createRouter() { -        Router router = new MetricRouter(getContext(), mMetricRegistry); - -        final Authenticator defaultGuard = new SessionCheckingAuthenticator(getContext(), true); -        defaultGuard.setNext(FreeMarkerPageResource.class); -        router.attachDefault(defaultGuard); -        router.attach("/", new Redirector(getContext(), "index.html", Redirector.MODE_CLIENT_PERMANENT)); -        router.attach("/login.html", LoginPageResource.class); -        router.attach("/newaccount.html", NewAccountResource.class); -        router.attach("/newbeliever", NewBelieverResource.class); - -        final Router accountRouter = new MetricRouter(getContext(), mMetricRegistry); -        accountRouter.attach("/authenticate", AuthenticatedResource.class); -        accountRouter.attach("/logout", LogoutResource.class); - -        accountRouter.attach("", AccountRedirectResource.class); -        accountRouter.attach("/assessment/question/{questionId}", SurveyPageResource.class); -        accountRouter.attach("/assessment/results", AssessmentResultsPage.class); -        accountRouter.attach("/assessment/reset", AssessmentResetPage.class); -        accountRouter.attach("/assessment", SurveyPageResource.class); -        accountRouter.attach("/training/{chapter}/completed", ChapterCompletePage.class); -        accountRouter.attach("/training/{chapter}/videos/{videoId}.json", VideosResource.class); -        accountRouter.attach("/training/{chapter}", TrainingPageResource.class); -        accountRouter.attach("/training", TrainingPageResource.class); -        accountRouter.attach("/feed/{topic}", FeedResource.class); -        accountRouter.attach("/feed/{topic}/{thread}", FeedResource.class); - -        final Authenticator accountGuard = createAuthenticatorChain(accountRouter); -        router.attach("/account", accountGuard); - -        return router; -    } - -    private Authenticator createAuthenticatorChain(Restlet last) { -        final Context context = getContext(); -        final String loginPage = getConfig().getString("dynamicRoot", "") + "/login.html"; -        final String loginPost = getConfig().getString("dynamicRoot", "") + "/account/authenticate"; -        final String defaultPage = getConfig().getString("dynamicRoot", "") + "/account"; - -        // This is used to check for an existing session -        SessionCheckingAuthenticator sessionChk = new SessionCheckingAuthenticator(context, true); - -        // This is used to authenticate the user -        Verifier verifier = getThirdPartyIntegrationFactory().newUserAuthenticationVerifier(); -        LoginFormAuthenticator loginAuth = new LoginFormAuthenticator(context, false, verifier); -        loginAuth.setLoginFormUrl(loginPage); -        loginAuth.setLoginPostUrl(loginPost); -        loginAuth.setDefaultPage(defaultPage); - -        // This is used to create a new session for a newly authenticated user. -        SessionCreatingAuthenticator sessionCreate = new SessionCreatingAuthenticator(context); - -        sessionChk.setNext(loginAuth); -        loginAuth.setNext(sessionCreate); - -        sessionCreate.setNext(last); - -        return sessionChk; -    } - -    /** -     * 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.getClients().add(Protocol.HTTPS); -        component.getClients().add(Protocol.FILE); -        //component.getClients().add(new Client(null, Arrays.asList(Protocol.HTTPS), "org.restlet.ext.httpclient.HttpClientHelper")); - -        // Static content -        try { -            component.getDefaultHost().attach("/images/", new FileServingApp("./build/root/images/")); -            component.getDefaultHost().attach("/scripts", new FileServingApp("./build/root/scripts")); -            component.getDefaultHost().attach("/style.css", new FileServingApp("./build/root/style.css")); -            component.getDefaultHost().attach("/favicon.ico", new FileServingApp("./build/root/favicon.ico")); -            component.getDefaultHost().attach("/notfound.html", new FileServingApp("./build/root/notfound.html")); -            component.getDefaultHost().attach("/error.html", new FileServingApp("./build/root/error.html")); -        } catch (IOException e) { -            LOG.error("Could not create directory for static resources: " -                    + e.getMessage(), e); -        } - -        // Setup App -        GrowFrontend app = new GrowFrontend(); - -        // Load an optional config file from the first argument. -        app.getConfig().setDomain("dev"); -        if (args.length == 1) { -            app.getConfig().updateConfig(args[0]); -        } - -        component.getDefaultHost().attach(app); - -        // Setup shutdown hook -        Runtime.getRuntime().addShutdownHook(new Thread() { -            public void run() { -                try { -                    component.stop(); -                } catch (Exception e) { -                    LOG.error("Exception during cleanup", e); -                } -            } -        }); - -        LOG.info("Starting server..."); - -        try { -            component.start(); -        } catch (Exception e) { -            LOG.fatal("Could not start: " + e.getMessage(), e); -        } -    } - -    private static class FileServingApp extends Application { -        private final String mPath; - -        public FileServingApp(String path) throws IOException { -            mPath = new File(path).getAbsolutePath(); -        } - -        @Override -        public Restlet createInboundRoot() { -            return new Directory(getContext(), "file://" + mPath); -        } -    } -} diff --git a/src/com/p4square/grow/frontend/IntegrationDriver.java b/src/com/p4square/grow/frontend/IntegrationDriver.java deleted file mode 100644 index b9c3508..0000000 --- a/src/com/p4square/grow/frontend/IntegrationDriver.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.p4square.grow.frontend; - -import org.restlet.security.Verifier; - -/** - * An IntegrationDriver is used to create implementations of various objects - * used to integration Grow with a particular Church Management System. - */ -public interface IntegrationDriver { - -    /** -     * Create a new Restlet Verifier to authenticate users when they login to the site. -     * -     * @return A Verifier. -     */ -    Verifier newUserAuthenticationVerifier(); - -    /** -     * Return a ProgressReporter for this Church Management System. -     * -     * The ProgressReporter should be thread-safe. -     * -     * @return The ProgressReporter. -     */ -    ProgressReporter getProgressReporter(); -} diff --git a/src/com/p4square/grow/frontend/JsonRequestProvider.java b/src/com/p4square/grow/frontend/JsonRequestProvider.java deleted file mode 100644 index bf3b2b3..0000000 --- a/src/com/p4square/grow/frontend/JsonRequestProvider.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.io.IOException; - -import com.fasterxml.jackson.databind.JavaType; - -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.Restlet; -import org.restlet.data.Method; -import org.restlet.data.Status; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; - -import com.p4square.grow.provider.Provider; -import com.p4square.grow.provider.JsonEncodedProvider; - -/** - * Fetch a JSON object via a Request. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class JsonRequestProvider<V> extends JsonEncodedProvider<V> implements Provider<String, V> { - -    private final Restlet mDispatcher; - -    public JsonRequestProvider(Restlet dispatcher, Class<V> clazz) { -        super(clazz); - -        mDispatcher = dispatcher; -    } - -    public JsonRequestProvider(Restlet dispatcher, JavaType type) { -        super(type); - -        mDispatcher = dispatcher; -    } - -    @Override -    public V get(String url) throws IOException { -        Request request = new Request(Method.GET, url); -        Response response = mDispatcher.handle(request); -        Representation representation = response.getEntity(); - -        if (!response.getStatus().isSuccess()) { -            if (representation != null) { -                representation.release(); -            } - -            if (Status.CLIENT_ERROR_NOT_FOUND.equals(response.getStatus())) { -                throw new NotFoundException("Could not get object. " + response.getStatus()); -            } else { -                throw new IOException("Could not get object. " + response.getStatus()); -            } -        } - -        return decode(representation.getText()); -    } - -    @Override -    public void put(String url, V obj) throws IOException { -        final Request request = new Request(Method.PUT, url); -        request.setEntity(new StringRepresentation(encode(obj))); - -        final Response response = mDispatcher.handle(request); - -        if (!response.getStatus().isSuccess()) { -            throw new IOException("Could not put object. " + response.getStatus()); -        } -    } - -    /** -     * Variant of put() which makes a POST request to the url. -     * -     * This method may eventually be incorporated into Provider for -     * creating new objects with auto-generated IDs. -     * -     * @param url The url to make the request to. -     * @param obj The post to post. -     * @throws IOException on failure. -     */ -    public void post(String url, V obj) throws IOException { -        final Request request = new Request(Method.POST, url); -        request.setEntity(new StringRepresentation(encode(obj))); - -        final Response response = mDispatcher.handle(request); - -        if (!response.getStatus().isSuccess()) { -            throw new IOException("Could not put object. " + response.getStatus()); -        } -    } -} diff --git a/src/com/p4square/grow/frontend/LoginFormAuthenticator.java b/src/com/p4square/grow/frontend/LoginFormAuthenticator.java deleted file mode 100644 index 21c9097..0000000 --- a/src/com/p4square/grow/frontend/LoginFormAuthenticator.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import org.apache.log4j.Logger; - -import org.restlet.Context; -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.data.ChallengeResponse; -import org.restlet.data.ChallengeScheme; -import org.restlet.data.Form; -import org.restlet.data.Method; -import org.restlet.data.Reference; -import org.restlet.security.Authenticator; -import org.restlet.security.Verifier; - -/** - * LoginFormAuthenticator changes - * - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class LoginFormAuthenticator extends Authenticator { -    private static final Logger LOG = Logger.getLogger(LoginFormAuthenticator.class); - -    private final Verifier mVerifier; - -    private String mLoginPage    = "/login.html"; -    private String mLoginPostUrl = "/authenticate"; -    private String mDefaultRedirect = "/index.html"; - -    public LoginFormAuthenticator(Context context, boolean optional, Verifier verifier) { -        super(context, false, optional, null); - -        mVerifier = verifier; -    } - -    public void setLoginFormUrl(String url) { -        mLoginPage = url; -    } - -    public void setLoginPostUrl(String url) { -        mLoginPostUrl = url; -    } - -    public void setDefaultPage(String url) { -        mDefaultRedirect = url; -    } - -    @Override -    protected int beforeHandle(Request request, Response response) { -        if (!isLoginAttempt(request) && request.getClientInfo().isAuthenticated()) { -            // TODO: Logout -            LOG.debug("Already authenticated. Skipping"); -            return CONTINUE; - -        } else { -            return super.beforeHandle(request, response); -        } -    } - - -    @Override -    protected boolean authenticate(Request request, Response response) { -        boolean isLoginAttempt = isLoginAttempt(request); - -        Form query = request.getOriginalRef().getQueryAsForm(); -        String redirect = query.getFirstValue("redirect"); -        if (redirect == null || redirect.length() == 0) { -            if (isLoginAttempt) { -                redirect = mDefaultRedirect; -            } else { -                redirect = request.getResourceRef().getPath(); -            } -        } - -        boolean authenticationFailed = false; - -        if (isLoginAttempt) { -            LOG.debug("Attempting authentication"); - -            // Process login form -            final Form form = new Form(request.getEntity()); -            final String email = form.getFirstValue("email"); -            final String password = form.getFirstValue("password"); - -            boolean authenticated = false; - -            if (email != null && !"".equals(email) && -                password != null && !"".equals(password)) { - -                LOG.debug("Got login request from " + email); - -                request.setChallengeResponse( -                    new ChallengeResponse(ChallengeScheme.HTTP_BASIC, email, password.toCharArray())); - -                // We expect the verifier to setup the User object. -                int result = mVerifier.verify(request, response); -                if (result == Verifier.RESULT_VALID) { -                    return true; -                } -            } - -            authenticationFailed = true; -        } - -        if (!isOptional() || authenticationFailed) { -            Reference ref = new Reference(mLoginPage); -            ref.addQueryParameter("redirect", redirect); - -            if (authenticationFailed) { -                ref.addQueryParameter("retry", "t"); -            } - -            LOG.debug("Redirecting to " + ref); -            response.redirectSeeOther(ref.toString()); -        } -        LOG.debug("Failing authentication."); -        return false; -    } - -    @Override -    protected int authenticated(Request request, Response response) { -        super.authenticated(request, response); - -        Form query = request.getOriginalRef().getQueryAsForm(); -        String redirect = query.getFirstValue("redirect"); -        if (redirect == null || redirect.length() == 0) { -            redirect = mDefaultRedirect; -        } - -        // TODO: Ensure redirect is a relative url. -        LOG.debug("Redirecting to " + redirect); -        response.redirectSeeOther(redirect); - -        return CONTINUE; -    } - -    private boolean isLoginAttempt(Request request) { -        String requestPath = request.getResourceRef().getPath(); -        return request.getMethod() == Method.POST && mLoginPostUrl.equals(requestPath); -    } -} diff --git a/src/com/p4square/grow/frontend/LoginPageResource.java b/src/com/p4square/grow/frontend/LoginPageResource.java deleted file mode 100644 index 38eba07..0000000 --- a/src/com/p4square/grow/frontend/LoginPageResource.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.util.Map; - -import freemarker.template.Template; - -import org.restlet.data.Form; -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.resource.ServerResource; -import org.restlet.representation.Representation; -import org.restlet.ext.freemarker.TemplateRepresentation; - -import org.apache.log4j.Logger; - -import com.p4square.fmfacade.FreeMarkerPageResource; - -/** - * LoginPageResource presents a login page template and processes the response. - * Upon successful authentication, the user is redirected to another page and - * a cookie is set. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class LoginPageResource extends FreeMarkerPageResource { -    private static Logger cLog = Logger.getLogger(LoginPageResource.class); - -    private GrowFrontend mGrowFrontend; - -    private String mErrorMessage; - -    @Override -    public void doInit() { -        super.doInit(); - -        mGrowFrontend = (GrowFrontend) getApplication(); - -        mErrorMessage = null; -    } - -    /** -     * Return the login page. -     */ -    @Override -    protected Representation get() { -        Template t = mGrowFrontend.getTemplate("pages/login.html.ftl"); - -        try { -            if (t == null) { -                setStatus(Status.CLIENT_ERROR_NOT_FOUND); -                return null; -            } - -            Map<String, Object> root = getRootObject(); - -            Form query = getRequest().getOriginalRef().getQueryAsForm(); -            String redirect = query.getFirstValue("redirect"); -            root.put("redirect", redirect); -            String retry = query.getFirstValue("retry"); -            if ("t".equals(retry)) { -                root.put("errorMessage", "Invalid email or password."); -            } - -            return new TemplateRepresentation(t, root, MediaType.TEXT_HTML); - -        } catch (Exception e) { -            cLog.fatal("Could not render page: " + e.getMessage(), e); -            setStatus(Status.SERVER_ERROR_INTERNAL); -            return null; -        } -    } - -} diff --git a/src/com/p4square/grow/frontend/LogoutResource.java b/src/com/p4square/grow/frontend/LogoutResource.java deleted file mode 100644 index e26dcb7..0000000 --- a/src/com/p4square/grow/frontend/LogoutResource.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; -import org.restlet.resource.ServerResource; - -import com.p4square.session.Sessions; - -import com.p4square.grow.config.Config; - -/** - * This Resource removes a user's session and session cookies. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class LogoutResource extends ServerResource { -    private Config mConfig; - -    @Override -    protected void doInit() { -        super.doInit(); - -        GrowFrontend growFrontend = (GrowFrontend) getApplication(); -        mConfig = growFrontend.getConfig(); -    } - -    @Override -    protected Representation get() { -        Sessions.getInstance().delete(getRequest(), getResponse()); - -        String nextPage = mConfig.getString("dynamicRoot", ""); -        nextPage += "/index.html"; -        getResponse().redirectSeeOther(nextPage); -        return new StringRepresentation("Redirecting to " + nextPage); -    } -} diff --git a/src/com/p4square/grow/frontend/NewAccountResource.java b/src/com/p4square/grow/frontend/NewAccountResource.java deleted file mode 100644 index 5c13017..0000000 --- a/src/com/p4square/grow/frontend/NewAccountResource.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.util.Map; - -import com.p4square.f1oauth.FellowshipOneIntegrationDriver; -import freemarker.template.Template; - -import org.restlet.data.Form; -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; -import org.restlet.ext.freemarker.TemplateRepresentation; - -import org.apache.log4j.Logger; - -import com.p4square.f1oauth.F1Access; -import com.p4square.restlet.oauth.OAuthException; - -import com.p4square.fmfacade.FreeMarkerPageResource; - -/** - * This resource creates a new InFellowship account. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class NewAccountResource extends FreeMarkerPageResource { -    private static Logger LOG = Logger.getLogger(NewAccountResource.class); - -    private GrowFrontend mGrowFrontend; -    private F1Access mHelper; - -    private String mErrorMessage; - -    private String mLoginPageUrl; -    private String mVerificationPage; - -    @Override -    public void doInit() { -        super.doInit(); - -        mGrowFrontend = (GrowFrontend) getApplication(); - -        final IntegrationDriver driver = mGrowFrontend.getThirdPartyIntegrationFactory(); -        if (driver instanceof FellowshipOneIntegrationDriver) { -            mHelper = ((FellowshipOneIntegrationDriver) driver).getF1Access(); -        } else { -            LOG.error("NewAccountResource only works with F1!"); -            mHelper = null; -        } - -        mErrorMessage = ""; - -        mLoginPageUrl = mGrowFrontend.getConfig().getString("postAccountCreationPage", -                getRequest().getRootRef().toString()); -        mVerificationPage = mGrowFrontend.getConfig().getString("dynamicRoot", "") -                                + "/verification.html"; -    } - -    /** -     * Return the login page. -     */ -    @Override -    protected Representation get() { -        Template t = mGrowFrontend.getTemplate("pages/newaccount.html.ftl"); - -        try { -            if (t == null) { -                setStatus(Status.CLIENT_ERROR_NOT_FOUND); -                return ErrorPage.TEMPLATE_NOT_FOUND; -            } - -            Map<String, Object> root = getRootObject(); -            if (mErrorMessage.length() > 0) { -                root.put("errorMessage", mErrorMessage); -            } - -            return new TemplateRepresentation(t, root, MediaType.TEXT_HTML); - -        } catch (Exception e) { -            LOG.fatal("Could not render page: " + e.getMessage(), e); -            setStatus(Status.SERVER_ERROR_INTERNAL); -            return ErrorPage.RENDER_ERROR; -        } -    } - -    @Override -    protected Representation post(Representation rep) { -        if (mHelper == null) { -            mErrorMessage += "F1 support is not enabled! "; -            return get(); -        } - -        Form form = new Form(rep); - -        String firstname = form.getFirstValue("firstname"); -        String lastname  = form.getFirstValue("lastname"); -        String email     = form.getFirstValue("email"); - -        if (isEmpty(firstname)) { -            mErrorMessage += "First Name is a required field. "; -        } -        if (isEmpty(lastname)) { -            mErrorMessage += "Last Name is a required field. "; -        } -        if (isEmpty(email)) { -            mErrorMessage += "Email is a required field. "; -        } - -        if (mErrorMessage.length() > 0) { -            return get(); -        } - -        try { -            if (!mHelper.createAccount(firstname, lastname, email, mLoginPageUrl)) { -                mErrorMessage = "An account with that address already exists."; -                return get(); -            } - -            getResponse().redirectSeeOther(mVerificationPage); -            return new StringRepresentation("Redirecting to " + mVerificationPage); - -        } catch (OAuthException e) { -            return new ErrorPage(e.getStatus().getDescription()); -        } -    } - -    private boolean isEmpty(String s) { -        return s == null || s.trim().length() == 0; -    } -} diff --git a/src/com/p4square/grow/frontend/NewBelieverResource.java b/src/com/p4square/grow/frontend/NewBelieverResource.java deleted file mode 100644 index 8fe078a..0000000 --- a/src/com/p4square/grow/frontend/NewBelieverResource.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import freemarker.template.Template; - -import org.restlet.data.CookieSetting; -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.representation.Representation; -import org.restlet.ext.freemarker.TemplateRepresentation; - -import org.apache.log4j.Logger; - -import com.p4square.fmfacade.FreeMarkerPageResource; - -/** - * This resource displays the transitional page between chapters. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class NewBelieverResource extends FreeMarkerPageResource { -    private static final Logger LOG = Logger.getLogger(NewBelieverResource.class); - -    public static final String COOKIE_NAME = "seeker"; - -    private GrowFrontend mGrowFrontend; - -    @Override -    public void doInit() { -        super.doInit(); - -        mGrowFrontend = (GrowFrontend) getApplication(); -    } - -    /** -     * Display the New Believer page. -     * -     * The New Believer page creates a cookie to remember the user, -     * explains what's going on, and then asks the user to go to the login -     * page. -     * -     * When the user hits the {@link AccountRedirectResource} the cookie -     * is read and the user is moved ahead to the training section. -     */ -    @Override -    protected Representation get() { -        Template t = mGrowFrontend.getTemplate("templates/newbeliever.ftl"); - -        try { -            if (t == null) { -                setStatus(Status.CLIENT_ERROR_NOT_FOUND); -                return ErrorPage.TEMPLATE_NOT_FOUND; -            } - -            // Set the new believer cookie -            CookieSetting cookie = new CookieSetting(COOKIE_NAME, "true"); -            cookie.setPath("/"); -            getRequest().getCookies().add(cookie); -            getResponse().getCookieSettings().add(cookie); - -            return new TemplateRepresentation(t, getRootObject(), MediaType.TEXT_HTML); - -        } catch (Exception e) { -            LOG.fatal("Could not render page: " + e.getMessage(), e); -            setStatus(Status.SERVER_ERROR_INTERNAL); -            return ErrorPage.RENDER_ERROR; -        } -    } -} diff --git a/src/com/p4square/grow/frontend/NotFoundException.java b/src/com/p4square/grow/frontend/NotFoundException.java deleted file mode 100644 index dfa2a4c..0000000 --- a/src/com/p4square/grow/frontend/NotFoundException.java +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.io.IOException; - -public class NotFoundException extends IOException { -    public NotFoundException(final String message) { -        super(message); -    } -} diff --git a/src/com/p4square/grow/frontend/ProgressReporter.java b/src/com/p4square/grow/frontend/ProgressReporter.java deleted file mode 100644 index 2f36832..0000000 --- a/src/com/p4square/grow/frontend/ProgressReporter.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.p4square.grow.frontend; - -import org.restlet.security.User; - -import java.util.Date; - -/** - * A ProgressReporter is used to record a User's progress in a Church Management System. - */ -public interface ProgressReporter { - -    /** -     * Report that the User completed the assessment. -     * -     * @param user The user who completed the assessment. -     * @param level The assessment level. -     * @param date The completion date. -     * @param results Result information (e.g. json of the results). -     */ -    void reportAssessmentComplete(User user, String level, Date date, String results); - -    /** -     * Report that the User completed the chapter. -     * -     * @param user The user who completed the chapter. -     * @param chapter The chapter completed. -     * @param date The completion date. -     */ -    void reportChapterComplete(User user, String chapter, Date date); -} diff --git a/src/com/p4square/grow/frontend/SurveyPageResource.java b/src/com/p4square/grow/frontend/SurveyPageResource.java deleted file mode 100644 index 3575fe3..0000000 --- a/src/com/p4square/grow/frontend/SurveyPageResource.java +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.io.IOException; - -import java.util.Map; -import java.util.HashMap; - -import freemarker.template.Template; - -import org.restlet.data.Form; -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.ext.freemarker.TemplateRepresentation; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; -import org.restlet.resource.ServerResource; - -import org.apache.log4j.Logger; - -import com.p4square.fmfacade.json.JsonRequestClient; -import com.p4square.fmfacade.json.JsonResponse; -import com.p4square.fmfacade.json.ClientException; - -import com.p4square.fmfacade.FreeMarkerPageResource; - -import com.p4square.grow.config.Config; -import com.p4square.grow.model.Question; -import com.p4square.grow.model.UserRecord; -import com.p4square.grow.provider.DelegateProvider; -import com.p4square.grow.provider.JsonEncodedProvider; -import com.p4square.grow.provider.Provider; - -/** - * SurveyPageResource handles rendering the survey and processing user's answers. - * - * This resource expects the user to be authenticated and the ClientInfo User object - * to be populated. Each question is requested from the backend along with the - * user's previous answer. Each answer is sent to the backend and the user is redirected - * to the next question. After the last question the user is sent to his results. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class SurveyPageResource extends FreeMarkerPageResource { -    private static final Logger LOG = Logger.getLogger(SurveyPageResource.class); - -    private Config mConfig; -    private Template mSurveyTemplate; -    private JsonRequestClient mJsonClient; -    private Provider<String, Question> mQuestionProvider; -    private Provider<String, UserRecord> mUserRecordProvider; - -    // Fields pertaining to this request. -    private String mQuestionId; -    private String mUserId; - -    @Override -    public void doInit() { -        super.doInit(); - -        GrowFrontend growFrontend = (GrowFrontend) getApplication(); -        mConfig = growFrontend.getConfig(); -        mSurveyTemplate = growFrontend.getTemplate("templates/survey.ftl"); -        if (mSurveyTemplate == null) { -            LOG.fatal("Could not find survey template."); -            setStatus(Status.SERVER_ERROR_INTERNAL); -        } - -        mJsonClient = new JsonRequestClient(getContext().getClientDispatcher()); -        mQuestionProvider = new DelegateProvider<String, String, Question>( -                new JsonRequestProvider<Question>(getContext().getClientDispatcher(), -                    Question.class)) { -            @Override -            public String makeKey(String questionId) { -                return getBackendEndpoint() + "/assessment/question/" + questionId; -            } -        }; - -        mUserRecordProvider = new DelegateProvider<String, String, UserRecord>( -                new JsonRequestProvider<UserRecord>(getContext().getClientDispatcher(), -                    UserRecord.class)) { -            @Override -            public String makeKey(String userid) { -                return getBackendEndpoint() + "/accounts/" + userid; -            } -        }; - -        mQuestionId = getAttribute("questionId"); -        mUserId = getRequest().getClientInfo().getUser().getIdentifier(); -    } - -    /** -     * Return a page with a survey question. -     */ -    @Override -    protected Representation get() { -        try { -            // Get the current question. -            if (mQuestionId == null) { -                // Get user's current question -                mQuestionId = getCurrentQuestionId(); - -                if (mQuestionId != null) { -                    Question lastQuestion = getQuestion(mQuestionId); -                    return redirectToNextQuestion(lastQuestion, getAnswer(mQuestionId)); -                } -            } - -            // If we don't have a current question, get the first one. -            if (mQuestionId == null) { -                mQuestionId = "first"; -            } - -            Question question = getQuestion(mQuestionId); -            if (question == null) { -                setStatus(Status.CLIENT_ERROR_NOT_FOUND); -                return new ErrorPage("Could not find the question."); -            } - -            // Set the real question id if a meta-id was used (i.e. first) -            mQuestionId = question.getId(); - -            // Get any previous answer to the question -            String selectedAnswer = getAnswer(mQuestionId); - -            Map root = getRootObject(); -            root.put("question", question); -            root.put("selectedAnswerId", selectedAnswer); - -            // Get the question count and compute progress -            { -                JsonResponse response = backendGet("/assessment/question/count"); -                if (response.getStatus().isSuccess()) { -                    Map countData = response.getMap(); -                    if (countData != null) { -                        response = backendGet("/accounts/" + mUserId + "/assessment"); -                        if (response.getStatus().isSuccess()) { -                            Integer completed = (Integer) response.getMap().get("totalAnswers"); -                            Integer total = (Integer) countData.get("count"); - -                            if (completed != null && total != null && total != 0) { -                                root.put("percentComplete", String.valueOf((int) (100.0 * completed) / total)); -                            } -                        } -                    } -                } -            } - -            return new TemplateRepresentation(mSurveyTemplate, root, MediaType.TEXT_HTML); - -        } catch (Exception e) { -            LOG.fatal("Could not render page: " + e.getMessage(), e); -            setStatus(Status.SERVER_ERROR_INTERNAL); -            return ErrorPage.RENDER_ERROR; -        } -    } - -    /** -     * Record a survey answer and redirect to the next question. -     */ -    @Override -    protected Representation post(Representation entity) { -        final Form form = new Form(entity); -        final String answerId = form.getFirstValue("answer"); -        final String direction = form.getFirstValue("direction"); -        boolean justGoBack = false; // FIXME: Ugly hack - -        if (mQuestionId == null || answerId == null || answerId.length() == 0) { -            if ("previous".equals(direction)) { -                // Just go back -                justGoBack = true; - -            } else { -                // Something is wrong. -                setStatus(Status.CLIENT_ERROR_BAD_REQUEST); -                return new ErrorPage("Question or answer messing."); -            } -        } - -        try { -            // Find the question -            Question question = getQuestion(mQuestionId); -            if (question == null) { -                // User is answering a question which doesn't exist -                setStatus(Status.CLIENT_ERROR_NOT_FOUND); -                return new ErrorPage("Question not found."); -            } - -            // Store answer -            if (!justGoBack) { -                Map<String, String> answer = new HashMap<String, String>(); -                answer.put("answerId", answerId); -                JsonResponse response = backendPut("/accounts/" + mUserId + -                        "/assessment/answers/" + mQuestionId, answer); - -                if (!response.getStatus().isSuccess()) { -                    // Something went wrong talking to the backend, error out. -                    LOG.fatal("Error recording survey answer " + response.getStatus()); -                    setStatus(Status.SERVER_ERROR_INTERNAL); -                    return ErrorPage.BACKEND_ERROR; -                } -            } - -            // Find the next question or finish the assessment. -            if ("previous".equals(direction)) { -                return redirectToPreviousQuestion(question); - -            } else { -                return redirectToNextQuestion(question, answerId); -            } - -        } catch (Exception e) { -            LOG.fatal("Could not render page: " + e.getMessage(), e); -            setStatus(Status.SERVER_ERROR_INTERNAL); -            return ErrorPage.RENDER_ERROR; -        } -    } - -    private Question getQuestion(String id) { -        try { -            return mQuestionProvider.get(id); - -        } catch (IOException e) { -            LOG.warn("Error fetching question.", e); -            return null; -        } -    } - -    private String getAnswer(String questionId) { -        try { -            JsonResponse response = backendGet("/accounts/" + mUserId + "/assessment/answers/" + questionId); -            if (response.getStatus().isSuccess()) { -                return (String) response.getMap().get("answerId"); -            } - -        } catch (ClientException e) { -            LOG.warn("Error fetching answer to question " + questionId, e); -        } - -        return null; -    } - -    private Representation redirectToNextQuestion(Question question, String answerid) { -        String nextQuestionId = question.getNextQuestion(answerid); - -        if (nextQuestionId == null) { -            // Just finished the last question. Update the user's account -            try { -                UserRecord account = null; -                try { -                    account = mUserRecordProvider.get(mUserId); -                } catch (NotFoundException e) { -                    // User record doesn't exist, so create a new one. -                    account = new UserRecord(getRequest().getClientInfo().getUser()); -                } -                account.setLanding("training"); -                mUserRecordProvider.put(mUserId, account); -            } catch (IOException e) { -                LOG.warn("IOException updating landing for " + mUserId, e); -            } - -            String nextPage = mConfig.getString("dynamicRoot", ""); -            nextPage += "/account/assessment/results"; -            getResponse().redirectSeeOther(nextPage); -            return new StringRepresentation("Redirecting to " + nextPage); -        } - -        return redirectToQuestion(nextQuestionId); -    } - -    private Representation redirectToPreviousQuestion(Question question) { -        String nextQuestionId = question.getPreviousQuestion(); - -        if (nextQuestionId == null) { -            nextQuestionId = (String) question.getId(); -        } - -        return redirectToQuestion(nextQuestionId); -    } - -    private Representation redirectToQuestion(String id) { -        String nextPage = mConfig.getString("dynamicRoot", ""); -        nextPage += "/account/assessment/question/" + id; -        getResponse().redirectSeeOther(nextPage); -        return new StringRepresentation("Redirecting to " + nextPage); -    } - -    private String getCurrentQuestionId() { -        String id = null; -        try { -            JsonResponse response = backendGet("/accounts/" + mUserId + "/assessment"); - -            if (response.getStatus().isSuccess()) { -                return (String) response.getMap().get("lastAnswered"); - -            } else { -                LOG.warn("Failed to get assessment results: " + response.getStatus()); -            } - -        } catch (ClientException e) { -            LOG.error("Exception getting assessment results.", e); -        } - -        return null; -    } - -    /** -     * @return The backend endpoint URI -     */ -    private String getBackendEndpoint() { -        return mConfig.getString("backendUri", "riap://component/backend"); -    } - -    /** -     * Helper method to send a GET to the backend. -     */ -    private JsonResponse backendGet(final String uri) { -        LOG.debug("Sending backend GET " + uri); - -        final JsonResponse response = mJsonClient.get(getBackendEndpoint() + uri); -        final Status status = response.getStatus(); -        if (!status.isSuccess() && !Status.CLIENT_ERROR_NOT_FOUND.equals(status)) { -            LOG.warn("Error making backend request for '" + uri + "'. status = " + response.getStatus().toString()); -        } - -        return response; -    } - -    protected JsonResponse backendPut(final String uri, final Map data) { -        LOG.debug("Sending backend PUT " + uri); - -        final JsonResponse response = mJsonClient.put(getBackendEndpoint() + uri, data); -        final Status status = response.getStatus(); -        if (!status.isSuccess() && !Status.CLIENT_ERROR_NOT_FOUND.equals(status)) { -            LOG.warn("Error making backend request for '" + uri + "'. status = " + response.getStatus().toString()); -        } - -        return response; -    } -} diff --git a/src/com/p4square/grow/frontend/TrainingPageResource.java b/src/com/p4square/grow/frontend/TrainingPageResource.java deleted file mode 100644 index a1e7789..0000000 --- a/src/com/p4square/grow/frontend/TrainingPageResource.java +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import freemarker.template.Template; - -import org.restlet.data.CookieSetting; -import org.restlet.data.Form; -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.ext.freemarker.TemplateRepresentation; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; -import org.restlet.resource.ServerResource; - -import org.apache.log4j.Logger; - -import com.p4square.fmfacade.json.JsonRequestClient; -import com.p4square.fmfacade.json.JsonResponse; - -import com.p4square.fmfacade.FreeMarkerPageResource; - -import com.p4square.grow.config.Config; -import com.p4square.grow.model.TrainingRecord; -import com.p4square.grow.model.VideoRecord; -import com.p4square.grow.model.Playlist; -import com.p4square.grow.provider.TrainingRecordProvider; -import com.p4square.grow.provider.Provider; - -/** - * TrainingPageResource handles rendering the training page. - * - * This resource expects the user to be authenticated and the ClientInfo User object - * to be populated. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class TrainingPageResource extends FreeMarkerPageResource { -    private static final Logger LOG = Logger.getLogger(TrainingPageResource.class); - -    private static final String[] CHAPTERS = { "introduction", "seeker", "believer", "disciple", "teacher", "leader" }; -    private static final Comparator<Map<String, Object>> VIDEO_COMPARATOR = new Comparator<Map<String, Object>>() { -        @Override -        public int compare(Map<String, Object> left, Map<String, Object> right) { -            String leftNumberStr = (String) left.get("number"); -            String rightNumberStr = (String) right.get("number"); - -            if (leftNumberStr == null || rightNumberStr == null) { -                return -1; -            } - -            double leftNumber = Double.valueOf(leftNumberStr); -            double rightNumber = Double.valueOf(rightNumberStr); - -            return Double.compare(leftNumber, rightNumber); -        } -    }; - -    private Config mConfig; -    private Template mTrainingTemplate; -    private JsonRequestClient mJsonClient; - -    private Provider<String, TrainingRecord> mTrainingRecordProvider; -    private FeedData mFeedData; - -    // Fields pertaining to this request. -    protected String mChapter; -    protected String mUserId; - -    @Override -    public void doInit() { -        super.doInit(); - -        GrowFrontend growFrontend = (GrowFrontend) getApplication(); -        mConfig = growFrontend.getConfig(); -        mTrainingTemplate = growFrontend.getTemplate("templates/training.ftl"); -        if (mTrainingTemplate == null) { -            LOG.fatal("Could not find training template."); -            setStatus(Status.SERVER_ERROR_INTERNAL); -        } - -        mJsonClient = new JsonRequestClient(getContext().getClientDispatcher()); -        mTrainingRecordProvider = new TrainingRecordProvider<String>(new JsonRequestProvider<TrainingRecord>(getContext().getClientDispatcher(), TrainingRecord.class)) { -            @Override -            public String makeKey(String userid) { -                return getBackendEndpoint() + "/accounts/" + userid + "/training"; -            } -        }; - -        mFeedData = new FeedData(getContext(), mConfig); - -        mChapter = getAttribute("chapter"); -        mUserId = getRequest().getClientInfo().getUser().getIdentifier(); -    } - -    /** -     * Return a page of videos. -     */ -    @Override -    protected Representation get() { -        try { -            // Get the training summary -            TrainingRecord trainingRecord = mTrainingRecordProvider.get(mUserId); -            if (trainingRecord == null) { -                setStatus(Status.SERVER_ERROR_INTERNAL); -                return new ErrorPage("Could not retrieve TrainingRecord."); -            } - -            Playlist playlist = trainingRecord.getPlaylist(); -            Map<String, Boolean> chapters = playlist.getChapterStatuses(); -            Map<String, Boolean> allowedChapters = new LinkedHashMap<String, Boolean>(); - -            // The user is not allowed to view chapters after his highest completed chapter. -            // In this loop we find which chapters are allowed and check if the user tried -            // to skip ahead. -            boolean allowUserToSkip = mConfig.getBoolean("allowUserToSkip", false) || getQueryValue("magicskip") != null; -            String defaultChapter = null; -            boolean userTriedToSkip = false; -            int overallProgress = 0; - -            boolean foundRequired = false; -            for (String chapterId : getChaptersInOrder()) { -                boolean allowed = true; - -                Boolean completed = chapters.get(chapterId); -                if (completed != null) { -                    if (!foundRequired) { -                       if (!completed) { -                            // The first incomplete chapter is the highest allowed chapter. -                            foundRequired = true; -                            defaultChapter = chapterId; -                       } - -                    } else { -                        allowed = allowUserToSkip; - -                        if (!allowUserToSkip && chapterId.equals(mChapter)) { -                            userTriedToSkip = true; -                        } -                    } - -                    allowedChapters.put(chapterId, allowed); - -                    if (completed) { -                        overallProgress++; -                    } -                } -            } - -            // Overall progress is the percentage of chapters complete -            overallProgress = (int) ((double) overallProgress / getChaptersInOrder().length * 100); - -            if (defaultChapter == null) { -                // Everything is completed... send them back to introduction. -                defaultChapter = "introduction"; -            } - -            if (mChapter == null || userTriedToSkip) { -                // No chapter was specified or the user tried to skip ahead. -                // Either case, redirect. -                String nextPage = mConfig.getString("dynamicRoot", ""); -                nextPage += "/account/training/" + defaultChapter; -                getResponse().redirectSeeOther(nextPage); -                return new StringRepresentation("Redirecting to " + nextPage); -            } - - -            // Get videos for the chapter. -            List<Map<String, Object>> videos = null; -            { -                JsonResponse response = backendGet("/training/" + mChapter); -                if (!response.getStatus().isSuccess()) { -                    setStatus(Status.CLIENT_ERROR_NOT_FOUND); -                    return null; -                } -                videos = (List<Map<String, Object>>) response.getMap().get("videos"); -                Collections.sort(videos, VIDEO_COMPARATOR); -            } - -            // Mark the completed videos as completed -            int chapterProgress = 0; -            for (Map<String, Object> video : videos) { -                boolean completed = false; -                VideoRecord record = playlist.find((String) video.get("id")); -                LOG.info("VideoId: " + video.get("id")); -                if (record != null) { -                    LOG.info("VideoRecord: " + record.getComplete()); -                    completed = record.getComplete(); -                } -                video.put("completed", completed); - -                if (completed) { -                    chapterProgress++; -                } -            } -            chapterProgress = chapterProgress * 100 / videos.size(); - -            Map root = getRootObject(); -            root.put("chapter", mChapter); -            root.put("chapters", allowedChapters.keySet()); -            root.put("isChapterAllowed", allowedChapters); -            root.put("chapterProgress", chapterProgress); -            root.put("overallProgress", overallProgress); -            root.put("videos", videos); -            root.put("allowUserToSkip", allowUserToSkip); - -            // Determine if we should show the feed. -            boolean showfeed = true; - -            // Don't show the feed if the topic isn't allowed. -            if (!FeedData.TOPICS.contains(mChapter)) { -                showfeed = false; -            } - -            root.put("showfeed", showfeed); -            if (showfeed) { -                root.put("feeddata", mFeedData); -            } - -            return new TemplateRepresentation(mTrainingTemplate, root, MediaType.TEXT_HTML); - -        } catch (Exception e) { -            LOG.fatal("Could not render page: " + e.getMessage(), e); -            setStatus(Status.SERVER_ERROR_INTERNAL); -            return ErrorPage.RENDER_ERROR; -        } -    } - -    /** -     * This method returns a list of chapters in the correct order. -     */ -    protected String[] getChaptersInOrder() { -        return CHAPTERS; -    } - -    /** -     * @return The backend endpoint URI -     */ -    private String getBackendEndpoint() { -        return mConfig.getString("backendUri", "riap://component/backend"); -    } - -    /** -     * Helper method to send a GET to the backend. -     */ -    private JsonResponse backendGet(final String uri) { -        LOG.debug("Sending backend GET " + uri); - -        final JsonResponse response = mJsonClient.get(getBackendEndpoint() + uri); -        final Status status = response.getStatus(); -        if (!status.isSuccess() && !Status.CLIENT_ERROR_NOT_FOUND.equals(status)) { -            LOG.warn("Error making backend request for '" + uri + "'. status = " + response.getStatus().toString()); -        } - -        return response; -    } - -} diff --git a/src/com/p4square/grow/frontend/VideosResource.java b/src/com/p4square/grow/frontend/VideosResource.java deleted file mode 100644 index 2099a77..0000000 --- a/src/com/p4square/grow/frontend/VideosResource.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.util.HashMap; -import java.util.Map; - -import freemarker.template.Template; - -import org.restlet.data.Form; -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.ext.jackson.JacksonRepresentation; -import org.restlet.representation.Representation; -import org.restlet.resource.ServerResource; - -import org.apache.log4j.Logger; - -import com.p4square.fmfacade.json.JsonRequestClient; -import com.p4square.fmfacade.json.JsonResponse; - -import com.p4square.grow.config.Config; - -/** - * VideosResource returns JSON blobs with video information and records watched - * videos. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class VideosResource extends ServerResource { -    private static Logger cLog = Logger.getLogger(VideosResource.class); - -    private Config mConfig; -    private JsonRequestClient mJsonClient; - -    // Fields pertaining to this request. -    private String mChapter; -    private String mVideoId; -    private String mUserId; - -    @Override -    public void doInit() { -        super.doInit(); - -        GrowFrontend growFrontend = (GrowFrontend) getApplication(); -        mConfig = growFrontend.getConfig(); - -        mJsonClient = new JsonRequestClient(getContext().getClientDispatcher()); - -        mChapter = getAttribute("chapter"); -        mVideoId = getAttribute("videoId"); -        mUserId = getRequest().getClientInfo().getUser().getIdentifier(); -    } - -    /** -     * Fetch a video record from the backend. -     */ -    @Override -    protected Representation get() { -        try { -            JsonResponse response = backendGet("/training/" + mChapter + "/videos/" + mVideoId); - -            if (response.getStatus().isSuccess()) { -                return new JacksonRepresentation<Map>(response.getMap()); - -            } else { -                setStatus(response.getStatus()); -                return null; -            } - -        } catch (Exception e) { -            cLog.fatal("Could not render page: " + e.getMessage(), e); -            setStatus(Status.SERVER_ERROR_INTERNAL); -            return null; -        } -    } - -    /** -     * Mark a video as completed. -     */ -    @Override -    protected Representation post(Representation entity) { -        Map<String, Object> data = new HashMap<String, Object>(); -        data.put("complete", "true"); -        JsonResponse response = backendPut("/accounts/" + mUserId + "/training/videos/" + mVideoId, data); - -        if (!response.getStatus().isSuccess()) { -            // Something went wrong talking to the backend, error out. -            cLog.fatal("Error recording completed video " + response.getStatus()); -            setStatus(Status.SERVER_ERROR_INTERNAL); -            return ErrorPage.BACKEND_ERROR; -        } - -        setStatus(Status.SUCCESS_NO_CONTENT); -        return null; -    } - -    /** -     * @return The backend endpoint URI -     */ -    private String getBackendEndpoint() { -        return mConfig.getString("backendUri", "riap://component/backend"); -    } - -    /** -     * Helper method to send a GET to the backend. -     */ -    private JsonResponse backendGet(final String uri) { -        cLog.debug("Sending backend GET " + uri); - -        final JsonResponse response = mJsonClient.get(getBackendEndpoint() + uri); -        final Status status = response.getStatus(); -        if (!status.isSuccess() && !Status.CLIENT_ERROR_NOT_FOUND.equals(status)) { -            cLog.warn("Error making backend request for '" + uri + "'. status = " + response.getStatus().toString()); -        } - -        return response; -    } - -    private JsonResponse backendPut(final String uri, final Map data) { -        cLog.debug("Sending backend PUT " + uri); - -        final JsonResponse response = mJsonClient.put(getBackendEndpoint() + uri, data); -        final Status status = response.getStatus(); -        if (!status.isSuccess() && !Status.CLIENT_ERROR_NOT_FOUND.equals(status)) { -            cLog.warn("Error making backend request for '" + uri + "'. status = " + response.getStatus().toString()); -        } - -        return response; -    } -} diff --git a/src/com/p4square/grow/model/Answer.java b/src/com/p4square/grow/model/Answer.java deleted file mode 100644 index a818365..0000000 --- a/src/com/p4square/grow/model/Answer.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -import org.apache.log4j.Logger; - -/** - * This is the model of an assessment question's answer. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class Answer { -    private static final Logger LOG = Logger.getLogger(Answer.class); - -    /** -     * ScoreType determines how the answer will be scored. -     * -     */ -    public static enum ScoreType { -        /** -         * This question has no effect on the score. -         */ -        NONE, - -        /** -         * The score of this question is part of the average. -         */ -        AVERAGE, - -        /** -         * The score of this question is the total score, no other questions -         * matter after this point. -         */ -        TRUMP; - -        @Override -        public String toString() { -            return name().toLowerCase(); -        } -    } - -    private String mAnswerText; -    private ScoreType mType; -    private float mScoreFactor; -    private String mNextQuestionId; - -    public Answer() { -        mType = ScoreType.AVERAGE; -    } - -    /** -     * @return The text associated with the answer. -     */ -    public String getText() { -        return mAnswerText; -    } - -    /** -     * Set the text associated with the answer. -     * @param text The new text. -     */ -    public void setText(String text) { -        mAnswerText = text; -    } - -    /** -     * @return the ScoreType for the Answer. -     */ -    public ScoreType getType() { -        return mType; -    } - -    /** -     * Set the ScoreType for the answer. -     * @param type The new ScoreType. -     */ -    public void setType(ScoreType type) { -        mType = type; -    } - -    /** -     * @return the delta of the score if this answer is selected. -     */ -    public float getScore() { -        if (mType == ScoreType.NONE) { -            return 0; -        } - -        return mScoreFactor; -    } - -    /** -     * Set the score delta for this answer. -     * @param score The new delta. -     */ -    public void setScore(float score) { -        mScoreFactor = score; -    } - -    /** -     * @return the id of the next question if this answer is selected, or null -     *         if selecting this answer has no effect. -     */ -    public String getNextQuestion() { -        return mNextQuestionId; -    } - -    /** -     * Set the id of the next question when this answer is selected. -     * @param id The next question id or null to proceed as usual. -     */ -    public void setNextQuestion(String id) { -        mNextQuestionId = id; -    } - -    /** -     * Adjust the running score for the selection of this answer. -     * @param score The running score to adjust. -     * @return true if scoring should continue, false if this answer trumps all. -     */ -    public boolean score(final Score score) { -        switch (getType()) { -            case TRUMP: -                score.sum = getScore(); -                score.count = 1; -                return false; // Quit scoring. - -            case AVERAGE: -                LOG.debug("ScoreType.AVERAGE: { delta: \"" + getScore() + "\" }"); -                score.sum += getScore(); -                score.count++; -                break; - -            case NONE: -                break; -        } - -        return true; // Continue scoring -    } -} diff --git a/src/com/p4square/grow/model/Banner.java b/src/com/p4square/grow/model/Banner.java deleted file mode 100644 index b786b36..0000000 --- a/src/com/p4square/grow/model/Banner.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.model; - -/** - * Page Banner Data. - */ -public class Banner { -     private String mHtml; - -     public String getHtml() { -        return mHtml; -     } - -     public void setHtml(final String html) { -         mHtml = html; -     } -} diff --git a/src/com/p4square/grow/model/Chapter.java b/src/com/p4square/grow/model/Chapter.java deleted file mode 100644 index 3a08e4c..0000000 --- a/src/com/p4square/grow/model/Chapter.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -import java.util.HashMap; -import java.util.Map; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnore; - -/** - * Chapter is a list of VideoRecords in a Playlist. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class Chapter implements Cloneable { -    private String mName; -    private Map<String, VideoRecord> mVideos; - -    public Chapter(String name) { -        mName = name; -        mVideos = new HashMap<String, VideoRecord>(); -    } - -    /** -     * Private constructor for JSON decoding. -     */ -    private Chapter() { -        this(null); -    } - -    /** -     * @return The Chapter name. -     */ -    public String getName() { -        return mName; -    } - -    /** -     * Set the chapter name. -     * -     * @param name The name of the chapter. -     */ -    public void setName(final String name) { -        mName = name; -    } - -    /** -     * @return The VideoRecord for videoid or null if videoid is not in the chapter. -     */ -    public VideoRecord getVideoRecord(String videoid) { -        return mVideos.get(videoid); -    } - -    /** -     * @return A map of video ids to VideoRecords. -     */ -    @JsonAnyGetter -    public Map<String, VideoRecord> getVideos() { -        return mVideos; -    } - -    /** -     * Set the VideoRecord for a video id. -     * @param videoId the video id. -     * @param video the VideoRecord. -     */ -    @JsonAnySetter -    public void setVideoRecord(String videoId, VideoRecord video) { -        mVideos.put(videoId, video); -    } - -    /** -     * Remove the VideoRecord for a video id. -     * @param videoId The id to remove. -     */ -    public void removeVideoRecord(String videoId) { -        mVideos.remove(videoId); -    } - -    /** -     * @return true if every required video has been completed. -     */ -    @JsonIgnore -    public boolean isComplete() { -        boolean complete = true; - -        for (VideoRecord r : mVideos.values()) { -            if (r.getRequired() && !r.getComplete()) { -                return false; -            } -        } - -        return complete; -    } - -    /** -     * Deeply clone a chapter. -     * -     * @return a new Chapter object identical but independent of this one. -     */ -    public Chapter clone() throws CloneNotSupportedException { -        Chapter c = new Chapter(mName); -        for (Map.Entry<String, VideoRecord> videoEntry : mVideos.entrySet()) { -            c.setVideoRecord(videoEntry.getKey(), videoEntry.getValue().clone()); -        } -        return c; -    } -} diff --git a/src/com/p4square/grow/model/CircleQuestion.java b/src/com/p4square/grow/model/CircleQuestion.java deleted file mode 100644 index 71acc14..0000000 --- a/src/com/p4square/grow/model/CircleQuestion.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -/** - * Circle Question. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class CircleQuestion extends Question { -    private static final ScoringEngine ENGINE = new QuadScoringEngine(); - -    private String mTopLeft; -    private String mTopRight; -    private String mBottomLeft; -    private String mBottomRight; - -    /** -     * @return the Top Left label. -     */ -    public String getTopLeft() { -        return mTopLeft; -    } - -    /** -     * Set the Top Left label. -     * @param s The new top left label. -     */ -    public void setTopLeft(String s) { -        mTopLeft = s; -    } - -    /** -     * @return the Top Right label. -     */ -    public String getTopRight() { -        return mTopRight; -    } - -    /** -     * Set the Top Right label. -     * @param s The new top left label. -     */ -    public void setTopRight(String s) { -        mTopRight = s; -    } - -    /** -     * @return the Bottom Left label. -     */ -    public String getBottomLeft() { -        return mBottomLeft; -    } - -    /** -     * Set the Bottom Left label. -     * @param s The new top left label. -     */ -    public void setBottomLeft(String s) { -        mBottomLeft = s; -    } - -    /** -     * @return the Bottom Right label. -     */ -    public String getBottomRight() { -        return mBottomRight; -    } - -    /** -     * Set the Bottom Right label. -     * @param s The new top left label. -     */ -    public void setBottomRight(String s) { -        mBottomRight = s; -    } - -    @Override -    public boolean scoreAnswer(Score score, RecordedAnswer answer) { -        return ENGINE.scoreAnswer(score, this, answer); -    } - -    @Override -    public QuestionType getType() { -        return QuestionType.CIRCLE; -    } -} diff --git a/src/com/p4square/grow/model/ImageQuestion.java b/src/com/p4square/grow/model/ImageQuestion.java deleted file mode 100644 index d94c32c..0000000 --- a/src/com/p4square/grow/model/ImageQuestion.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -/** - * Image Question. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class ImageQuestion extends Question { -    private static final ScoringEngine ENGINE = new SimpleScoringEngine(); - -    @Override -    public boolean scoreAnswer(Score score, RecordedAnswer answer) { -        return ENGINE.scoreAnswer(score, this, answer); -    } - -    @Override -    public QuestionType getType() { -        return QuestionType.IMAGE; -    } -} diff --git a/src/com/p4square/grow/model/Message.java b/src/com/p4square/grow/model/Message.java deleted file mode 100644 index 9d33320..0000000 --- a/src/com/p4square/grow/model/Message.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -import java.util.Date; -import java.util.UUID; - -/** - * A feed message. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class Message { -    private String mThreadId; -    private String mId; -    private UserRecord mAuthor; -    private Date mCreated; -    private String mMessage; - -    /** -     * @return a new message id. -     */ -    public static String generateId() { -        return String.format("%x-%s", System.currentTimeMillis(), UUID.randomUUID().toString()); -    } - -    /** -     * @return The id of the thread that the message belongs to. -     */ -    public String getThreadId() { -        return mThreadId; -    } - -    /** -     * Set the id of the thread that the message belongs to. -     * @param id The new thread id. -     */ -    public void setThreadId(String id) { -        mThreadId = id; -    } - -    /** -     * @return The id the message. -     */ -    public String getId() { -        return mId; -    } - -    /** -     * Set the id of the message. -     * @param id The new message id. -     */ -    public void setId(String id) { -        mId = id; -    } - -    /** -     * @return The author of the message. -     */ -    public UserRecord getAuthor() { -        return mAuthor; -    } - -    /** -     * Set the author of the message. -     * @param author The new author. -     */ -    public void setAuthor(UserRecord author) { -        mAuthor = author; -    } - -    /** -     * @return The Date the message was created. -     */ -    public Date getCreated() { -        return mCreated; -    } - -    /** -     * Set the Date the message was created. -     * @param date The new creation date. -     */ -    public void setCreated(Date date) { -        mCreated = date; -    } - -    /** -     * @return The message text. -     */ -    public String getMessage() { -        return mMessage; -    } - -    /** -     * Set the message text. -     * @param text The message text. -     */ -    public void setMessage(String text) { -        mMessage = text; -    } -} diff --git a/src/com/p4square/grow/model/MessageThread.java b/src/com/p4square/grow/model/MessageThread.java deleted file mode 100644 index 9542a18..0000000 --- a/src/com/p4square/grow/model/MessageThread.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -import java.util.UUID; - -/** - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class MessageThread { -    private String mId; -    private Message mMessage; - -    /** -     * Create a new thread with a probably unique id. -     * -     * @return the new thread. -     */ -    public static MessageThread createNew() { -        MessageThread t = new MessageThread(); -        // IDs are keyed to sort lexicographically from latest to oldest. -        t.setId(String.format("%016x-%s", Long.MAX_VALUE - System.currentTimeMillis(), -                    UUID.randomUUID().toString())); - -        return t; -    } - -    /** -     * @return The id the message. -     */ -    public String getId() { -        return mId; -    } - -    /** -     * Set the id of the message. -     * @param id The new message id. -     */ -    public void setId(String id) { -        mId = id; -    } - -    /** -     * @return The original message. -     */ -    public Message getMessage() { -        return mMessage; -    } - -    /** -     * Set the original message. -     * @param id The new message. -     */ -    public void setMessage(Message message) { -        mMessage = message; -    } -} diff --git a/src/com/p4square/grow/model/Playlist.java b/src/com/p4square/grow/model/Playlist.java deleted file mode 100644 index 3e77ada..0000000 --- a/src/com/p4square/grow/model/Playlist.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -import java.util.Date; -import java.util.HashMap; -import java.util.Map; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnore; - -/** - * Representation of a user's playlist. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class Playlist { -    /** -     * Map of Chapter ID to map of Video ID to VideoRecord. -     */ -    private Map<String, Chapter> mPlaylist; - -    private Date mLastUpdated; - -    /** -     * Construct an empty playlist. -     */ -    public Playlist() { -        mPlaylist = new HashMap<String, Chapter>(); -        mLastUpdated = new Date(0); // Default to a prehistoric date if we don't have one. -    } - -    /** -     * Find the VideoRecord for a video id. -     */ -    public VideoRecord find(String videoId) { -        for (Chapter chapter : mPlaylist.values()) { -            VideoRecord r = chapter.getVideoRecord(videoId); - -            if (r != null) { -                return r; -            } -        } - -        return null; -    } - -    /** -     * @param videoId The video to search for. -     * @return the Chapter containing videoId. -     */ -    private Chapter findChapter(String videoId) { -        for (Chapter chapter : mPlaylist.values()) { -            VideoRecord r = chapter.getVideoRecord(videoId); - -            if (r != null) { -                return chapter; -            } -        } - -        return null; -    } - -    /** -     * @return The last modified date of the source playlist. -     */ -    public Date getLastUpdated() { -        return mLastUpdated; -    } - -    /** -     * Set the last updated date. -     * @param date the new last updated date. -     */ -    public void setLastUpdated(Date date) { -        mLastUpdated = date; -    } - -    /** -     * Add a video to the playlist. -     */ -    public VideoRecord add(String chapterId, String videoId) { -        Chapter chapter = mPlaylist.get(chapterId); - -        if (chapter == null) { -            chapter = new Chapter(chapterId); -            mPlaylist.put(chapterId, chapter); -        } - -        VideoRecord r = new VideoRecord(); -        chapter.setVideoRecord(videoId, r); -        return r; -    } - -    /** -     * Add a Chapter to the Playlist. -     * @param chapterId The name of the chapter. -     * @param chapter The Chapter object to add. -     */ -    @JsonAnySetter -    public void addChapter(String chapterId, Chapter chapter) { -        chapter.setName(chapterId); -        mPlaylist.put(chapterId, chapter); -    } - -    /** -     * @return a map of chapter id to chapter. -     */ -    @JsonAnyGetter -    public Map<String, Chapter> getChaptersMap() { -        return mPlaylist; -    } - -    /** -     * @return The last chapter to be completed. -     */ -    @JsonIgnore -    public Map<String, Boolean> getChapterStatuses() { -        Map<String, Boolean> completed = new HashMap<String, Boolean>(); - -        for (Map.Entry<String, Chapter> entry : mPlaylist.entrySet()) { -            completed.put(entry.getKey(), entry.getValue().isComplete()); -        } - -        return completed; -    } - -    /** -     * @return true if all required videos in the chapter have been watched. -     */ -    public boolean isChapterComplete(String chapterId) { -        Chapter chapter = mPlaylist.get(chapterId); -        if (chapter != null) { -            return chapter.isComplete(); -        } - -        return false; -    } - -    /** -     * Merge a playlist into this playlist. -     * -     * Merge is accomplished by adding all missing Chapters and VideoRecords to -     * this playlist. -     */ -    public void merge(Playlist source) { -        if (source.getLastUpdated().before(mLastUpdated)) { -            // Already up to date. -            return; -        } - -        for (Map.Entry<String, Chapter> entry : source.getChaptersMap().entrySet()) { -            String chapterName = entry.getKey(); -            Chapter theirChapter = entry.getValue(); -            Chapter myChapter = mPlaylist.get(entry.getKey()); - -            if (myChapter == null) { -                // Add new chapter -                myChapter = new Chapter(chapterName); -                addChapter(chapterName, myChapter); -            } - -            // Check chapter for missing videos -            for (Map.Entry<String, VideoRecord> videoEntry : theirChapter.getVideos().entrySet()) { -                String videoId = videoEntry.getKey(); -                VideoRecord myVideo = myChapter.getVideoRecord(videoId); - -                if (myVideo == null) { -                    myVideo = find(videoId); -                    if (myVideo == null) { -                        // New Video -                        try { -                            myVideo = videoEntry.getValue().clone(); -                            myChapter.setVideoRecord(videoId, myVideo); -                        } catch (CloneNotSupportedException e) { -                            throw new RuntimeException(e); // Unexpected... -                        } -                    } else { -                        // Video moved -                        findChapter(videoId).removeVideoRecord(videoId); -                        myChapter.setVideoRecord(videoId, myVideo); -                    } -                } -            } -        } - -        mLastUpdated = source.getLastUpdated(); -    } -} diff --git a/src/com/p4square/grow/model/Point.java b/src/com/p4square/grow/model/Point.java deleted file mode 100644 index e9fc0ca..0000000 --- a/src/com/p4square/grow/model/Point.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -/** - * Simple double based point class. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class Point { -    /** -     * Parse a comma separated x,y pair into a point. -     * -     * @return The point represented by the string. -     * @throws IllegalArgumentException if the input is malformed. -     */ -    public static Point valueOf(String str) { -        final int comma = str.indexOf(','); -        if (comma == -1 || comma == 0 || comma == str.length() - 1) { -            throw new IllegalArgumentException("Malformed point string"); -        } - -        final String sX = str.substring(0, comma); -        final String sY = str.substring(comma + 1); - -        return new Point(Double.valueOf(sX), Double.valueOf(sY)); -    } - -    private final double mX; -    private final double mY; - -    /** -     * Create a new point with the given coordinates. -     * -     * @param x The x coordinate. -     * @param y The y coordinate. -     */ -    public Point(double x, double y) { -        mX = x; -        mY = y; -    } - -    /** -     * Compute the distance between this point and another. -     * -     * @param other The other point. -     * @return The distance between this point and other. -     */ -    public double distance(Point other) { -        final double dx = mX - other.mX; -        final double dy = mY - other.mY; - -        return Math.sqrt(dx*dx + dy*dy); -    } - -    /** -     * @return The x coordinate. -     */ -    public double getX() { -        return mX; -    } - -    /** -     * @return The y coordinate. -     */ -    public double getY() { -        return mY; -    } - -    /** -     * @return The point represented as a comma separated pair. -     */ -    @Override -    public String toString() { -        return String.format("%.2f,%.2f", mX, mY); -    } -} diff --git a/src/com/p4square/grow/model/QuadQuestion.java b/src/com/p4square/grow/model/QuadQuestion.java deleted file mode 100644 index a7b4179..0000000 --- a/src/com/p4square/grow/model/QuadQuestion.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -/** - * Two-dimensional Question. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class QuadQuestion extends Question { -    private static final ScoringEngine ENGINE = new QuadScoringEngine(); - -    private String mTop; -    private String mRight; -    private String mBottom; -    private String mLeft; - -    /** -     * @return the top label. -     */ -    public String getTop() { -        return mTop; -    } - -    /** -     * Set the top label. -     * @param s The new top label. -     */ -    public void setTop(String s) { -        mTop = s; -    } - -    /** -     * @return the right label. -     */ -    public String getRight() { -        return mRight; -    } - -    /** -     * Set the right label. -     * @param s The new right label. -     */ -    public void setRight(String s) { -        mRight = s; -    } - -    /** -     * @return the bottom label. -     */ -    public String getBottom() { -        return mBottom; -    } - -    /** -     * Set the bottom label. -     * @param s The new bottom label. -     */ -    public void setBottom(String s) { -        mBottom = s; -    } - -    /** -     * @return the left label. -     */ -    public String getLeft() { -        return mLeft; -    } - -    /** -     * Set the left label. -     * @param s The new left label. -     */ -    public void setLeft(String s) { -        mLeft = s; -    } - -    @Override -    public boolean scoreAnswer(Score score, RecordedAnswer answer) { -        return ENGINE.scoreAnswer(score, this, answer); -    } - -    @Override -    public QuestionType getType() { -        return QuestionType.QUAD; -    } -} diff --git a/src/com/p4square/grow/model/QuadScoringEngine.java b/src/com/p4square/grow/model/QuadScoringEngine.java deleted file mode 100644 index 33403b5..0000000 --- a/src/com/p4square/grow/model/QuadScoringEngine.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -import com.p4square.grow.model.Point; - -/** - * QuadScoringEngine expects the user's answer to be a Point string. We find - * the closest answer Point to the user's answer and treat that as the answer. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class QuadScoringEngine extends ScoringEngine { - -    @Override -    public boolean scoreAnswer(Score score, Question question, RecordedAnswer userAnswer) { -        // Find all of the answer points. -        Point[] answers = new Point[question.getAnswers().size()]; -        { -            int i = 0; -            for (String answerStr : question.getAnswers().keySet()) { -               answers[i++] = Point.valueOf(answerStr); -            } -        } - -        // Parse the user's answer. -        Point userPoint = Point.valueOf(userAnswer.getAnswerId()); - -        // Find the closest answer point to the user's answer. -        double minDistance = Double.MAX_VALUE; -        int answerIndex = 0; -        for (int i = 0; i < answers.length; i++) { -            final double distance = userPoint.distance(answers[i]); -            if (distance < minDistance) { -                minDistance = distance; -                answerIndex = i; -            } -        } - -        LOG.debug("Quad " + question.getId() + ": Got answer " -                + answers[answerIndex].toString() + " for user point " + userAnswer); - -        // Get the answer and update the score. -        final Answer answer = question.getAnswers().get(answers[answerIndex].toString()); -        return answer.score(score); -    } -} diff --git a/src/com/p4square/grow/model/Question.java b/src/com/p4square/grow/model/Question.java deleted file mode 100644 index f4b9458..0000000 --- a/src/com/p4square/grow/model/Question.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -import java.util.HashMap; -import java.util.Map; - -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonSubTypes.Type; -import com.fasterxml.jackson.annotation.JsonTypeInfo; - -/** - * Model of an assessment question. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -@JsonTypeInfo( -    use = JsonTypeInfo.Id.NAME, -    include = JsonTypeInfo.As.PROPERTY, -    property = "type") -@JsonSubTypes({ -    @Type(value = TextQuestion.class, name = "text"), -    @Type(value = ImageQuestion.class, name = "image"), -    @Type(value = SliderQuestion.class, name = "slider"), -    @Type(value = QuadQuestion.class, name = "quad"), -    @Type(value = CircleQuestion.class, name = "circle"), -}) -public abstract class Question { -    /** -     * QuestionType indicates the type of Question. -     * -     * @author Jesse Morgan <jesse@jesterpm.net> -     */ -    public enum QuestionType { -        TEXT, -        IMAGE, -        SLIDER, -        QUAD, -        CIRCLE; - -        @Override -        public String toString() { -            return name().toLowerCase(); -        } -    } - -    private String mQuestionId; -    private QuestionType mType; -    private String mQuestionText; -    private Map<String, Answer> mAnswers; - -    private String mPreviousQuestionId; -    private String mNextQuestionId; - -    public Question() { -        mAnswers = new HashMap<String, Answer>(); -    } - -    /** -     * @return the id String for this question. -     */ -    public String getId() { -        return mQuestionId; -    } - -    /** -     * Set the id String for this question. -     * @param id New id -     */ -    public void setId(String id) { -        mQuestionId = id; -    } - -    /** -     * @return The Question text. -     */ -    public String getQuestion() { -        return mQuestionText; -    } - -    /** -     * Set the question text. -     * @param value The new question text. -     */ -    public void setQuestion(String value) { -        mQuestionText = value; -    } - -    /** -     * @return The id String of the previous question or null if no previous question exists. -     */ -    public String getPreviousQuestion() { -        return mPreviousQuestionId; -    } - -    /** -     * Set the id string of the previous question. -     * @param id Previous question id or null if there is no previous question. -     */ -    public void setPreviousQuestion(String id) { -        mPreviousQuestionId = id; -    } - -    /** -     * @return The id String of the next question or null if no next question exists. -     */ -    public String getNextQuestion() { -        return mNextQuestionId; -    } - -    /** -     * Set the id string of the next question. -     * @param id next question id or null if there is no next question. -     */ -    public void setNextQuestion(String id) { -        mNextQuestionId = id; -    } - -    /** -     * @return a map of Answer id Strings to Answer objects. -     */ -    public Map<String, Answer> getAnswers() { -        return mAnswers; -    } - -    /** -     * Determine the id of the next question based on the answer to this -     * question. -     * -     * @param answerid -     *              The id of the selected answer. -     * @return a question id or null if this is the last question. -     */ -    public String getNextQuestion(String answerid) { -        String nextQuestion = null; - -        Answer a = mAnswers.get(answerid); -        if (a != null) { -            nextQuestion = a.getNextQuestion(); -        } - -        if (nextQuestion == null) { -            nextQuestion = mNextQuestionId; -        } - -        return nextQuestion; -    } - -    /** -     * Update the score based on the answer to this question. -     * -     * @param score The running score to update. -     * @param answer The answer give to this question. -     * @return true if scoring should continue, false if this answer trumps everything else. -     */ -    public abstract boolean scoreAnswer(Score score, RecordedAnswer answer); - -    /** -     * @return the QuestionType of this question. -     */ -    public abstract QuestionType getType(); - -} diff --git a/src/com/p4square/grow/model/RecordedAnswer.java b/src/com/p4square/grow/model/RecordedAnswer.java deleted file mode 100644 index 7d9905d..0000000 --- a/src/com/p4square/grow/model/RecordedAnswer.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -/** - * Simple model for a user's assessment answer. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class RecordedAnswer { -    private String mAnswerId; - -    /** -     * @return The user's answer. -     */ -    public String getAnswerId() { -        return mAnswerId; -    } - -    /** -     * Set the answer id field. -     * @param id The new id. -     */ -    public void setAnswerId(String id) { -        mAnswerId = id; -    } - -    @Override -    public String toString() { -        return mAnswerId; -    } -} diff --git a/src/com/p4square/grow/model/Score.java b/src/com/p4square/grow/model/Score.java deleted file mode 100644 index 031c309..0000000 --- a/src/com/p4square/grow/model/Score.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -/** - * Simple structure containing a score's sum and count. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class Score { -    /** -     * Return the decimal value for the given Score String. -     * -     * This method satisfies the invariant for Score x: -     *  numericScore(x.toString()) <= x.getScore() -     */ -    public static double numericScore(String score) { -        score = score.toLowerCase(); - -        if ("teacher".equals(score)) { -            return 3.5; -        } else if ("disciple".equals(score)) { -            return 2.5; -        } else if ("believer".equals(score)) { -            return 1.5; -        } else if ("seeker".equals(score)) { -            return 0; -        } else { -            return Integer.MAX_VALUE; -        } -    } - -    double sum; -    int count; - -    public Score() { -        sum = 0; -        count = 0; -    } - -    public Score(double sum, int count) { -        this.sum = sum; -        this.count = count; -    } - -    /** -     * Copy Constructor. -     */ -    public Score(Score other) { -        sum = other.sum; -        count = other.count; -    } - -    /** -     * @return The sum of all the points. -     */ -    public double getSum() { -        return sum; -    } - -    /** -     * @return The number of questions included in the score. -     */ -    public int getCount() { -        return count; -    } - -    /** -     * @return The final score. -     */ -    public double getScore() { -        if (count == 0) { -            return 0; -        } - -        return sum / count; -    } - -    /** -     * @return the lowest score in the same category as this score. -     */ -    public double floor() { -        final double score = getScore(); - -        if (score >= 3.5) { -            return 3.5; // teacher - -        } else if (score >= 2.5) { -            return 2.5; // disciple - -        } else if (score >= 1.5) { -            return 1.5; // believer - -        } else { -            return 0; // seeker -        } -    } - -    @Override -    public String toString() { -        final double score = getScore(); - -        if (score >= 3.5) { -            return "teacher"; - -        } else if (score >= 2.5) { -            return "disciple"; - -        } else if (score >= 1.5) { -            return "believer"; - -        } else { -            return "seeker"; -        } -    } - -} diff --git a/src/com/p4square/grow/model/ScoringEngine.java b/src/com/p4square/grow/model/ScoringEngine.java deleted file mode 100644 index 8ff18b3..0000000 --- a/src/com/p4square/grow/model/ScoringEngine.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -import org.apache.log4j.Logger; - -/** - * ScoringEngine computes the score for a question and a given answer. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public abstract class ScoringEngine { -    protected static final Logger LOG = Logger.getLogger(ScoringEngine.class); - -    /** -     * Update the score based on the given question and answer. -     * -     * @param score The running score to update. -     * @param question The question to compute the score for. -     * @param answer The answer give to this question. -     * @return true if scoring should continue, false if this answer trumps everything else. -     */ -    public abstract boolean scoreAnswer(Score score, Question question, RecordedAnswer answer); -} diff --git a/src/com/p4square/grow/model/SimpleScoringEngine.java b/src/com/p4square/grow/model/SimpleScoringEngine.java deleted file mode 100644 index 6ef2dbb..0000000 --- a/src/com/p4square/grow/model/SimpleScoringEngine.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -/** - * SimpleScoringEngine expects the user's answer to a valid answer id and - * scores accordingly. - * - * If the answer id is not valid an Exception is thrown. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class SimpleScoringEngine extends ScoringEngine { - -    @Override -    public boolean scoreAnswer(Score score, Question question, RecordedAnswer userAnswer) { -        final Answer answer = question.getAnswers().get(userAnswer.getAnswerId()); -        if (answer == null) { -            throw new IllegalArgumentException("Not a valid answer."); -        } - -        return answer.score(score); -    } -} diff --git a/src/com/p4square/grow/model/SliderQuestion.java b/src/com/p4square/grow/model/SliderQuestion.java deleted file mode 100644 index f0861e3..0000000 --- a/src/com/p4square/grow/model/SliderQuestion.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -/** - * Slider Question. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class SliderQuestion extends Question { -    private static final ScoringEngine ENGINE = new SliderScoringEngine(); - -    @Override -    public boolean scoreAnswer(Score score, RecordedAnswer answer) { -        return ENGINE.scoreAnswer(score, this, answer); -    } - -    @Override -    public QuestionType getType() { -        return QuestionType.SLIDER; -    } -} diff --git a/src/com/p4square/grow/model/SliderScoringEngine.java b/src/com/p4square/grow/model/SliderScoringEngine.java deleted file mode 100644 index 2961e95..0000000 --- a/src/com/p4square/grow/model/SliderScoringEngine.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -/** - * SliderScoringEngine expects the user's answer to be a decimal value in the - * range [0, 1]. The value is scaled to the range [1, 4] and added to the - * score. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class SliderScoringEngine extends ScoringEngine { - -    @Override -    public boolean scoreAnswer(Score score, Question question, RecordedAnswer userAnswer) { -        int numberOfAnswers = question.getAnswers().size(); -        if (numberOfAnswers == 0) { -            throw new IllegalArgumentException("Question has no answers."); -        } - -        double answer = Double.valueOf(userAnswer.getAnswerId()); -        if (answer < 0 || answer > 1) { -            throw new IllegalArgumentException("Answer out of bounds."); -        } - -        double delta = Math.max(1, Math.ceil(answer * numberOfAnswers) / numberOfAnswers * 4); - -        score.sum += delta; -        score.count++; - -        return true; -    } -} diff --git a/src/com/p4square/grow/model/TextQuestion.java b/src/com/p4square/grow/model/TextQuestion.java deleted file mode 100644 index 88c2a34..0000000 --- a/src/com/p4square/grow/model/TextQuestion.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -/** - * Text Question. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class TextQuestion extends Question { -    private static final ScoringEngine ENGINE = new SimpleScoringEngine(); - -    @Override -    public boolean scoreAnswer(Score score, RecordedAnswer answer) { -        return ENGINE.scoreAnswer(score, this, answer); -    } - -    @Override -    public QuestionType getType() { -        return QuestionType.TEXT; -    } -} diff --git a/src/com/p4square/grow/model/TrainingRecord.java b/src/com/p4square/grow/model/TrainingRecord.java deleted file mode 100644 index bc3ffa9..0000000 --- a/src/com/p4square/grow/model/TrainingRecord.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -/** - * Representation of a user's training record. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class TrainingRecord { -    private String mLastVideo; -    private Playlist mPlaylist; - -    public TrainingRecord() { -        mPlaylist = new Playlist(); -    } - -    /** -     * @return Video id of the last video watched. -     */ -    public String getLastVideo() { -        return mLastVideo; -    } - -    /** -     * Set the video id for the last video watched. -     * @param video The new video id. -     */ -    public void setLastVideo(String video) { -        mLastVideo = video; -    } - -    /** -     * @return the user's Playlist. -     */ -    public Playlist getPlaylist() { -        return mPlaylist; -    } - -    /** -     * Set the user's playlist. -     * @param playlist The new playlist. -     */ -    public void setPlaylist(Playlist playlist) { -        mPlaylist = playlist; -    } -} diff --git a/src/com/p4square/grow/model/UserRecord.java b/src/com/p4square/grow/model/UserRecord.java deleted file mode 100644 index 4399282..0000000 --- a/src/com/p4square/grow/model/UserRecord.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.model; - -import java.io.IOException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -import org.apache.commons.codec.binary.Hex; - -import org.restlet.security.User; - -/** - * A simple user representation without any secrets. - */ -public class UserRecord { -    private String mId; -    private String mFirstName; -    private String mLastName; -    private String mEmail; -    private String mLanding; -    private boolean mNewBeliever; - -    // Backend Access -    private String mBackendPasswordHash; - -    /** -     * Create an empty UserRecord. -     */ -    public UserRecord() { -    } - -    /** -     * Create a new UserRecord with the information from a User. -     */ -    public UserRecord(final User user) { -        mId = user.getIdentifier(); -        mFirstName = user.getFirstName(); -        mLastName = user.getLastName(); -        mEmail = user.getEmail(); -    } - -    /** -     * @return The user's identifier. -     */ -    public String getId() { -        return mId; -    } - -    /** -     * Set the user's identifier. -     * @param value The new id. -     */ -    public void setId(final String value) { -        mId = value; -    } - -    /** -     * @return The user's email. -     */ -    public String getEmail() { -        return mEmail; -    } - -    /** -     * Set the user's email. -     * @param value The new email. -     */ -    public void setEmail(final String value) { -        mEmail = value; -    } - -    /** -     * @return The user's first name. -     */ -    public String getFirstName() { -        return mFirstName; -    } - -    /** -     * Set the user's first name. -     * @param value The new first name. -     */ -    public void setFirstName(final String value) { -        mFirstName = value; -    } - -    /** -     * @return The user's last name. -     */ -    public String getLastName() { -        return mLastName; -    } - -    /** -     * Set the user's last name. -     * @param value The new last name. -     */ -    public void setLastName(final String value) { -        mLastName = value; -    } - -    /** -     * @return The user's landing page. -     */ -    public String getLanding() { -        return mLanding; -    } - -    /** -     * Set the user's landing page. -     * @param value The new landing page. -     */ -    public void setLanding(final String value) { -        mLanding = value; -    } - -    /** -     * @return true if the user came from the New Believer's landing. -     */ -    public boolean getNewBeliever() { -        return mNewBeliever; -    } - -    /** -     * Set the user's new believer flag. -     * @param value The new flag. -     */ -    public void setNewBeliever(final boolean value) { -        mNewBeliever = value; -    } - -    /** -     * @return The user's backend password hash, null if he doesn't have -     * access. -     */ -    public String getBackendPasswordHash() { -        return mBackendPasswordHash; -    } - -    /** -     * Set the user's backend password hash. -     * @param value The new backend password hash or null to remove -     * access. -     */ -    public void setBackendPasswordHash(final String value) { -        mBackendPasswordHash = value; -    } - -    /** -     * Set the user's backend password to the clear-text value given. -     * @param value The new backend password. -     */ -    public void setBackendPassword(final String value) { -        try { -            mBackendPasswordHash = hashPassword(value); -        } catch (NoSuchAlgorithmException e) { -            throw new RuntimeException(e); -        } -    } - -    /** -     * Hash the given secret. -     */ -    public static String hashPassword(final String secret) throws NoSuchAlgorithmException { -        MessageDigest md = MessageDigest.getInstance("SHA-1"); - -        // Convert the char[] to byte[] -        // FIXME This approach is incorrectly truncating multibyte -        // characters. -        byte[] b = new byte[secret.length()]; -        for (int i = 0; i < secret.length(); i++) { -            b[i] = (byte) secret.charAt(i); -        } - -        md.update(b); - -        byte[] hash = md.digest(); -        return new String(Hex.encodeHex(hash)); -    } -} diff --git a/src/com/p4square/grow/model/VideoRecord.java b/src/com/p4square/grow/model/VideoRecord.java deleted file mode 100644 index ec99d0d..0000000 --- a/src/com/p4square/grow/model/VideoRecord.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -import java.util.Date; - -import com.fasterxml.jackson.annotation.JsonIgnore; - -/** - * Simple bean containing video completion data. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class VideoRecord implements Cloneable { -    private Boolean mComplete; -    private Boolean mRequired; -    private Date mCompletionDate; - -    public VideoRecord() { -        mComplete = null; -        mRequired = null; -        mCompletionDate = null; -    } - -    public boolean getComplete() { -        if (mComplete == null) { -            return false; -        } -        return mComplete; -    } - -    public void setComplete(boolean complete) { -        mComplete = complete; -    } - -    @JsonIgnore -    public boolean isCompleteSet() { -        return mComplete != null; -    } - -    public boolean getRequired() { -        if (mRequired == null) { -            return true; -        } -        return mRequired; -    } - -    public void setRequired(boolean complete) { -        mRequired = complete; -    } - -    @JsonIgnore -    public boolean isRequiredSet() { -        return mRequired != null; -    } - -    public Date getCompletionDate() { -        return mCompletionDate; -    } - -    public void setCompletionDate(Date date) { -        mCompletionDate = date; -    } - -    /** -     * Convenience method to mark a video complete. -     */ -    public void complete() { -        mComplete = true; -        mCompletionDate = new Date(); -    } - -    /** -     * @return an identical clone of this record. -     */ -    public VideoRecord clone() throws CloneNotSupportedException { -        VideoRecord r = (VideoRecord) super.clone(); -        r.mComplete = mComplete; -        r.mRequired = mRequired; -        r.mCompletionDate = mCompletionDate; -        return r; -    } -} diff --git a/src/com/p4square/grow/provider/CollectionProvider.java b/src/com/p4square/grow/provider/CollectionProvider.java deleted file mode 100644 index e4e9040..0000000 --- a/src/com/p4square/grow/provider/CollectionProvider.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.provider; - -import java.io.IOException; -import java.util.Map; - -/** - * ListProvider is the logical extension of Provider for dealing with lists of - * items. - * - * @param C The type of the collection key. - * @param K The type of the item key. - * @param V The type of the value. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public interface CollectionProvider<C, K, V> { -    /** -     * Retrieve a specific object from the collection. -     * -     * @param collection The collection key. -     * @param key The key for the object in the collection. -     * @return The object or null if not found. -     */ -    V get(C collection, K key) throws IOException; - -    /** -     * Retrieve a collection. -     * -     * The returned map will never be null. -     * -     * @param collection The collection key. -     * @return A Map of keys to values. -     */ -    Map<K, V> query(C collection) throws IOException; - -    /** -     * Retrieve a portion of a collection. -     * -     * The returned map will never be null. -     * -     * @param collection The collection key. -     * @param limit Max number of items to return. -     * @return A Map of keys to values. -     */ -    Map<K, V> query(C collection, int limit) throws IOException; - -    /** -     * Persist the object with the given key. -     * -     * @param collection The collection key. -     * @param key The key for the object in the collection. -     * @param obj The object to persist. -     */ -    void put(C collection, K key, V obj) throws IOException; -} diff --git a/src/com/p4square/grow/provider/DelegateCollectionProvider.java b/src/com/p4square/grow/provider/DelegateCollectionProvider.java deleted file mode 100644 index cf697ba..0000000 --- a/src/com/p4square/grow/provider/DelegateCollectionProvider.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.provider; - -import java.io.IOException; -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public abstract class DelegateCollectionProvider<C, DC, K, DK, V> -    implements CollectionProvider<C, K, V> { - -    private CollectionProvider<DC, DK, V> mProvider; - -    public DelegateCollectionProvider(final CollectionProvider<DC, DK, V> provider) { -        mProvider = provider; -    } - -    public V get(C collection, K key) throws IOException { -        return mProvider.get(makeCollectionKey(collection), makeKey(key)); -    } - -    public Map<K, V> query(C collection) throws IOException { -        return query(collection, -1); -    } - -    public Map<K, V> query(C collection, int limit) throws IOException { -        Map<DK, V> delegateResult =  mProvider.query(makeCollectionKey(collection), limit); -        Map<K, V> result = new LinkedHashMap<>(); -        for (Map.Entry<DK, V> entry : delegateResult.entrySet()) { -            result.put(unmakeKey(entry.getKey()), entry.getValue()); -        } - -        return result; -    } - -    public void put(C collection, K key, V obj) throws IOException { -        mProvider.put(makeCollectionKey(collection), makeKey(key), obj); -    } - -    /** -     * Make a collection key for the delegated provider. -     * -     * @param input The pre-transform key. -     * @return the post-transform key. -     */ -    protected abstract DC makeCollectionKey(final C input); - -    /** -     * Make a key for the delegated provider. -     * -     * @param input The pre-transform key. -     * @return the post-transform key. -     */ -    protected abstract DK makeKey(final K input); - -    /** -     * Transform a key for the delegated provider to an input key. -     * -     * @param input The post-transform key. -     * @return the pre-transform key. -     */ -    protected abstract K unmakeKey(final DK input); -} diff --git a/src/com/p4square/grow/provider/DelegateProvider.java b/src/com/p4square/grow/provider/DelegateProvider.java deleted file mode 100644 index 42dcc63..0000000 --- a/src/com/p4square/grow/provider/DelegateProvider.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.provider; - -import java.io.IOException; - -/** - * DelegateProvider wraps an existing Provider an transforms the key from - * type K to type D. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public abstract class DelegateProvider<K, D, V> implements Provider<K, V> { - -    private Provider<D, V> mProvider; - -    public DelegateProvider(final Provider<D, V> provider) { -        mProvider = provider; -    } - -    @Override -    public V get(final K key) throws IOException { -        return mProvider.get(makeKey(key)); -    } - -    @Override -    public void put(final K key, final V obj) throws IOException { -        mProvider.put(makeKey(key), obj); -    } - -    /** -     * Make a Key for the delegated provider. -     * -     * @param input The pre-transform key. -     * @return the post-transform key. -     */ -    protected abstract D makeKey(final K input); -} diff --git a/src/com/p4square/grow/provider/JsonEncodedProvider.java b/src/com/p4square/grow/provider/JsonEncodedProvider.java deleted file mode 100644 index 500f761..0000000 --- a/src/com/p4square/grow/provider/JsonEncodedProvider.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.provider; - -import java.io.IOException; - -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; - -/** - * Provider provides a simple interface for loading and persisting - * objects. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public abstract class JsonEncodedProvider<V> { -    public static final ObjectMapper MAPPER = new ObjectMapper(); -    static { -        MAPPER.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true); -        MAPPER.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true); -        MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); -    } - -    protected final Class<V> mClazz; -    protected final JavaType mType; - -    public JsonEncodedProvider(Class<V> clazz) { -        mClazz = clazz; -        mType = null; -    } - -    public JsonEncodedProvider(JavaType type) { -        mType = type; -        mClazz = null; -    } - -    /** -     * Encode the object as JSON. -     * -     * @param obj The object to encode. -     * @return The JSON encoding of obj. -     * @throws IOException if the object cannot be encoded. -     */ -    protected String encode(V obj) throws IOException { -        if (mClazz == String.class) { -            return (String) obj; -        } - -        return MAPPER.writeValueAsString(obj); -    } - -    /** -     * Decode the JSON string as an object. -     * -     * @param blob The JSON data to decode. -     * @return The decoded object or null if blob is null. -     * @throws IOException If an object cannot be decoded. -     */ -    protected V decode(String blob) throws IOException { -        if (blob == null) { -            return null; -        } - -        if (mClazz == String.class) { -            return (V) blob; -        } - -        V obj; -        if (mClazz != null) { -            obj = MAPPER.readValue(blob, mClazz); - -        } else { -            obj = MAPPER.readValue(blob, mType); -        } - -        return obj; -    } -} - diff --git a/src/com/p4square/grow/provider/MapCollectionProvider.java b/src/com/p4square/grow/provider/MapCollectionProvider.java deleted file mode 100644 index 4c5cef6..0000000 --- a/src/com/p4square/grow/provider/MapCollectionProvider.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2015 Jesse Morgan - */ - -package com.p4square.grow.provider; - -import java.io.IOException; -import java.util.Iterator; -import java.util.Map; -import java.util.HashMap; - -/** - * In-memory CollectionProvider implementation, useful for tests. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class MapCollectionProvider<C, K, V> implements CollectionProvider<C, K, V> { -    private final Map<C, Map<K, V>> mMap; - -    public MapCollectionProvider() { -        mMap = new HashMap<>(); -    } - -    @Override -    public synchronized V get(C collection, K key) throws IOException { -        Map<K, V> map = mMap.get(collection); -        if (map != null) { -            return map.get(key); -        } - -        return null; -    } - -    @Override -    public synchronized Map<K, V> query(C collection) throws IOException { -        Map<K, V> map = mMap.get(collection); -        if (map == null) { -            map = new HashMap<K, V>(); -        } - -        return map; -    } - -    @Override -    public synchronized Map<K, V> query(C collection, int limit) throws IOException { -        Map<K, V> map = query(collection); - -        if (map.size() > limit) { -            Map<K, V> smallMap = new HashMap<>(); - -            Iterator<Map.Entry<K, V>> iterator = map.entrySet().iterator(); -            for (int i = 0; i < limit; i++) { -                Map.Entry<K, V> entry = iterator.next(); -                smallMap.put(entry.getKey(), entry.getValue()); -            } - -            return smallMap; - -        } else { -            return map; -        } -    } - -    @Override -    public synchronized void put(C collection, K key, V obj) throws IOException { -        Map<K, V> map = mMap.get(collection); -        if (map == null) { -            map = new HashMap<K, V>(); -            mMap.put(collection, map); -        } - -        map.put(key, obj); -    } -} diff --git a/src/com/p4square/grow/provider/MapProvider.java b/src/com/p4square/grow/provider/MapProvider.java deleted file mode 100644 index 40f8107..0000000 --- a/src/com/p4square/grow/provider/MapProvider.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2015 Jesse Morgan - */ - -package com.p4square.grow.provider; - -import java.io.IOException; -import java.util.Map; -import java.util.HashMap; - -/** - * In-memory Provider implementation, useful for tests. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class MapProvider<K, V> implements Provider<K, V> { -    private final Map<K, V> mMap = new HashMap<K, V>(); - -    @Override -    public V get(K key) throws IOException { -        return mMap.get(key); -    } - -    @Override -    public void put(K key, V obj) throws IOException { -        mMap.put(key, obj); -    } -} diff --git a/src/com/p4square/grow/provider/Provider.java b/src/com/p4square/grow/provider/Provider.java deleted file mode 100644 index ca6af25..0000000 --- a/src/com/p4square/grow/provider/Provider.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.provider; - -import java.io.IOException; - -/** - * Provider provides a simple interface for loading and persisting - * objects. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public interface Provider<K, V> { -    /** -     * Retrieve the object with the given key. -     * -     * @param key The key for the object. -     * @return The object or null if not found. -     */ -    V get(K key) throws IOException; - -    /** -     * Persist the object with the given key. -     * -     * @param key The key for the object. -     * @param obj The object to persist. -     */ -    void put(K key, V obj) throws IOException; -} diff --git a/src/com/p4square/grow/provider/ProvidesAssessments.java b/src/com/p4square/grow/provider/ProvidesAssessments.java deleted file mode 100644 index 62ba8f6..0000000 --- a/src/com/p4square/grow/provider/ProvidesAssessments.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.provider; - -import com.p4square.grow.model.RecordedAnswer; - -/** - *  - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public interface ProvidesAssessments { -    /** -     * Provides a collection of user assessments. -     * The collection key is the user id. -     * The key is the question id. -     */ -    CollectionProvider<String, String, String> getAnswerProvider(); -} diff --git a/src/com/p4square/grow/provider/ProvidesQuestions.java b/src/com/p4square/grow/provider/ProvidesQuestions.java deleted file mode 100644 index b43f649..0000000 --- a/src/com/p4square/grow/provider/ProvidesQuestions.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.provider; - -import com.p4square.grow.model.Question; - -/** - * Indicates the ability to provide a Question Provider. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public interface ProvidesQuestions { -    /** -     * @return A Provider of Questions keyed by question id. -     */ -    Provider<String, Question> getQuestionProvider(); -} diff --git a/src/com/p4square/grow/provider/ProvidesStrings.java b/src/com/p4square/grow/provider/ProvidesStrings.java deleted file mode 100644 index 5d9976e..0000000 --- a/src/com/p4square/grow/provider/ProvidesStrings.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.provider; - -/** - * Indicates the ability to provide a String provider. - * - * Strings are typically configuration settings stored as a String. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public interface ProvidesStrings { -    /** -     * @return A Provider of Questions keyed by question id. -     */ -    Provider<String, String> getStringProvider(); -}
\ No newline at end of file diff --git a/src/com/p4square/grow/provider/ProvidesTrainingRecords.java b/src/com/p4square/grow/provider/ProvidesTrainingRecords.java deleted file mode 100644 index 586e649..0000000 --- a/src/com/p4square/grow/provider/ProvidesTrainingRecords.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.provider; - -import java.io.IOException; - -import com.p4square.grow.model.TrainingRecord; -import com.p4square.grow.model.Playlist; - -/** - * Indicates the ability to provide a TrainingRecord Provider. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public interface ProvidesTrainingRecords { -    /** -     * @return A Provider of Questions keyed by question id. -     */ -    Provider<String, TrainingRecord> getTrainingRecordProvider(); - -    /** -     * @return the Default Playlist. -     */ -    Playlist getDefaultPlaylist() throws IOException; -} diff --git a/src/com/p4square/grow/provider/ProvidesUserRecords.java b/src/com/p4square/grow/provider/ProvidesUserRecords.java deleted file mode 100644 index d77c878..0000000 --- a/src/com/p4square/grow/provider/ProvidesUserRecords.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.provider; - -import com.p4square.grow.model.UserRecord; - -/** - * Indicates the ability to provide a UserRecord Provider. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public interface ProvidesUserRecords { -    /** -     * @return A Provider of Questions keyed by question id. -     */ -    Provider<String, UserRecord> getUserRecordProvider(); -} diff --git a/src/com/p4square/grow/provider/ProvidesVideos.java b/src/com/p4square/grow/provider/ProvidesVideos.java deleted file mode 100644 index 3d055d3..0000000 --- a/src/com/p4square/grow/provider/ProvidesVideos.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.provider; - -/** - *  - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public interface ProvidesVideos { -    /** -     * @return A Provider of Questions keyed by question id. -     */ -    CollectionProvider<String, String, String> getVideoProvider(); -} diff --git a/src/com/p4square/grow/provider/TrainingRecordProvider.java b/src/com/p4square/grow/provider/TrainingRecordProvider.java deleted file mode 100644 index 44dba87..0000000 --- a/src/com/p4square/grow/provider/TrainingRecordProvider.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.provider; - -import java.io.IOException; - -import com.p4square.grow.model.TrainingRecord; - -/** - * TrainingRecordProvider wraps an existing Provider to get and put TrainingRecords. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public abstract class TrainingRecordProvider<K> implements Provider<String, TrainingRecord> { - -    private Provider<K, TrainingRecord> mProvider; - -    public TrainingRecordProvider(Provider<K, TrainingRecord> provider) { -        mProvider = provider; -    } - -    @Override -    public TrainingRecord get(String key) throws IOException { -        return mProvider.get(makeKey(key)); -    } - -    @Override -    public void put(String key, TrainingRecord obj) throws IOException { -        mProvider.put(makeKey(key), obj); -    } - -    /** -     * Make a Key for a TrainingRecord.. -     * -     * @param userId The user id. -     * @return a key for the TrainingRecord of userid. -     */ -    protected abstract K makeKey(String userId); -} diff --git a/src/com/p4square/grow/tools/AssessmentStats.java b/src/com/p4square/grow/tools/AssessmentStats.java deleted file mode 100644 index ca83411..0000000 --- a/src/com/p4square/grow/tools/AssessmentStats.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.tools; - - -import java.util.Map; -import java.util.HashMap; -import java.util.Queue; -import java.util.List; -import java.util.LinkedList; -import java.io.IOException; - -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; - -import com.p4square.grow.model.Answer; -import com.p4square.grow.model.Question; -import com.p4square.grow.model.RecordedAnswer; -import com.p4square.grow.model.Score; -import com.p4square.grow.provider.Provider; -import com.p4square.grow.provider.JsonEncodedProvider; - -/** - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class AssessmentStats { -    public static void main(String... args) throws Exception { -        if (args.length == 0) { -            System.out.println("Usage: AssessmentStats directory firstQuestionId"); -            System.exit(1); -        } - -        Map<String, Question> questions; -        questions = loadQuestions(args[0], args[1]); - -        // Find the highest possible score -        List<AnswerPath> scores = findHighestFromId(questions, args[1]); - -        // Print Results -        System.out.printf("Found %d different paths.\n", scores.size()); -        int i = 0; -        for (AnswerPath path : scores) { -            Score s = path.mScore; -            System.out.printf("Path %d: %f points, %d questions. Score: %f (%s)\n", -                    i++, s.getSum(), s.getCount(), s.getScore(), s.toString()); -            System.out.println("    " + path.mPath); -            System.out.println("    " + path.mScores); -        } -    } - -    private static Map<String, Question> loadQuestions(String baseDir, String firstId) throws IOException { -        FileQuestionProvider provider = new FileQuestionProvider(baseDir); - -        // Questions to find... -        Queue<String> queue = new LinkedList<>(); -        queue.offer(firstId); - -        Map<String, Question> questions = new HashMap<>(); - - -        while (!queue.isEmpty()) { -            Question q = provider.get(queue.poll()); -            questions.put(q.getId(), q); - -            if (q.getNextQuestion() != null) { -                queue.offer(q.getNextQuestion()); - -            } - -            for (Answer a : q.getAnswers().values()) { -                if (a.getNextQuestion() != null) { -                    queue.offer(a.getNextQuestion()); -                } -            } - -            // Quick Sanity check -            if (q.getPreviousQuestion() != null) { -                if (questions.get(q.getPreviousQuestion()) == null) { -                    throw new IllegalStateException("Haven't seen previous question??"); -                } -            } -        } - -        return questions; -    } - -    private static List<AnswerPath> findHighestFromId(Map<String, Question> questions, String id) { -        List<AnswerPath> scores = new LinkedList<>(); -        doFindHighestFromId(questions, id, scores, new AnswerPath()); -        return scores; -    } - -    private static void doFindHighestFromId(Map<String, Question> questions, String id, List<AnswerPath> scores, AnswerPath path) { -        if (id == null) { -            // End of the road! Save the score and return. -            scores.add(path); -            return; -        } - -        Question q = questions.get(id); - -        // Find the best answer following this path and find other paths. -        Score maxScore = path.mScore; -        double max = 0; - -        int answerCount = 1; -        for (Map.Entry<String, Answer> entry : q.getAnswers().entrySet()) { -            Answer a = entry.getValue(); -            RecordedAnswer userAnswer = new RecordedAnswer(); - -            if (q.getType() == Question.QuestionType.SLIDER) { -                // Special Case -                userAnswer.setAnswerId(String.valueOf((float) answerCount / q.getAnswers().size())); - -            } else { -                userAnswer.setAnswerId(entry.getKey()); -            } - -            Score tempScore = new Score(path.mScore); // Always start with the initial score. -            boolean endOfRoad = !q.scoreAnswer(tempScore, userAnswer); -            double thisScore = tempScore.getSum() - path.mScore.getSum(); - -            if (endOfRoad) { -                // End of Road is a fork too. Record and pick another answer. -                AnswerPath fork = new AnswerPath(path); -                fork.update(id, tempScore); -                scores.add(fork); - -            } else if (a.getNextQuestion() != null) { -                // Found a new path, follow it. -                // Remember to count this answer in the score. -                AnswerPath fork = new AnswerPath(path); -                fork.update(id, tempScore); -                doFindHighestFromId(questions, a.getNextQuestion(), scores, fork); - -            } else if (thisScore > max) { -                // Found a higher option that isn't a new path. -                maxScore = tempScore; -                max = thisScore; -            } - -            answerCount++; -        } - -        path.update(id, maxScore); -        doFindHighestFromId(questions, q.getNextQuestion(), scores, path); -    } - -    private static class FileQuestionProvider extends JsonEncodedProvider<Question> implements Provider<String, Question> { -        private String mBaseDir; - -        public FileQuestionProvider(String directory) { -            super(Question.class); -            mBaseDir = directory; -        } - -        @Override -        public Question get(String key) throws IOException { -            Path qfile = FileSystems.getDefault().getPath(mBaseDir, key + ".json"); -            byte[] blob = Files.readAllBytes(qfile); -            return decode(new String(blob)); -        } - -        @Override -        public void put(String key, Question obj) throws IOException { -            throw new UnsupportedOperationException("Not Implemented"); -        } -    } - -    private static class AnswerPath { -        String mPath; -        String mScores; -        Score mScore; - -        public AnswerPath() { -            mPath = null; -            mScores = null; -            mScore = new Score(); -        } - -        public AnswerPath(AnswerPath other) { -            mPath = other.mPath; -            mScores = other.mScores; -            mScore = other.mScore; -        } - -        public void update(String questionId, Score newScore) { -            String value; - -            if (mScore.getCount() == newScore.getCount()) { -                value = "n/a"; - -            } else { -                double delta = newScore.getSum() - mScore.getSum(); -                if (delta < 0) { -                    value = "TRUMP"; -                } else { -                    value = String.valueOf(delta); -                } -            } - -            if (mPath == null) { -                mPath = questionId; -                mScores = value; - -            } else { -                mPath += ",  " + questionId; -                mScores += " + " + value; -            } - -            mScore = newScore; -        } -    } -} diff --git a/src/com/p4square/grow/tools/AttributeBackfillTool.java b/src/com/p4square/grow/tools/AttributeBackfillTool.java deleted file mode 100644 index d7fd2ff..0000000 --- a/src/com/p4square/grow/tools/AttributeBackfillTool.java +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.tools; - -import java.util.Arrays; -import java.util.Date; -import java.util.List; -import java.util.Map; - -import org.restlet.Client; -import org.restlet.Context; -import org.restlet.data.Protocol; - -import com.p4square.f1oauth.Attribute; -import com.p4square.f1oauth.F1API; -import com.p4square.f1oauth.F1Access; -import com.p4square.f1oauth.F1Exception; -import com.p4square.restlet.oauth.OAuthUser; - -import com.p4square.grow.backend.dynamo.DynamoDatabase; -import com.p4square.grow.backend.dynamo.DynamoKey; - -import com.p4square.grow.config.Config; - -import com.p4square.grow.model.Chapter; -import com.p4square.grow.model.Playlist; -import com.p4square.grow.model.TrainingRecord; -import com.p4square.grow.model.VideoRecord; -import com.p4square.grow.provider.JsonEncodedProvider; - -/** - * This utility is used to backfill F1 Attributes from the GROW database into F1. - * - * This tool currently reads from Dynamo directly. It should probably access the - * backend or use the {@link com.p4square.grow.backend.GrowData} abstraction instead. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class AttributeBackfillTool { - -    private static Config mConfig; -    private static F1API mF1API; -    private static DynamoDatabase mDatabase; - -    public static void usage() { -        System.out.println("java com.p4square.grow.tools.AttributeBackfillTool <command>...\n"); -        System.out.println("Commands:"); -        System.out.println("\t--domain <domain>             Set config domain"); -        System.out.println("\t--dev                         Set config domain to dev"); -        System.out.println("\t--config <file>               Merge in config file"); -        System.out.println("\t--assessments                 Backfill All Assessments"); -        System.out.println("\t--training                    Backfill All Training Records"); -    } - -    public static void main(String... args) { -        if (args.length == 0) { -            usage(); -            System.exit(1); -        } - -        mConfig = new Config(); - -        try { -            mConfig.updateConfig(AttributeTool.class.getResourceAsStream("/grow.properties")); - -            int offset = 0; -            while (offset < args.length) { -                if ("--domain".equals(args[offset])) { -                    mConfig.setDomain(args[offset + 1]); -                    mF1API = null; -                    mDatabase = null; -                    offset += 2; - -                } else if ("--dev".equals(args[offset])) { -                    mConfig.setDomain("dev"); -                    mF1API = null; -                    mDatabase = null; -                    offset += 1; - -                } else if ("--config".equals(args[offset])) { -                    mConfig.updateConfig(args[offset + 1]); -                    mF1API = null; -                    mDatabase = null; -                    offset += 2; - -                } else if ("--assessments".equals(args[offset])) { -                    offset = assessments(args, ++offset); - -                } else if ("--training".equals(args[offset])) { -                    offset = training(args, ++offset); - -                } else { -                    throw new IllegalArgumentException("Unknown command " + args[offset]); -                } -            } -        } catch (Exception e) { -            e.printStackTrace(); -            System.exit(2); -        } -    } - -    private static F1API getF1API() throws Exception { -        if (mF1API == null) { -            Context context = new Context(); -            Client client = new Client(context, Arrays.asList(Protocol.HTTP, Protocol.HTTPS)); -            context.setClientDispatcher(client); - -            F1Access f1Access = new F1Access(context, -                    mConfig.getString("f1ConsumerKey"), -                    mConfig.getString("f1ConsumerSecret"), -                    mConfig.getString("f1BaseUrl"), -                    mConfig.getString("f1ChurchCode"), -                    F1Access.UserType.WEBLINK); - -            // Gather Username and Password -            String username = System.console().readLine("F1 Username: "); -            char[] password = System.console().readPassword("F1 Password: "); - -            OAuthUser user = f1Access.getAccessToken(username, new String(password)); -            Arrays.fill(password, ' '); // Lost cause, but I'll still try. - -            mF1API = f1Access.getAuthenticatedApi(user); -        } - -        return mF1API; -    } - -    private static DynamoDatabase getDatabase() { -        if (mDatabase == null) { -            mDatabase = new DynamoDatabase(mConfig); -        } - -        return mDatabase; -    } - -    private static int assessments(String[] args, int offset) throws Exception { -        final F1API f1 = getF1API(); -        final DynamoDatabase db = getDatabase(); - -        DynamoKey key = DynamoKey.newKey("assessments", null); - -        while (key != null) { -            Map<DynamoKey, Map<String, String>> rows = db.getAll(key); - -            key = null; - -            for (Map.Entry<DynamoKey, Map<String, String>> row : rows.entrySet()) { -                key = row.getKey(); - -                String userId = key.getHashKey(); - -                String summaryString = row.getValue().get("summary"); -                if (summaryString == null || summaryString.length() == 0) { -                    System.out.printf("%s assessment incomplete\n", userId); -                    continue; -                } - -                try { -                    Map summary = JsonEncodedProvider.MAPPER.readValue(summaryString, Map.class); - -                    String result = (String) summary.get("result"); -                    if (result == null) { -                        System.out.printf("%s assessment incomplete\n", userId); -                        continue; -                    } - -                    String attributeName = "Assessment Complete - " + result; - -                    // Check if the user already has the attribute. -                    List<Attribute> attributes = f1.getAttribute(userId, attributeName); - -                    if (attributes.size() == 0) { -                        Attribute attribute = new Attribute(attributeName); -                        attribute.setStartDate(new Date()); -                        attribute.setComment(summaryString); - -                        if (f1.addAttribute(userId, attribute)) { -                            System.out.printf("%s attribute added\n", userId); -                        } else { -                            System.out.printf("%s failed to add attribute\n", userId); -                        } -                    } else { -                        System.out.printf("%s already has attribute\n", userId); -                    } -                } catch (Exception e) { -                    System.out.printf("%s exception: %s\n", userId, e.getMessage()); -                } -            } -        } - -        return offset; -    } - -    private static int training(String[] args, int offset) throws Exception { -        final F1API f1 = getF1API(); -        final DynamoDatabase db = getDatabase(); - -        DynamoKey key = DynamoKey.newKey("training", null); - -        while (key != null) { -            Map<DynamoKey, Map<String, String>> rows = db.getAll(key); - -            key = null; - -            for (Map.Entry<DynamoKey, Map<String, String>> row : rows.entrySet()) { -                key = row.getKey(); - -                String userId = key.getHashKey(); - -                String valueString = row.getValue().get("value"); -                if (valueString == null || valueString.length() == 0) { -                    System.out.printf("%s empty training record\n", userId); -                    continue; -                } - -                try { -                    TrainingRecord record = -                        JsonEncodedProvider.MAPPER.readValue(valueString, TrainingRecord.class); -                    Playlist playlist = record.getPlaylist(); - -chapters: -                    for (Map.Entry<String, Chapter> entry : playlist.getChaptersMap().entrySet()) { -                        Chapter chapter = entry.getValue(); - -                        // Find completion date -                        Date complete = new Date(0); -                        for (VideoRecord vr : chapter.getVideos().values()) { -                            if (!vr.getComplete()) { -                                continue chapters; -                            } - -                            Date recordCompletion = vr.getCompletionDate(); -                            if (recordCompletion != null && complete.before(recordCompletion)) { -                                complete = vr.getCompletionDate(); -                            } -                        } - -                        String attributeName = "Training Complete - " + entry.getKey(); - -                        // Check if the user already has the attribute. -                        List<Attribute> attributes = f1.getAttribute(userId, attributeName); - -                        if (attributes.size() == 0) { -                            Attribute attribute = new Attribute(attributeName); -                            attribute.setStartDate(complete); - -                            if (f1.addAttribute(userId, attribute)) { -                                System.out.printf("%s added %s\n", userId, attributeName); -                            } else { -                                System.out.printf("%s failed to add %s\n", userId, attributeName); -                            } -                        } else { -                            System.out.printf("%s already has %s\n", userId, attributeName); -                        } -                    } - -                } catch (Exception e) { -                    System.out.printf("%s exception: %s\n", userId, e.getMessage()); -                    e.printStackTrace(); -                } -            } -        } - -        return offset; -    } -} diff --git a/src/com/p4square/grow/tools/AttributeTool.java b/src/com/p4square/grow/tools/AttributeTool.java deleted file mode 100644 index 8e0540a..0000000 --- a/src/com/p4square/grow/tools/AttributeTool.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.tools; - -import java.util.Arrays; -import java.util.Date; -import java.util.List; -import java.util.Map; - -import org.restlet.Client; -import org.restlet.Context; -import org.restlet.data.Protocol; - -import com.p4square.grow.config.Config; -import com.p4square.f1oauth.Attribute; -import com.p4square.f1oauth.F1Access; -import com.p4square.f1oauth.F1API; -import com.p4square.f1oauth.F1Exception; -import com.p4square.restlet.oauth.OAuthUser; - -/** - * Tool for manipulating F1 Attributes. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class AttributeTool { - -    private static Config mConfig; -    private static F1API mF1API; - -    public static void usage() { -        System.out.println("java com.p4square.grow.tools.AttributeTool <command>...\n"); -        System.out.println("Commands:"); -        System.out.println("\t--domain <domain>                          Set config domain"); -        System.out.println("\t--dev                                      Set config domain to dev"); -        System.out.println("\t--config <file>                            Merge in config file"); -        System.out.println("\t--list                                     List all attributes"); -        System.out.println("\t--assign <userId> <attribute> <comment>    Assign an attribute"); -        System.out.println("\t--getall <userId>                          Get an attribute"); -        System.out.println("\t--get <userId> <attribute>                 Get an attribute"); -    } - -    public static void main(String... args) { -        if (args.length == 0) { -            usage(); -            System.exit(1); -        } - -        mConfig = new Config(); - -        try { -            mConfig.updateConfig(AttributeTool.class.getResourceAsStream("/grow.properties")); - -            int offset = 0; -            while (offset < args.length) { -                if ("--domain".equals(args[offset])) { -                    mConfig.setDomain(args[offset + 1]); -                    mF1API = null; -                    offset += 2; - -                } else if ("--dev".equals(args[offset])) { -                    mConfig.setDomain("dev"); -                    mF1API = null; -                    offset += 1; - -                } else if ("--config".equals(args[offset])) { -                    mConfig.updateConfig(args[offset + 1]); -                    mF1API = null; -                    offset += 2; - -                } else if ("--list".equals(args[offset])) { -                    offset = list(args, ++offset); - -                } else if ("--assign".equals(args[offset])) { -                    offset = assign(args, ++offset); - -                } else if ("--getall".equals(args[offset])) { -                    offset = getall(args, ++offset); - -                } else if ("--get".equals(args[offset])) { -                    offset = get(args, ++offset); - -                } else { -                    throw new IllegalArgumentException("Unknown command " + args[offset]); -                } -            } -        } catch (Exception e) { -            e.printStackTrace(); -            System.exit(2); -        } -    } - -    private static F1API getF1API() throws Exception { -        if (mF1API == null) { -            Context context = new Context(); -            Client client = new Client(context, Arrays.asList(Protocol.HTTP, Protocol.HTTPS)); -            context.setClientDispatcher(client); - -            F1Access f1Access = new F1Access(context, -                    mConfig.getString("f1ConsumerKey"), -                    mConfig.getString("f1ConsumerSecret"), -                    mConfig.getString("f1BaseUrl"), -                    mConfig.getString("f1ChurchCode"), -                    F1Access.UserType.WEBLINK); - -            // Gather Username and Password -            String username = System.console().readLine("F1 Username: "); -            char[] password = System.console().readPassword("F1 Password: "); - -            OAuthUser user = f1Access.getAccessToken(username, new String(password)); -            Arrays.fill(password, ' '); // Lost cause, but I'll still try. - -            mF1API = f1Access.getAuthenticatedApi(user); -        } - -        return mF1API; -    } - -    private static int list(String[] args, int offset) throws Exception { -        final F1API f1 = getF1API(); - -        final Map<String, String> attributes = f1.getAttributeList(); -        System.out.printf("%7s %s\n", "ID", "Name"); -        for (Map.Entry<String, String> entry : attributes.entrySet()) { -            System.out.printf("%7s %s\n", entry.getValue(), entry.getKey()); -        } - -        return offset; -    } - -    private static int assign(String[] args, int offset) throws Exception { -        final String userId = args[offset++]; -        final String attributeName = args[offset++]; -        final String comment = args[offset++]; - -        final F1API f1 = getF1API(); - -        Attribute attribute = new Attribute(attributeName); -        attribute.setStartDate(new Date()); -        attribute.setComment(comment); - -        if (f1.addAttribute(userId, attribute)) { -            System.out.println("Added attribute " + attributeName + " for " + userId); -        } else { -            System.out.println("Failed to add attribute " + attributeName + " for " + userId); -        } - -        return offset; -    } - -    private static int getall(String[] args, int offset) throws Exception { -        final String userId = args[offset++]; - -        doGet(userId, null); - -        return offset; -    } - -    private static int get(String[] args, int offset) throws Exception { -        final String userId = args[offset++]; -        final String attributeName = args[offset++]; - -        doGet(userId, attributeName); - -        return offset; -    } - -    private static void doGet(final String userId, final String attributeName) throws Exception { -        final F1API f1 = getF1API(); - -        List<Attribute> attributes = f1.getAttribute(userId, attributeName); -        for (Attribute attribute : attributes) { -            System.out.printf("%s %s %s %s %s\n%s\n\n", -                    userId, -                    attribute.getAttributeName(), -                    attribute.getId(), -                    attribute.getStartDate(), -                    attribute.getEndDate(), -                    attribute.getComment()); -        } -    } -} diff --git a/src/com/p4square/restlet/metrics/MetricRouter.java b/src/com/p4square/restlet/metrics/MetricRouter.java deleted file mode 100644 index d4da270..0000000 --- a/src/com/p4square/restlet/metrics/MetricRouter.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.restlet.metrics; - -import com.codahale.metrics.Counter; -import com.codahale.metrics.MetricRegistry; -import com.codahale.metrics.Timer; - -import org.restlet.Context; -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.Restlet; -import org.restlet.routing.TemplateRoute; -import org.restlet.routing.Router; - -/** - *  - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class MetricRouter extends Router { - -    private final MetricRegistry mMetricRegistry; - -    public MetricRouter(Context context, MetricRegistry metrics) { -        super(context); -        mMetricRegistry = metrics; -    } - -    @Override -    protected void doHandle(Restlet next, Request request, Response response) { -        String baseName; -        if (next instanceof TemplateRoute) { -            TemplateRoute temp = (TemplateRoute) next; -            baseName = MetricRegistry.name("MetricRouter", temp.getTemplate().getPattern()); -        } else { -            baseName = MetricRegistry.name("MetricRouter", "unknown"); -        } - -        final Timer.Context aggTimer = mMetricRegistry.timer("MetricRouter.time").time(); -        final Timer.Context timer = mMetricRegistry.timer(baseName + ".time").time(); - -        try { -            super.doHandle(next, request, response); -        } finally { -            timer.stop(); -            aggTimer.stop(); - -            // Record status code -            boolean success = !response.getStatus().isError(); -            if (success) { -                mMetricRegistry.counter("MetricRouter.success").inc(); -                mMetricRegistry.counter(baseName + ".response.success").inc(); -            } else { -                mMetricRegistry.counter("MetricRouter.failure").inc(); -                mMetricRegistry.counter(baseName + ".response.failure").inc(); -            } -        } -    } -} diff --git a/src/com/p4square/restlet/metrics/MetricsApplication.java b/src/com/p4square/restlet/metrics/MetricsApplication.java deleted file mode 100644 index 6caf742..0000000 --- a/src/com/p4square/restlet/metrics/MetricsApplication.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.restlet.metrics; - -import java.util.concurrent.TimeUnit; - -import com.codahale.metrics.MetricRegistry; -import com.codahale.metrics.json.MetricsModule; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.restlet.Application; -import org.restlet.Restlet; -import org.restlet.resource.Finder; - -/** - *  - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class MetricsApplication extends Application { -    static final ObjectMapper MAPPER; -    static { -       MAPPER = new ObjectMapper(); -       MAPPER.registerModule(new MetricsModule(TimeUnit.SECONDS, TimeUnit.MILLISECONDS, true)); -    } - -    private final MetricRegistry mMetricRegistry; - -    public MetricsApplication(MetricRegistry metrics) { -        mMetricRegistry = metrics; -    } - -    public MetricRegistry getMetricRegistry() { -        return mMetricRegistry; -    } - -    @Override -    public Restlet createInboundRoot() { -        return new Finder(getContext(), MetricsResource.class); -    } -} diff --git a/src/com/p4square/restlet/metrics/MetricsResource.java b/src/com/p4square/restlet/metrics/MetricsResource.java deleted file mode 100644 index e2ab14d..0000000 --- a/src/com/p4square/restlet/metrics/MetricsResource.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.restlet.metrics; - -import com.codahale.metrics.MetricRegistry; - -import org.restlet.ext.jackson.JacksonRepresentation; -import org.restlet.representation.Representation; -import org.restlet.resource.ServerResource; - -/** - *  - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class MetricsResource extends ServerResource { - -    private MetricRegistry mMetricRegistry; - -    @Override -    public void doInit() { -        mMetricRegistry = ((MetricsApplication) getApplication()).getMetricRegistry(); -    } - -    @Override -    protected Representation get() { -        JacksonRepresentation<MetricRegistry> rep = new JacksonRepresentation<>(mMetricRegistry); -        rep.setObjectMapper(MetricsApplication.MAPPER); -        return rep; -    } -} diff --git a/src/com/p4square/restlet/oauth/OAuthAuthenticator.java b/src/com/p4square/restlet/oauth/OAuthAuthenticator.java deleted file mode 100644 index c33bb5a..0000000 --- a/src/com/p4square/restlet/oauth/OAuthAuthenticator.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.restlet.oauth; - -import org.apache.log4j.Logger; - -import org.restlet.Context; -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.security.Authenticator; -import org.restlet.security.User; - -/** - * Authenticator which makes an OAuth request to authenticate the user. - * - * If this Authenticator is made optional than no requests are made to the - * service provider. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class OAuthAuthenticator extends Authenticator { -    private static Logger LOG = Logger.getLogger(OAuthAuthenticator.class); - -    private static final String OAUTH_TOKEN = "oauth_token"; -    private static final String COOKIE_NAME = "oauth_secret"; - -    private final OAuthHelper mHelper; - -    /** -     * Create a new Authenticator. -     * -     * @param Context the current context. -     * @param optional If true, unauthenticated users are allowed to continue. -     * @param helper The OAuthHelper which will help with the requests. -     */ -    public OAuthAuthenticator(Context context, boolean optional, OAuthHelper helper) { -        super(context, false, optional, null); - -        mHelper = helper; -    } - -    protected boolean authenticate(Request request, Response response) { -        /* -         * The authentication workflow has three steps: -         *  1. Get RequestToken -         *  2. Authenticate the user -         *  3. Get AccessToken -         * -         * The authentication workflow is broken into two stages. In the first, -         * we generate the RequestToken (step 1) and redirect the user to the -         * authentication page. When the user comes back, we will request the -         * AccessToken (step 2). -         * -         * We determine which half we are in by the presence of the oauth_token -         * parameter in the query string. -         */ - -        final String token = request.getResourceRef().getQueryAsForm().getFirstValue(OAUTH_TOKEN); -        final String secret = request.getCookies().getFirstValue(COOKIE_NAME); - -        try { -            if (token == null) { -                if (isOptional()) { -                    return false; -                } - -                // 1. Get RequestToken -                Token requestToken = mHelper.getRequestToken(); - -                if (requestToken == null) { -                    return false; -                } - -                // 2. Redirect user -                // TODO Encrypt cookie -                response.getCookieSettings().add(COOKIE_NAME, requestToken.getSecret()); -                response.redirectSeeOther(mHelper.getLoginUrl(requestToken, request.getResourceRef().toString())); -                return false; - -            } else { -                // 3. Get AccessToken -                Token requestToken = new Token(token, secret); -                User user = mHelper.getAccessToken(requestToken); -                request.getClientInfo().setUser(user); -                return true; -            } - -        } catch (OAuthException e) { -            LOG.debug("Authentication failed: " + e); -            return false; -        } -    } -} diff --git a/src/com/p4square/restlet/oauth/OAuthAuthenticatorHelper.java b/src/com/p4square/restlet/oauth/OAuthAuthenticatorHelper.java deleted file mode 100644 index 76ff044..0000000 --- a/src/com/p4square/restlet/oauth/OAuthAuthenticatorHelper.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.restlet.oauth; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; - -import java.net.URLEncoder; - -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; - -import java.util.Collections; -import java.util.Random; - -import javax.crypto.Mac; -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; - -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.data.ChallengeRequest; -import org.restlet.data.ChallengeResponse; -import org.restlet.data.ChallengeScheme; -import org.restlet.data.CharacterSet; -import org.restlet.data.Form; -import org.restlet.data.Method; -import org.restlet.data.Parameter; -import org.restlet.data.Reference; -import org.restlet.engine.header.ChallengeWriter; -import org.restlet.engine.header.Header; -import org.restlet.engine.security.AuthenticatorHelper; -import org.restlet.engine.util.Base64; -import org.restlet.util.Series; - -/** - * Authentication helper for signing OAuth Requests. - * - * This implementation is limited to one consumer token/secret per restlet - * engine. In practice this means you will only be able to interact with one - * service provider unless you loaded/unloaded the AuthenticationHelper for - * each request. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class OAuthAuthenticatorHelper extends AuthenticatorHelper { -    private static final String SIGNATURE_METHOD = "HMAC-SHA1"; -    private static final String JAVA_SIGNATURE_METHOD = "HmacSHA1"; -    private static final String ENCODING = "UTF-8"; - -    private final Random mRandom; -    private final Token mConsumerToken; - -    /** -     * Package-private constructor. -     * -     * This class should only be instantiated by OAuthHelper. -     */ -    OAuthAuthenticatorHelper(Token consumerToken) { -        super(ChallengeScheme.HTTP_OAUTH, true, false); - -        mRandom = new Random(); -        mConsumerToken = consumerToken; -    } - -    @Override -    public void formatRequest(ChallengeWriter cw, ChallengeRequest cr, -            Response response, Series<Header> httpHeaders) throws IOException { - -        throw new UnsupportedOperationException("OAuth Requests are not implemented"); -    } - -    @Override -    public void formatResponse(ChallengeWriter cw, ChallengeResponse response, -            Request request, Series<Header> httpHeaders) { - -        try { -            Series<Parameter> authParams = new Series<Parameter>(Parameter.class); - -            String nonce = String.valueOf(mRandom.nextInt()); -            String timestamp = String.valueOf(System.currentTimeMillis() / 1000); - -            authParams.add(new Parameter("oauth_consumer_key", mConsumerToken.getToken())); -            authParams.add(new Parameter("oauth_nonce", nonce)); -            authParams.add(new Parameter("oauth_signature_method", SIGNATURE_METHOD)); -            authParams.add(new Parameter("oauth_timestamp", timestamp)); -            authParams.add(new Parameter("oauth_version", "1.0")); - -            String accessToken = response.getIdentifier(); -            if (accessToken != null) { -                authParams.add(new Parameter("oauth_token", accessToken)); -            } - -            // Generate Signature -            String signature = generateSignature(response, request, authParams); -            authParams.add(new Parameter("oauth_signature", signature)); - -            // Write Header -            for (Parameter p : authParams) { -                cw.appendQuotedChallengeParameter(encode(p.getName()), encode(p.getValue())); -            } - -        } catch (IOException e) { -            throw new RuntimeException(e); - -        } catch (InvalidKeyException e) { -            throw new RuntimeException(e); - -        } catch (NoSuchAlgorithmException e) { -            throw new RuntimeException(e); -        } -    } - -    /** -     * Helper method to generate an OAuth Signature. -     */ -    private String generateSignature(ChallengeResponse response, Request request, -           Series<Parameter> authParams) -        throws NoSuchAlgorithmException, InvalidKeyException, IOException, -                          UnsupportedEncodingException { - -        // HTTP Request Method -        String httpMethod = request.getMethod().getName(); - -        // Request Url -        Reference url = request.getResourceRef(); -        String requestUrl = encode(url.getScheme() + ":" + url.getHierarchicalPart()); - -        // Normalized parameters -        Series<Parameter> params = new Series<Parameter>(Parameter.class); - -        // OAUTH Params -        params.addAll(authParams); - -        // Query Params -        Form query = url.getQueryAsForm(); -        params.addAll(query); - -        // Sort it -        Collections.sort(params); - -        StringBuilder normalizedParamsBuilder = new StringBuilder(); -        for (Parameter p : params) { -            normalizedParamsBuilder.append('&'); -            normalizedParamsBuilder.append(p.encode(CharacterSet.UTF_8)); -        } -        String normalizedParams = encode(normalizedParamsBuilder.substring(1)); // remove the first & - -        // Generate signature base -        String sigBase = httpMethod + "&" + requestUrl + "&" + normalizedParams.toString(); - -        // Sign the signature base -        Mac mac = Mac.getInstance(JAVA_SIGNATURE_METHOD); - -        String accessTokenSecret = ""; -        if (response.getIdentifier() != null) { -            accessTokenSecret = new String(response.getSecret()); -        } - -        byte[] keyBytes = (encode(mConsumerToken.getSecret()) + "&" + encode(accessTokenSecret)).getBytes(ENCODING); -        SecretKey key = new SecretKeySpec(keyBytes, JAVA_SIGNATURE_METHOD); -        mac.init(key); - -        byte[] signature = mac.doFinal(sigBase.getBytes(ENCODING)); - -        return Base64.encode(signature, false).trim(); -    } - -    /** -     * Helper method to URL Encode Strings. -     */ -    private String encode(String input) throws UnsupportedEncodingException { -        return URLEncoder.encode(input, ENCODING); -    } -} diff --git a/src/com/p4square/restlet/oauth/OAuthException.java b/src/com/p4square/restlet/oauth/OAuthException.java deleted file mode 100644 index dd326d3..0000000 --- a/src/com/p4square/restlet/oauth/OAuthException.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.restlet.oauth; - -import org.restlet.data.Status; - -/** - * Exception throw when the service provider returns an error. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class OAuthException extends Exception { -    private final Status mStatus; - -    public OAuthException(Status status) { -        super("Service provider failed request: " + status.getDescription()); -        mStatus = status; -    } - -    public Status getStatus() { -        return mStatus; -    } -} diff --git a/src/com/p4square/restlet/oauth/OAuthHelper.java b/src/com/p4square/restlet/oauth/OAuthHelper.java deleted file mode 100644 index 67dd238..0000000 --- a/src/com/p4square/restlet/oauth/OAuthHelper.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.restlet.oauth; - -import java.net.URLEncoder; - -import org.restlet.Context; -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.Restlet; -import org.restlet.data.ChallengeResponse; -import org.restlet.data.ChallengeScheme; -import org.restlet.data.Form; -import org.restlet.data.Method; -import org.restlet.data.Reference; -import org.restlet.data.Status; -import org.restlet.engine.Engine; -import org.restlet.representation.Representation; - -/** - * Helper Class for OAuth 1.0 Authentication. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public abstract class OAuthHelper { -    private final Restlet mDispatcher; -    private final Token mConsumerToken; - -    /** -     * Create a new OAuth Helper. -     * As currently implemented, there can only be one OAuthHelper per Restlet -     * Engine since this class registers its own provider for the OAuth -     * authentication protocol. -     * -     * FIXME: This could be improved by making OAuthAuthenticationHelper and -     * maybe Token aware of multiple service providers. -     * -     * @param context The restlet context which provides a ClientDispatcher. -     * @param consumerKey The OAuth consumer key for this application. -     * @param consumerSecret the OAuth consumer secret for this application. -     */ -    public OAuthHelper(Context context, String consumerKey, String consumerSecret) { -        mDispatcher = context.getClientDispatcher(); -        mConsumerToken = new Token(consumerKey, consumerSecret); - -        Engine.getInstance().getRegisteredAuthenticators().add(new OAuthAuthenticatorHelper(mConsumerToken)); -    } - -    /** -     * @return the URL for the initial RequestToken request. -     */ -    protected abstract String getRequestTokenUrl(); - -    /** -     * Request a RequestToken. -     * -     * @return a Token containing the RequestToken. -     * @throws OAuthException if the request fails. -     */ -    public Token getRequestToken() throws OAuthException { -        Request request = new Request(Method.GET, getRequestTokenUrl()); -        request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_OAUTH)); - -        Response response = mDispatcher.handle(request); - -        return processTokenRequest(response); -    } - -    /** -     * @return the URL to redirect the user to for Authentication. -     */ -    public abstract String getLoginUrl(Token requestToken, String callback); - -    /** -     * @return the URL for the AccessToken request. -     */ -    protected abstract String getAccessTokenUrl(); - -    /** -     * Request an AccessToken for a previously authenticated RequestToken. -     * -     * @return an OAuthUser object containing the AccessToken. -     * @throws OAuthException if the request fails. -     */ -    public OAuthUser getAccessToken(Token requestToken) throws OAuthException { -        Request request = new Request(Method.GET, getAccessTokenUrl()); -        request.setChallengeResponse(requestToken.getChallengeResponse()); - -        return processAccessTokenRequest(request); -    } - -    /** -     * Helper method to decode the token returned from an OAuth Request. -     * -     * @param response The Response object from the Request. -     * @return the Token from the oauth_token and oauth_token_secret parameters. -     * @throws OAuthException is the server reported an error. -     */ -    protected Token processTokenRequest(Response response) throws OAuthException { -        Status status = response.getStatus(); -        Representation entity = response.getEntity(); - -        try { -            if (status.isSuccess()) { -                Form form = new Form(entity); -                String token = form.getFirstValue("oauth_token"); -                String secret = form.getFirstValue("oauth_token_secret"); - -                return new Token(token, secret); - -            } else { -                throw new OAuthException(status); -            } -        } finally { -            entity.release(); -        } -    } - -    /** -     * Helper method to create an OAuthUser from the AccessToken request. -     * -     * The User's identifier is set to the Content-Location header, if present. -     * -     * @param response The Response to the AccessToken Request. -     * @return An OAuthUser object wrapping the AccessToken. -     * @throws OAuthException if the request failed. -     */ -    public OAuthUser processAccessTokenRequest(Request request) throws OAuthException { -        Response response = getResponse(request); -        Token accessToken = processTokenRequest(response); - -        Reference ref = response.getEntity().getLocationRef(); -        if (ref != null) { -            return new OAuthUser(ref.toString(), accessToken); - -        } else { -            return new OAuthUser(accessToken); -        } -    } - -    /** -     * Helper method to get a Response for a Request. -     */ -    public Response getResponse(Request request) { -        return mDispatcher.handle(request); -    } -} diff --git a/src/com/p4square/restlet/oauth/OAuthUser.java b/src/com/p4square/restlet/oauth/OAuthUser.java deleted file mode 100644 index 11dbac1..0000000 --- a/src/com/p4square/restlet/oauth/OAuthUser.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.restlet.oauth; - -import org.restlet.data.ChallengeResponse; -import org.restlet.security.User; - -/** - * Simple User object which also contains an OAuth AccessToken. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class OAuthUser extends User { -    private final Token mToken; -    private final String mContentLocation; - -    public OAuthUser(Token token) { -        this(null, token); -    } - -    public OAuthUser(String location, Token token) { -        super(); -        mToken = token; -        mContentLocation = location; -    } - -    /** -     * @return the Location associated with the user. -     */ -    public String getLocation() { -        return mContentLocation; -    } - -    /** -     * @return The AccessToken. -     */ -    public Token getToken() { -        return mToken; -    } - -    /** -     * Convenience method for getToken().getChallengeResponse(). -     * @return A ChallengeResponse based upon the access token. -     */ -    public ChallengeResponse getChallengeResponse() { -        return mToken.getChallengeResponse(); -    } -} diff --git a/src/com/p4square/restlet/oauth/Token.java b/src/com/p4square/restlet/oauth/Token.java deleted file mode 100644 index 51a9087..0000000 --- a/src/com/p4square/restlet/oauth/Token.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.restlet.oauth; - -import org.restlet.data.ChallengeResponse; -import org.restlet.data.ChallengeScheme; - -/** - * Token wraps the two Strings which make up an OAuth Token: the public - * component and the private component. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class Token { -    private final String mToken; -    private final String mSecret; - -    public Token(String token, String secret) { -        mToken = token; -        mSecret = secret; -    } - -    /** -     * @return the public component. -     */ -    public String getToken() { -        return mToken; -    } - -    /** -     * @return the secret component. -     */ -    public String getSecret() { -        return mSecret; -    } - -    @Override -    public String toString() { -        return mToken + "&" + mSecret; -    } - -    /** -     * Generate a ChallengeResponse based on this Token. -     * -     * @return a ChallengeResponse object using the OAUTH ChallengeScheme. -     */ -    public ChallengeResponse getChallengeResponse() { -        return new ChallengeResponse(ChallengeScheme.HTTP_OAUTH, mToken, mSecret); -    } -} diff --git a/src/com/p4square/session/Session.java b/src/com/p4square/session/Session.java deleted file mode 100644 index 1bb65f5..0000000 --- a/src/com/p4square/session/Session.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.session; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -import org.restlet.security.User; - -/** - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class Session { -    static final long LIFETIME = 86400000; - -    private final String mSessionId; -    private final User mUser; -    private final Map<String, String> mData; -    private long mExpires; - -    Session(User user) { -        mUser = user; -        mSessionId = UUID.randomUUID().toString(); -        mExpires = System.currentTimeMillis() + LIFETIME; -        mData = new HashMap<String, String>(); -    } - -    void touch() { -        mExpires = System.currentTimeMillis() + LIFETIME; -    } - -    boolean isExpired() { -        return System.currentTimeMillis() > mExpires; -    } - -    public String getId() { -        return mSessionId; -    } - -    public Object get(String key) { -        return mData.get(key); -    } - -    public void put(String key, String value) { -        mData.put(key, value); -    } - -    public User getUser() { -        return mUser; -    } - -    public Map<String, String> getMap() { -        return mData; -    } -} diff --git a/src/com/p4square/session/SessionAuthenticator.java b/src/com/p4square/session/SessionAuthenticator.java deleted file mode 100644 index 794e1a8..0000000 --- a/src/com/p4square/session/SessionAuthenticator.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.session; - -import org.restlet.Context; -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.security.Authenticator; -import org.restlet.security.User; - -/** - *  - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class SessionAuthenticator /*extends Authenticator*/ { -    /* -    @Override -    protected boolean authenticate(Request request, Response response) { -        // Check for authentication cookie -        final String cookie = request.getCookies().getFirstValue(COOKIE_NAME); -        if (cookie != null) { -            cLog.debug("Got cookie: " + cookie); -            // TODO Decrypt user info -            User user = new User(cookie); -            request.getClientInfo().setUser(user); -            return true; -        } - -        // Challenge the user if not authenticated -        response.redirectSeeOther(mLoginPage); -        return false; -    } -    */ -} diff --git a/src/com/p4square/session/SessionCheckingAuthenticator.java b/src/com/p4square/session/SessionCheckingAuthenticator.java deleted file mode 100644 index 489d6a0..0000000 --- a/src/com/p4square/session/SessionCheckingAuthenticator.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.session; - -import org.apache.log4j.Logger; - -import org.restlet.Context; -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.security.Authenticator; - -/** - * Authenticator which succeeds if a valid Session exists. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class SessionCheckingAuthenticator extends Authenticator { -    private static final Logger LOG = Logger.getLogger(SessionCheckingAuthenticator.class); - -    public SessionCheckingAuthenticator(Context context, boolean optional) { -        super(context, optional); -    } - -    protected boolean authenticate(Request request, Response response) { -        Session s = Sessions.getInstance().get(request); - -        if (s != null) { -            LOG.debug("Found session for user " + s.getUser()); -            request.getClientInfo().setUser(s.getUser()); -            return true; - -        } else { -            return false; -        } -    } - -} diff --git a/src/com/p4square/session/SessionCookieAuthenticator.java b/src/com/p4square/session/SessionCookieAuthenticator.java deleted file mode 100644 index 0074b77..0000000 --- a/src/com/p4square/session/SessionCookieAuthenticator.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.session; - -import org.apache.log4j.Logger; - -import org.restlet.Context; -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.security.Authenticator; - -/** - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class SessionCookieAuthenticator extends Authenticator { -    private static final Logger LOG = Logger.getLogger(SessionCookieAuthenticator.class); - -    private static final String COOKIE_NAME  = "S"; - -    private final Sessions mSessions; - -    public SessionCookieAuthenticator(Context context, boolean optional, Sessions sessions) { -        super(context, optional); - -        mSessions = sessions; -    } - -    protected boolean authenticate(Request request, Response response) { -        final String cookie = request.getCookies().getFirstValue(COOKIE_NAME); - -        if (request.getClientInfo().isAuthenticated()) { -            // Request is already authenticated... create session if it doesn't exist. -            if (cookie == null) { -                Session s = mSessions.create(request.getClientInfo().getUser()); -                response.getCookieSettings().add(COOKIE_NAME, s.getId()); -            } - -            return true; - -        } else { -            // Check for authentication cookie -            if (cookie != null) { -                LOG.debug("Got cookie: " + cookie); - -                Session s = mSessions.get(cookie); -                if (s != null) { -                    request.getClientInfo().setUser(s.getUser()); -                    return true; -                } -            } - -            return false; -        } -    } - -} diff --git a/src/com/p4square/session/SessionCreatingAuthenticator.java b/src/com/p4square/session/SessionCreatingAuthenticator.java deleted file mode 100644 index 3ec14b4..0000000 --- a/src/com/p4square/session/SessionCreatingAuthenticator.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.session; - -import org.apache.log4j.Logger; - -import org.restlet.Context; -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.security.Authenticator; -import org.restlet.security.User; - -/** - * Authenticator which creates a Session for the request and adds a cookie - * to the response. - * - * The Request MUST be Authenticated and MUST have a User object associated. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class SessionCreatingAuthenticator extends Authenticator { -    private static final Logger LOG = Logger.getLogger(SessionCreatingAuthenticator.class); - -    public SessionCreatingAuthenticator(Context context) { -        super(context, true); -    } - -    protected boolean authenticate(Request request, Response response) { -        if (Sessions.getInstance().get(request) != null) { -            return true; -        } - -        User user = request.getClientInfo().getUser(); - -        if (request.getClientInfo().isAuthenticated() && user != null) { -            Sessions.getInstance().create(request, response); -            LOG.debug(response); -            return true; -        } - -        return false; -    } - -} diff --git a/src/com/p4square/session/Sessions.java b/src/com/p4square/session/Sessions.java deleted file mode 100644 index 9f9dda0..0000000 --- a/src/com/p4square/session/Sessions.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.session; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.Map; -import java.util.Timer; -import java.util.TimerTask; - -import org.restlet.Response; -import org.restlet.Request; -import org.restlet.data.CookieSetting; -import org.restlet.security.User; - -/** - * Singleton Session Manager. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class Sessions { -    private static final String COOKIE_NAME  = "S"; -    private static final int DELETE  = 0; - -    private static final Sessions THE = new Sessions(); -    public static Sessions getInstance() { -        return THE; -    } - -    private final Map<String, Session> mSessions; -    private final Timer mCleanupTimer; - -    private Sessions() { -        mSessions = new ConcurrentHashMap<String, Session>(); - -        mCleanupTimer = new Timer("sessionCleaner", true); -        mCleanupTimer.scheduleAtFixedRate(new TimerTask() { -            @Override -            public void run() { -                for (Session s : mSessions.values()) { -                    if (s.isExpired()) { -                        mSessions.remove(s.getId()); -                    } -                } -            } -        }, Session.LIFETIME, Session.LIFETIME); -    } - -    /** -     * Get a session by ID. -     * -     * @param sessionid -     *                  The Session id -     * @return The Session if found and not expired, null otherwise. -     */ -    public Session get(String sessionid) { -        Session s = mSessions.get(sessionid); - -        if (s != null && !s.isExpired()) { -            s.touch(); -            return s; -        } - -        return null; -    } - -    /** -     * Get the Session associated with the Request. -     * -     * @param request -     *                  The request to fetch a session for. -     * @return A session or null if no session is found. -     */ -    public Session get(Request request) { -        final String cookie = request.getCookies().getFirstValue(COOKIE_NAME); - -        if (cookie != null) { -            return get(cookie); -        } - -        return null; -    } - -    /** -     * Create a new Session for the given User object. -     * -     * @param user -     *              The User to associate with the Session. -     * @return The new Session object. -     */ -    public Session create(User user) { -        if (user == null) { -            throw new IllegalArgumentException("Can not create session for null user."); -        } - -        Session s = new Session(user); -        mSessions.put(s.getId(), s); - -        return s; -    } - -    /** -     * Delete a Session. -     * -     * @param sessionid -     *              The id of the Session to remove. -     */ -    public void delete(String sessionid) { -        mSessions.remove(sessionid); -    } - -    /** -     * Create a new Session and add the Session cookie to the response. -     * -     * @param request -     *              The request to create the Session for. -     * @param response -     *              The response to add the session cookie to. -     * @return The new Session. -     */ -    public Session create(Request request, Response response) { -        Session s = create(request.getClientInfo().getUser()); - -        CookieSetting cookie = new CookieSetting(COOKIE_NAME, s.getId()); -        cookie.setPath("/"); - -        request.getCookies().add(cookie); -        response.getCookieSettings().add(cookie); - -        return s; -    } - -    /** -     * Remove a Session and delete the cookies. -     * -     * @param request -     *              The request with the session cookie to remove -     * @param response -     *              The response to remove the session cookie from. -     */ -    public void delete(Request request, Response response) { -        final String sessionid = request.getCookies().getFirstValue(COOKIE_NAME); - -        delete(sessionid); - -        CookieSetting cookie = new CookieSetting(COOKIE_NAME, ""); -        cookie.setPath("/"); -        cookie.setMaxAge(DELETE); - -        request.getCookies().add(cookie); -        response.getCookieSettings().add(cookie); -    } - -} | 
