diff --git a/s3auth-hosts/pom.xml b/s3auth-hosts/pom.xml
index 2608cce3..2e024472 100644
--- a/s3auth-hosts/pom.xml
+++ b/s3auth-hosts/pom.xml
@@ -143,6 +143,16 @@
com.jcabi
jcabi-xml
+
+ com.jcabi
+ jcabi-jdbc
+ 0.12.1
+
+
+ com.h2database
+ h2
+ 1.3.176
+
diff --git a/s3auth-hosts/src/main/java/com/s3auth/hosts/DomainStatsData.java b/s3auth-hosts/src/main/java/com/s3auth/hosts/DomainStatsData.java
index 188e2bd3..2360a138 100644
--- a/s3auth-hosts/src/main/java/com/s3auth/hosts/DomainStatsData.java
+++ b/s3auth-hosts/src/main/java/com/s3auth/hosts/DomainStatsData.java
@@ -29,6 +29,7 @@
*/
package com.s3auth.hosts;
+import java.io.IOException;
import java.util.Map;
/**
@@ -37,26 +38,29 @@
* @author Carlos Miranda (miranda.cma@gmail.com)
* @version $Id$
*/
-public interface DomainStatsData {
+interface DomainStatsData {
/**
* Post the statistics of the given domain, for this particular time.
* @param domain The domain of this stats.
* @param stats The stats to keep.
+ * @throws IOException If something goes wrong.
*/
- void put(final String domain, final Stats stats);
+ void put(final String domain, final Stats stats) throws IOException;
/**
* Get the stats for the given domain.
* @param domain The domain whose stats we're interested in
* @return The stats for this domain
+ * @throws IOException If something goes wrong.
*/
- Stats get(final String domain);
+ Stats get(final String domain) throws IOException;
/**
* Get the stats for all domains.
* @return Map of each domain and their corresponding stats.
+ * @throws IOException If something goes wrong.
*/
- Map all();
+ Map all() throws IOException;
}
diff --git a/s3auth-hosts/src/main/java/com/s3auth/hosts/H2DomainStatsData.java b/s3auth-hosts/src/main/java/com/s3auth/hosts/H2DomainStatsData.java
new file mode 100644
index 00000000..bdb957fb
--- /dev/null
+++ b/s3auth-hosts/src/main/java/com/s3auth/hosts/H2DomainStatsData.java
@@ -0,0 +1,177 @@
+/**
+ * Copyright (c) 2012, s3auth.com
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met: 1) Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer. 2) Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution. 3) Neither the name of the s3auth.com nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
+ * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.s3auth.hosts;
+
+import com.jcabi.aspects.Immutable;
+import com.jcabi.aspects.Loggable;
+import com.jcabi.jdbc.JdbcSession;
+import com.jcabi.jdbc.Outcome;
+import java.io.File;
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Map;
+import java.util.Properties;
+import lombok.EqualsAndHashCode;
+import org.h2.Driver;
+
+/**
+ * Storage of {@link Stats} per domain with H2 Database.
+ *
+ * @author Carlos Miranda (miranda.cma@gmail.com)
+ * @version $Id$
+ * @checkstyle ClassDataAbstractionCouplingCheck (500 lines)
+ */
+@Immutable
+@EqualsAndHashCode(of = "jdbc")
+@Loggable(Loggable.DEBUG)
+final class H2DomainStatsData implements DomainStatsData {
+ /**
+ * Create Table statement.
+ */
+ private static final String CREATE = new StringBuilder("CREATE TABLE ")
+ .append("IF NOT EXISTS DOMAIN_STATS( ")
+ .append("ID INT IDENTITY,")
+ .append("DOMAIN VARCHAR(255),")
+ .append("BYTES INT,")
+ .append("CREATE_TIME TIMESTAMP")
+ .append(" )").toString();
+
+ /**
+ * Insert statement.
+ */
+ private static final String INSERT = new StringBuilder("INSERT INTO ")
+ .append("DOMAIN_STATS (DOMAIN, BYTES, CREATE_TIME) ")
+ .append("values (?, ?, CURRENT_TIMESTAMP())").toString();
+
+ /**
+ * Outcome for obtaining a single Stats per domain.
+ */
+ private static final Outcome STATS = new Outcome() {
+ @Override
+ public Stats handle(final ResultSet rset, final Statement stmt)
+ throws SQLException {
+ rset.next();
+ return new Simple(rset.getLong(1));
+ }
+ };
+
+ /**
+ * The JDBC URL.
+ */
+ private final transient String jdbc;
+
+ /**
+ * Public ctor.
+ * @throws IOException If an IO Exception occurs.
+ */
+ H2DomainStatsData() throws IOException {
+ this(new File("s3auth-domainStats"));
+ }
+
+ /**
+ * Public ctor.
+ * @param file The file pointing to the database to use.
+ * @throws IOException If an IO Exception occurs.
+ */
+ H2DomainStatsData(final File file) throws IOException {
+ this.jdbc = String.format("jdbc:h2:file:%s", file.getAbsolutePath());
+ try {
+ new JdbcSession(this.connection()).sql(CREATE).execute();
+ } catch (final SQLException ex) {
+ throw new IOException(ex);
+ }
+ }
+
+ @Override
+ public void put(final String domain, final Stats stats) throws IOException {
+ try {
+ new JdbcSession(this.connection())
+ .sql(INSERT)
+ .set(domain)
+ .set(stats.bytesTransferred())
+ .execute();
+ } catch (final SQLException ex) {
+ throw new IOException(ex);
+ }
+ }
+
+ @Override
+ public Stats get(final String domain) throws IOException {
+ try {
+ return new JdbcSession(this.connection())
+ .sql("SELECT SUM(BYTES) FROM DOMAIN_STATS WHERE DOMAIN = ?")
+ .set(domain)
+ .select(STATS);
+ } catch (final SQLException ex) {
+ throw new IOException(ex);
+ }
+ }
+
+ @Override
+ public Map all() throws IOException {
+ throw new UnsupportedOperationException("Not yet implemented.");
+ }
+
+ /**
+ * Make data source.
+ * @return Data source for JDBC
+ * @throws SQLException If it fails
+ */
+ private Connection connection() throws SQLException {
+ return new Driver().connect(this.jdbc, new Properties());
+ }
+
+ /**
+ * Simple stats.
+ */
+ @Immutable
+ private static final class Simple implements Stats {
+ /**
+ * Bytes transferred.
+ */
+ private final transient long bytes;
+
+ /**
+ * Ctor.
+ * @param transferred Number of bytes transferred.
+ */
+ public Simple(final long transferred) {
+ this.bytes = transferred;
+ }
+
+ @Override
+ public long bytesTransferred() {
+ return this.bytes;
+ }
+ }
+}
diff --git a/s3auth-hosts/src/test/java/com/s3auth/hosts/H2DomainStatsDataTest.java b/s3auth-hosts/src/test/java/com/s3auth/hosts/H2DomainStatsDataTest.java
new file mode 100644
index 00000000..89c492f1
--- /dev/null
+++ b/s3auth-hosts/src/test/java/com/s3auth/hosts/H2DomainStatsDataTest.java
@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2012, s3auth.com
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met: 1) Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer. 2) Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution. 3) Neither the name of the s3auth.com nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
+ * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.s3auth.hosts;
+
+import com.jcabi.aspects.Tv;
+import java.io.File;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.Test;
+
+/**
+ * Test case for {@link H2DomainStatsData}.
+ *
+ * @author Carlos Miranda (miranda.cma@gmail.com)
+ * @version $Id$
+ */
+public final class H2DomainStatsDataTest {
+
+ /**
+ * H2DomainStatsData can put and get data for a single domain.
+ * @throws Exception If something goes wrong.
+ */
+ @Test
+ public void putsAndGetsDataPerDomain() throws Exception {
+ final H2DomainStatsData data = new H2DomainStatsData(
+ File.createTempFile("test", "temp")
+ );
+ final String domain = "test-put-domain";
+ final long bytes = Tv.HUNDRED;
+ data.put(domain,
+ new Stats() {
+ @Override
+ public long bytesTransferred() {
+ return bytes;
+ }
+ }
+ );
+ final long result = data.get(domain).bytesTransferred();
+ MatcherAssert.assertThat(
+ result, Matchers.is(bytes)
+ );
+ }
+
+}