diff options
17 files changed, 519 insertions, 164 deletions
diff --git a/src/com/p4square/grow/backend/CassandraGrowData.java b/src/com/p4square/grow/backend/CassandraGrowData.java new file mode 100644 index 0000000..22a7716 --- /dev/null +++ b/src/com/p4square/grow/backend/CassandraGrowData.java @@ -0,0 +1,172 @@ +/* + * 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/GrowBackend.java b/src/com/p4square/grow/backend/GrowBackend.java index e747a1b..49d064c 100644 --- a/src/com/p4square/grow/backend/GrowBackend.java +++ b/src/com/p4square/grow/backend/GrowBackend.java @@ -18,12 +18,6 @@ import org.restlet.routing.Router;  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; @@ -32,7 +26,6 @@ import com.p4square.grow.model.TrainingRecord;  import com.p4square.grow.model.UserRecord;  import com.p4square.grow.provider.CollectionProvider; -import com.p4square.grow.provider.DelegateProvider;  import com.p4square.grow.provider.Provider;  import com.p4square.grow.provider.ProvidesQuestions;  import com.p4square.grow.provider.ProvidesTrainingRecords; @@ -54,23 +47,12 @@ import com.p4square.grow.backend.feed.TopicResource;   *   * @author Jesse Morgan <jesse@jesterpm.net>   */ -public class GrowBackend extends Application -        implements ProvidesQuestions, ProvidesTrainingRecords, FeedDataProvider, -          ProvidesUserRecords { -    private static final String DEFAULT_COLUMN = "value"; +public class GrowBackend extends Application implements GrowData {      private final static Logger LOG = Logger.getLogger(GrowBackend.class);      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, MessageThread> mFeedThreadProvider; -    private final CollectionProvider<String, String, Message> mFeedMessageProvider; +    private final GrowData mGrowData;      public GrowBackend() {          this(new Config()); @@ -78,30 +60,8 @@ public class GrowBackend extends Application      public GrowBackend(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); +        mGrowData = new CassandraGrowData(config);      }      @Override @@ -146,10 +106,7 @@ public class GrowBackend extends Application      public void start() throws Exception {          super.start(); -        // Setup database -        mDatabase.setClusterName(mConfig.getString("clusterName", "Dev Cluster")); -        mDatabase.setKeyspaceName(mConfig.getString("keyspace", "GROW")); -        mDatabase.init(); +        mGrowData.start();      }      /** @@ -158,48 +115,56 @@ public class GrowBackend extends Application      @Override      public void stop() throws Exception {          LOG.info("Shutting down..."); -        mDatabase.close(); +        mGrowData.stop();          super.stop();      } -    /** -     * @return the current database. -     */ -    public CassandraDatabase getDatabase() { -        return mDatabase; -    } -      @Override      public Provider<String, UserRecord> getUserRecordProvider() { -        return mUserRecordProvider; +        return mGrowData.getUserRecordProvider();      }      @Override      public Provider<String, Question> getQuestionProvider() { -        return mQuestionProvider; +        return mGrowData.getQuestionProvider(); +    } + +    @Override +    public CollectionProvider<String, String, String> getVideoProvider() { +        return mGrowData.getVideoProvider();      }      @Override      public Provider<String, TrainingRecord> getTrainingRecordProvider() { -        return mTrainingRecordProvider; +        return mGrowData.getTrainingRecordProvider();      }      /**       * @return the Default Playlist.       */      public Playlist getDefaultPlaylist() throws IOException { -        return mTrainingRecordProvider.getDefaultPlaylist(); +        return mGrowData.getDefaultPlaylist();      }      @Override      public CollectionProvider<String, String, MessageThread> getThreadProvider() { -        return mFeedThreadProvider; +        return mGrowData.getThreadProvider();      }      @Override      public CollectionProvider<String, String, Message> getMessageProvider() { -        return mFeedMessageProvider; +        return mGrowData.getMessageProvider(); +    } + +    @Override +    public Provider<String, String> getStringProvider() { +        return mGrowData.getStringProvider(); +    } + +    @Override +    public CollectionProvider<String, String, String> getAnswerProvider() { +        return mGrowData.getAnswerProvider();      }      /** diff --git a/src/com/p4square/grow/backend/GrowData.java b/src/com/p4square/grow/backend/GrowData.java new file mode 100644 index 0000000..293bb88 --- /dev/null +++ b/src/com/p4square/grow/backend/GrowData.java @@ -0,0 +1,36 @@ +/* + * 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/db/CassandraCollectionProvider.java b/src/com/p4square/grow/backend/db/CassandraCollectionProvider.java index 5e83247..bfcb48d 100644 --- a/src/com/p4square/grow/backend/db/CassandraCollectionProvider.java +++ b/src/com/p4square/grow/backend/db/CassandraCollectionProvider.java @@ -80,7 +80,11 @@ public class CassandraCollectionProvider<V> implements CollectionProvider<String       * @throws IOException if the object cannot be encoded.       */      protected String encode(V obj) throws IOException { -        return JsonEncodedProvider.MAPPER.writeValueAsString(obj); +        if (mClazz == String.class) { +            return (String) obj; +        } else { +            return JsonEncodedProvider.MAPPER.writeValueAsString(obj); +        }      }      /** @@ -95,6 +99,10 @@ public class CassandraCollectionProvider<V> implements CollectionProvider<String              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/CassandraProviderImpl.java b/src/com/p4square/grow/backend/db/CassandraProviderImpl.java index da5a9f2..9d896e7 100644 --- a/src/com/p4square/grow/backend/db/CassandraProviderImpl.java +++ b/src/com/p4square/grow/backend/db/CassandraProviderImpl.java @@ -26,12 +26,20 @@ public class CassandraProviderImpl<V> extends JsonEncodedProvider<V> implements      @Override      public V get(CassandraKey key) throws IOException {          String blob = mDb.getKey(key.getColumnFamily(), key.getId(), key.getColumn()); +        if (mClazz == String.class) { +            return (V) blob; +        }          return decode(blob);      }      @Override      public void put(CassandraKey key, V obj) throws IOException { -        String blob = encode(obj); +        String blob; +        if (mClazz == String.class) { +            blob = (String) obj; +        } else { +            blob = encode(obj); +        }          mDb.putKey(key.getColumnFamily(), key.getId(), key.getColumn(), blob);      }  } diff --git a/src/com/p4square/grow/backend/resources/BannerResource.java b/src/com/p4square/grow/backend/resources/BannerResource.java index 8519f64..2b9c8e6 100644 --- a/src/com/p4square/grow/backend/resources/BannerResource.java +++ b/src/com/p4square/grow/backend/resources/BannerResource.java @@ -17,9 +17,9 @@ import com.fasterxml.jackson.databind.ObjectMapper;  import org.apache.log4j.Logger;  import com.p4square.grow.backend.GrowBackend; -import com.p4square.grow.backend.db.CassandraDatabase;  import com.p4square.grow.model.Banner;  import com.p4square.grow.provider.JsonEncodedProvider; +import com.p4square.grow.provider.Provider;  /**   * Fetches or sets the banner string. @@ -31,14 +31,14 @@ public class BannerResource extends ServerResource {      public static final ObjectMapper MAPPER = JsonEncodedProvider.MAPPER; -    private CassandraDatabase mDb; +    private Provider<String, String> mStringProvider;      @Override      public void doInit() {          super.doInit();          final GrowBackend backend = (GrowBackend) getApplication(); -        mDb = backend.getDatabase(); +        mStringProvider = backend.getStringProvider();      }      /** @@ -46,7 +46,13 @@ public class BannerResource extends ServerResource {       */      @Override      protected Representation get() { -        String result = mDb.getKey("strings", "banner"); +        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}"; @@ -67,7 +73,7 @@ public class BannerResource extends ServerResource {              Banner banner = representation.getObject(); -            mDb.putKey("strings", "banner", MAPPER.writeValueAsString(banner)); +            mStringProvider.put("banner", MAPPER.writeValueAsString(banner));              setStatus(Status.SUCCESS_NO_CONTENT);          } catch (IOException e) { diff --git a/src/com/p4square/grow/backend/resources/SurveyResource.java b/src/com/p4square/grow/backend/resources/SurveyResource.java index 497978f..8723ee2 100644 --- a/src/com/p4square/grow/backend/resources/SurveyResource.java +++ b/src/com/p4square/grow/backend/resources/SurveyResource.java @@ -13,14 +13,17 @@ import com.fasterxml.jackson.databind.ObjectMapper;  import org.restlet.data.MediaType;  import org.restlet.data.Status; -import org.restlet.resource.ServerResource; +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.backend.db.CassandraDatabase; +import com.p4square.grow.model.Question; +import com.p4square.grow.provider.JsonEncodedProvider; +import com.p4square.grow.provider.Provider;  /**   * This resource manages assessment questions. @@ -30,9 +33,10 @@ import com.p4square.grow.backend.db.CassandraDatabase;  public class SurveyResource extends ServerResource {      private static final Logger LOG = Logger.getLogger(SurveyResource.class); -    private static final ObjectMapper MAPPER = new ObjectMapper(); +    private static final ObjectMapper MAPPER = JsonEncodedProvider.MAPPER; -    private CassandraDatabase mDb; +    private Provider<String, Question> mQuestionProvider; +    private Provider<String, String> mStringProvider;      private String mQuestionId; @@ -41,7 +45,8 @@ public class SurveyResource extends ServerResource {          super.doInit();          final GrowBackend backend = (GrowBackend) getApplication(); -        mDb = backend.getDatabase(); +        mQuestionProvider = backend.getQuestionProvider(); +        mStringProvider = backend.getStringProvider();          mQuestionId = getAttribute("questionId");      } @@ -71,13 +76,22 @@ public class SurveyResource extends ServerResource {          if (mQuestionId != null) {              // Get a question by id -            result = mDb.getKey("strings", "/questions/" + mQuestionId); +            Question question = null; +            try { +                question = mQuestionProvider.get(mQuestionId); +            } catch (IOException e) { +                LOG.error("IOException loading question: " + e); +            } -            if (result == null) { +            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); @@ -85,7 +99,8 @@ public class SurveyResource extends ServerResource {      private Map<?, ?> getQuestionsSummary() {          try { -            String json = mDb.getKey("strings", "/questions"); +            // TODO: This could be better. Quick fix for provider support. +            String json = mStringProvider.get("/questions");              if (json != null) {                  return MAPPER.readValue(json, Map.class); diff --git a/src/com/p4square/grow/backend/resources/SurveyResultsResource.java b/src/com/p4square/grow/backend/resources/SurveyResultsResource.java index 404ccec..7c15cfd 100644 --- a/src/com/p4square/grow/backend/resources/SurveyResultsResource.java +++ b/src/com/p4square/grow/backend/resources/SurveyResultsResource.java @@ -4,12 +4,10 @@  package com.p4square.grow.backend.resources; +import java.io.IOException;  import java.util.Map;  import java.util.HashMap; -import com.netflix.astyanax.model.Column; -import com.netflix.astyanax.model.ColumnList; -  import com.fasterxml.jackson.databind.ObjectMapper;  import org.restlet.data.MediaType; @@ -21,12 +19,12 @@ import org.restlet.resource.ServerResource;  import org.apache.log4j.Logger;  import com.p4square.grow.backend.GrowBackend; -import com.p4square.grow.backend.db.CassandraDatabase;  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; @@ -44,7 +42,7 @@ public class SurveyResultsResource extends ServerResource {          ASSESSMENT, ANSWER      } -    private CassandraDatabase mDb; +    private CollectionProvider<String, String, String> mAnswerProvider;      private Provider<String, Question> mQuestionProvider;      private Provider<String, UserRecord> mUserRecordProvider; @@ -57,7 +55,7 @@ public class SurveyResultsResource extends ServerResource {          super.doInit();          final GrowBackend backend = (GrowBackend) getApplication(); -        mDb = backend.getDatabase(); +        mAnswerProvider = backend.getAnswerProvider();          mQuestionProvider = backend.getQuestionProvider();          mUserRecordProvider = backend.getUserRecordProvider(); @@ -75,27 +73,33 @@ public class SurveyResultsResource extends ServerResource {       */      @Override      protected Representation get() { -        String result = null; - -        switch (mRequestType) { -            case ANSWER: -                result = mDb.getKey("assessments", mUserId, mQuestionId); -                break; +        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; +            } -            case ASSESSMENT: -                result = mDb.getKey("assessments", mUserId, "summary"); -                if (result == null) { -                    result = buildAssessment(); -                } -                break; -        } +            if (result == null) { +                setStatus(Status.CLIENT_ERROR_NOT_FOUND); +                return null; +            } -        if (result == null) { -            setStatus(Status.CLIENT_ERROR_NOT_FOUND); +            return new StringRepresentation(result); +        } catch (IOException e) { +            LOG.error("IOException getting answer: ", e); +            setStatus(Status.SERVER_ERROR_INTERNAL);              return null;          } - -        return new StringRepresentation(result);      }      /** @@ -108,9 +112,9 @@ public class SurveyResultsResource extends ServerResource {          switch (mRequestType) {              case ANSWER:                  try { -                    mDb.putKey("assessments", mUserId, mQuestionId, entity.getText()); -                    mDb.putKey("assessments", mUserId, "lastAnswered", mQuestionId); -                    mDb.deleteKey("assessments", mUserId, "summary"); +                    mAnswerProvider.put(mUserId, mQuestionId, entity.getText()); +                    mAnswerProvider.put(mUserId, "lastAnswered", mQuestionId); +                    mAnswerProvider.put(mUserId, "summary", null);                      success = true;                  } catch (Exception e) { @@ -143,8 +147,8 @@ public class SurveyResultsResource extends ServerResource {          switch (mRequestType) {              case ANSWER:                  try { -                    mDb.deleteKey("assessments", mUserId, mQuestionId); -                    mDb.deleteKey("assessments", mUserId, "summary"); +                    mAnswerProvider.put(mUserId, mQuestionId, null); +                    mAnswerProvider.put(mUserId, "summary", null);                      success = true;                  } catch (Exception e) { @@ -154,7 +158,9 @@ public class SurveyResultsResource extends ServerResource {              case ASSESSMENT:                  try { -                    mDb.deleteRow("assessments", mUserId); +                    mAnswerProvider.put(mUserId, "summary", null); +                    mAnswerProvider.put(mUserId, "lastAnswered", null); +                    // TODO Delete answers                      UserRecord record = mUserRecordProvider.get(mUserId);                      if (record != null) { @@ -188,48 +194,48 @@ public class SurveyResultsResource extends ServerResource {      /**       * This method compiles assessment results.       */ -    private String buildAssessment() { +    private String buildAssessment() throws IOException {          StringBuilder sb = new StringBuilder("{ ");          // Last question answered -        final String lastAnswered = mDb.getKey("assessments", mUserId, "lastAnswered"); -        if (lastAnswered != null) { -            sb.append("\"lastAnswered\": \"" + lastAnswered + "\""); +        final String lastAnswered = mAnswerProvider.get(mUserId, "lastAnswered"); +        if (lastAnswered != null && lastAnswered.length() > 0) { +            sb.append("\"lastAnswered\": \"" + lastAnswered + "\", ");          }          // Compute score -        ColumnList<String> row = mDb.getRow("assessments", mUserId); -        if (!row.isEmpty()) { +        Map<String, String> row = mAnswerProvider.query(mUserId); +        if (row.size() > 0) {              Score score = new Score();              boolean scoringDone = false;              int totalAnswers = 0; -            for (Column<String> c : row) { -                if (c.getName().equals("lastAnswered") || c.getName().equals("summary")) { +            for (Map.Entry<String, String> c : row.entrySet()) { +                if (c.getKey().equals("lastAnswered") || c.getKey().equals("summary")) {                      continue;                  }                  try { -                    Question question = mQuestionProvider.get(c.getName()); -                    RecordedAnswer userAnswer = MAPPER.readValue(c.getStringValue(), RecordedAnswer.class); +                    Question question = mQuestionProvider.get(c.getKey()); +                    RecordedAnswer userAnswer = MAPPER.readValue(c.getValue(), RecordedAnswer.class);                      if (question == null) { -                        LOG.warn("Answer for unknown question: " + c.getName()); +                        LOG.warn("Answer for unknown question: " + c.getKey());                          continue;                      } -                    LOG.debug("Scoring questionId: " + c.getName()); +                    LOG.debug("Scoring questionId: " + c.getKey());                      scoringDone = !question.scoreAnswer(score, userAnswer);                  } catch (Exception e) {                      LOG.error("Failed to score question: {userid: \"" + mUserId + -                            "\", questionid:\"" + c.getName() + -                            "\", userAnswer:\"" + c.getStringValue() + "\"}", e); +                            "\", questionid:\"" + c.getKey() + +                            "\", userAnswer:\"" + c.getValue() + "\"}", e);                  }                  totalAnswers++;              } -            sb.append(", \"score\":" + score.getScore()); +            sb.append("\"score\":" + score.getScore());              sb.append(", \"sum\":" + score.getSum());              sb.append(", \"count\":" + score.getCount());              sb.append(", \"totalAnswers\":" + totalAnswers); @@ -240,7 +246,7 @@ public class SurveyResultsResource extends ServerResource {          String summary = sb.toString();          // Persist summary -        mDb.putKey("assessments", mUserId, "summary", 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 index 3d9d67f..b316b75 100644 --- a/src/com/p4square/grow/backend/resources/TrainingRecordResource.java +++ b/src/com/p4square/grow/backend/resources/TrainingRecordResource.java @@ -11,9 +11,6 @@ import java.util.List;  import java.util.Map;  import java.util.HashMap; -import com.netflix.astyanax.model.Column; -import com.netflix.astyanax.model.ColumnList; -  import com.fasterxml.jackson.databind.ObjectMapper;  import org.restlet.data.MediaType; @@ -27,16 +24,17 @@ import org.restlet.ext.jackson.JacksonRepresentation;  import org.apache.log4j.Logger;  import com.p4square.grow.backend.GrowBackend; -import com.p4square.grow.backend.db.CassandraDatabase;  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.provider.JsonEncodedProvider;  import com.p4square.grow.model.Score; @@ -52,8 +50,8 @@ public class TrainingRecordResource extends ServerResource {          SUMMARY, VIDEO      } -    private CassandraDatabase mDb;      private Provider<String, TrainingRecord> mTrainingRecordProvider; +    private CollectionProvider<String, String, String> mAnswerProvider;      private RequestType mRequestType;      private String mUserId; @@ -64,8 +62,8 @@ public class TrainingRecordResource extends ServerResource {      public void doInit() {          super.doInit(); -        mDb = ((GrowBackend) getApplication()).getDatabase();          mTrainingRecordProvider = ((ProvidesTrainingRecords) getApplication()).getTrainingRecordProvider(); +        mAnswerProvider = ((ProvidesAssessments) getApplication()).getAnswerProvider();          mUserId = getAttribute("userId");          mVideoId = getAttribute("videoId"); @@ -184,7 +182,7 @@ public class TrainingRecordResource extends ServerResource {          double assessedScore = 0;          try { -            String summaryString = mDb.getKey("assessments", userId, "summary"); +            String summaryString = mAnswerProvider.get(userId, "summary");              if (summaryString == null) {                  LOG.warn("Asked to create training record for unassessed user " + userId);                  return; diff --git a/src/com/p4square/grow/backend/resources/TrainingResource.java b/src/com/p4square/grow/backend/resources/TrainingResource.java index 85d08c1..6efdfab 100644 --- a/src/com/p4square/grow/backend/resources/TrainingResource.java +++ b/src/com/p4square/grow/backend/resources/TrainingResource.java @@ -4,8 +4,8 @@  package com.p4square.grow.backend.resources; -import com.netflix.astyanax.model.Column; -import com.netflix.astyanax.model.ColumnList; +import java.io.IOException; +import java.util.Map;  import org.restlet.data.Status;  import org.restlet.resource.ServerResource; @@ -17,16 +17,16 @@ 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 cLog = Logger.getLogger(TrainingResource.class); +    private final static Logger LOG = Logger.getLogger(TrainingResource.class); -    private GrowBackend mBackend; -    private CassandraDatabase mDb; +    private CollectionProvider<String, String, String> mVideoProvider;      private String mLevel;      private String mVideoId; @@ -35,8 +35,8 @@ public class TrainingResource extends ServerResource {      public void doInit() {          super.doInit(); -        mBackend = (GrowBackend) getApplication(); -        mDb = mBackend.getDatabase(); +        GrowBackend backend = (GrowBackend) getApplication(); +        mVideoProvider = backend.getVideoProvider();          mLevel = getAttribute("level");          mVideoId = getAttribute("videoId"); @@ -54,35 +54,44 @@ public class TrainingResource extends ServerResource {              return null;          } -        if (mVideoId == null) { -            // Get all videos -            ColumnList<String> row = mDb.getRow("strings", "/training/" + mLevel); -            if (!row.isEmpty()) { -                StringBuilder sb = new StringBuilder("{ \"level\": \"" + mLevel + "\""); -                sb.append(", \"videos\": ["); -                boolean first = true; -                for (Column<String> c : row) { -                    if (!first) { -                        sb.append(", "); +        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(c.getStringValue()); -                    first = false; +                    sb.append("] }"); +                    result = sb.toString();                  } -                sb.append("] }"); -                result = sb.toString(); + +            } else { +                // Get single video +                result = mVideoProvider.get(mLevel, mVideoId);              } -        } else { -            // Get single video -            result = mDb.getKey("strings", "/training/" + mLevel, mVideoId); -        } +            if (result == null) { +                // 404 +                setStatus(Status.CLIENT_ERROR_NOT_FOUND); +                return null; +            } -        if (result == null) { -            // 404 -            setStatus(Status.CLIENT_ERROR_NOT_FOUND); +            return new StringRepresentation(result); + +        } catch (IOException e) { +            LOG.error("IOException fetch video: " + e.getMessage(), e); +            setStatus(Status.SERVER_ERROR_INTERNAL);              return null;          } - -        return new StringRepresentation(result);      }  } diff --git a/src/com/p4square/grow/provider/DelegateCollectionProvider.java b/src/com/p4square/grow/provider/DelegateCollectionProvider.java new file mode 100644 index 0000000..e17af87 --- /dev/null +++ b/src/com/p4square/grow/provider/DelegateCollectionProvider.java @@ -0,0 +1,69 @@ +/* + * Copyright 2014 Jesse Morgan + */ + +package com.p4square.grow.provider; + +import java.io.IOException; +import java.util.HashMap; +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 HashMap<>(); +        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 index 66c5666..42dcc63 100644 --- a/src/com/p4square/grow/provider/DelegateProvider.java +++ b/src/com/p4square/grow/provider/DelegateProvider.java @@ -31,10 +31,10 @@ public abstract class DelegateProvider<K, D, V> implements Provider<K, V> {      }      /** -     * Make a Key for questionId. +     * Make a Key for the delegated provider.       * -     * @param questionId The question id. -     * @return a key for questionId. +     * @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 index 7ae3f71..7651443 100644 --- a/src/com/p4square/grow/provider/JsonEncodedProvider.java +++ b/src/com/p4square/grow/provider/JsonEncodedProvider.java @@ -25,8 +25,8 @@ public abstract class JsonEncodedProvider<V> {          MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);      } -    private final Class<V> mClazz; -    private final JavaType mType; +    protected final Class<V> mClazz; +    protected final JavaType mType;      public JsonEncodedProvider(Class<V> clazz) {          mClazz = clazz; diff --git a/src/com/p4square/grow/provider/ProvidesAssessments.java b/src/com/p4square/grow/provider/ProvidesAssessments.java new file mode 100644 index 0000000..62ba8f6 --- /dev/null +++ b/src/com/p4square/grow/provider/ProvidesAssessments.java @@ -0,0 +1,20 @@ +/* + * 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/ProvidesStrings.java b/src/com/p4square/grow/provider/ProvidesStrings.java new file mode 100644 index 0000000..5d9976e --- /dev/null +++ b/src/com/p4square/grow/provider/ProvidesStrings.java @@ -0,0 +1,19 @@ +/* + * 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 index 27ffa3e..586e649 100644 --- a/src/com/p4square/grow/provider/ProvidesTrainingRecords.java +++ b/src/com/p4square/grow/provider/ProvidesTrainingRecords.java @@ -4,7 +4,10 @@  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. @@ -16,4 +19,9 @@ 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/ProvidesVideos.java b/src/com/p4square/grow/provider/ProvidesVideos.java new file mode 100644 index 0000000..3d055d3 --- /dev/null +++ b/src/com/p4square/grow/provider/ProvidesVideos.java @@ -0,0 +1,16 @@ +/* + * 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(); +}  | 
