Details
diff --git a/usage/src/main/java/com/ning/billing/usage/timeline/shutdown/ShutdownSaveMode.java b/usage/src/main/java/com/ning/billing/usage/timeline/shutdown/ShutdownSaveMode.java
new file mode 100644
index 0000000..9ce0ebe
--- /dev/null
+++ b/usage/src/main/java/com/ning/billing/usage/timeline/shutdown/ShutdownSaveMode.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.usage.timeline.shutdown;
+
+public enum ShutdownSaveMode {
+ SAVE_ALL_TIMELINES, // Save all timelines in the db
+ SAVE_START_TIMES; // Save just the start times for each timeline, and use the replay facility to reconstruct the accumulators on startup
+
+ public static ShutdownSaveMode fromString(final String mode) {
+ for (final ShutdownSaveMode s : ShutdownSaveMode.values()) {
+ if (s.name().equalsIgnoreCase(mode)) {
+ return s;
+ }
+ }
+ throw new IllegalArgumentException(String.format("The argument %s was supposed to be a ShutdownSaveMode, but was not", mode));
+ }
+}
diff --git a/usage/src/main/java/com/ning/billing/usage/timeline/shutdown/StartTimes.java b/usage/src/main/java/com/ning/billing/usage/timeline/shutdown/StartTimes.java
new file mode 100644
index 0000000..7e1bc9d
--- /dev/null
+++ b/usage/src/main/java/com/ning/billing/usage/timeline/shutdown/StartTimes.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.usage.timeline.shutdown;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.joda.time.DateTime;
+
+/**
+ * This class is used solely as a Json mapping class when saving timelines in a database
+ * blob on shutdown, and restoring them on startup.
+ * <p/>
+ * The Map<Integer, Map<Integer, DateTime>> maps from sourceId to eventCategoryId to startTime.
+ */
+public class StartTimes {
+
+ private final DateTime timeInserted;
+ private final Map<Integer, Map<Integer, DateTime>> startTimesMap;
+ private DateTime minStartTime;
+
+ public StartTimes(final DateTime timeInserted, final Map<Integer, Map<Integer, DateTime>> startTimesMap) {
+ this.timeInserted = timeInserted;
+ this.startTimesMap = startTimesMap;
+ DateTime minDateTime = new DateTime(Long.MAX_VALUE);
+ for (final Map<Integer, DateTime> categoryMap : startTimesMap.values()) {
+ for (final DateTime startTime : categoryMap.values()) {
+ if (minDateTime.isAfter(startTime)) {
+ minDateTime = startTime;
+ }
+ }
+ }
+ this.minStartTime = minDateTime;
+ }
+
+ public StartTimes() {
+ this.timeInserted = new DateTime();
+ minStartTime = new DateTime(Long.MAX_VALUE);
+ this.startTimesMap = new HashMap<Integer, Map<Integer, DateTime>>();
+ }
+
+ public void addTime(final int sourceId, final int categoryId, final DateTime dateTime) {
+ Map<Integer, DateTime> sourceTimes = startTimesMap.get(sourceId);
+ if (sourceTimes == null) {
+ sourceTimes = new HashMap<Integer, DateTime>();
+ startTimesMap.put(sourceId, sourceTimes);
+ }
+ sourceTimes.put(categoryId, dateTime);
+ if (dateTime.isBefore(minStartTime)) {
+ minStartTime = dateTime;
+ }
+ }
+
+ public DateTime getStartTimeForSourceIdAndCategoryId(final int sourceId, final int categoryId) {
+ final Map<Integer, DateTime> sourceTimes = startTimesMap.get(sourceId);
+ if (sourceTimes != null) {
+ return sourceTimes.get(categoryId);
+ } else {
+ return null;
+ }
+ }
+
+ public Map<Integer, Map<Integer, DateTime>> getStartTimesMap() {
+ return startTimesMap;
+ }
+
+ public DateTime getTimeInserted() {
+ return timeInserted;
+ }
+
+ public DateTime getMinStartTime() {
+ return minStartTime;
+ }
+}
diff --git a/usage/src/main/java/com/ning/billing/usage/timeline/shutdown/StartTimesBinder.java b/usage/src/main/java/com/ning/billing/usage/timeline/shutdown/StartTimesBinder.java
new file mode 100644
index 0000000..00c08c0
--- /dev/null
+++ b/usage/src/main/java/com/ning/billing/usage/timeline/shutdown/StartTimesBinder.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.usage.timeline.shutdown;
+
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.skife.jdbi.v2.SQLStatement;
+import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.BinderFactory;
+import org.skife.jdbi.v2.sqlobject.BindingAnnotation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.ning.billing.usage.timeline.DateTimeUtils;
+import com.ning.billing.usage.timeline.shutdown.StartTimesBinder.StartTimesBinderFactory;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+@BindingAnnotation(StartTimesBinderFactory.class)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.PARAMETER})
+public @interface StartTimesBinder {
+
+ public static class StartTimesBinderFactory implements BinderFactory {
+
+ private static final Logger log = LoggerFactory.getLogger(StartTimesBinderFactory.class);
+ private static final ObjectMapper mapper = new ObjectMapper();
+
+ public Binder build(final Annotation annotation) {
+ return new Binder<StartTimesBinder, StartTimes>() {
+ public void bind(final SQLStatement query, final StartTimesBinder binder, final StartTimes startTimes) {
+ try {
+ final String s = mapper.writeValueAsString(startTimes.getStartTimesMap());
+ query.bind("startTimes", s)
+ .bind("timeInserted", DateTimeUtils.unixSeconds(startTimes.getTimeInserted()));
+ } catch (IOException e) {
+ log.error("Exception while binding StartTimes", e);
+ }
+ }
+ };
+ }
+ }
+}
diff --git a/usage/src/main/java/com/ning/billing/usage/timeline/shutdown/StartTimesMapper.java b/usage/src/main/java/com/ning/billing/usage/timeline/shutdown/StartTimesMapper.java
new file mode 100644
index 0000000..29ac314
--- /dev/null
+++ b/usage/src/main/java/com/ning/billing/usage/timeline/shutdown/StartTimesMapper.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.usage.timeline.shutdown;
+
+import java.io.IOException;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Map;
+
+import org.joda.time.DateTime;
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.tweak.ResultSetMapper;
+
+import com.ning.billing.usage.timeline.DateTimeUtils;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class StartTimesMapper implements ResultSetMapper<StartTimes> {
+
+ private static final ObjectMapper mapper = new ObjectMapper();
+
+ @Override
+ public StartTimes map(final int index, final ResultSet r, final StatementContext ctx) throws SQLException {
+ try {
+ return new StartTimes(DateTimeUtils.dateTimeFromUnixSeconds(r.getInt("time_inserted")),
+ (Map<Integer, Map<Integer, DateTime>>) mapper.readValue(r.getBlob("start_times").getBinaryStream(), new TypeReference<Map<Integer, Map<Integer, DateTime>>>() {
+ }));
+ } catch (IOException e) {
+ throw new IllegalStateException(String.format("Could not decode the StartTimes map"), e);
+ }
+ }
+}