diff options
author | Jesse Morgan <jesse@jesterpm.net> | 2014-05-23 13:05:19 -0700 |
---|---|---|
committer | Jesse Morgan <jesse@jesterpm.net> | 2014-05-23 13:05:19 -0700 |
commit | ef862e30975db691339fd143d2c4d60eb5132a3e (patch) | |
tree | 2388b461e98a6c6443294a172ddb9ab789de8a26 | |
parent | 728c8cfae2312e68fefd49241436517afb4c6134 (diff) |
Abstracting out Cassandra.
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(); +} |