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) + ); + } + +}