diff --git a/pom.xml b/pom.xml index e34accb8d..01507fb76 100755 --- a/pom.xml +++ b/pom.xml @@ -47,6 +47,30 @@ + + jrebel + + + + + + + + org.zeroturnaround + jrebel-maven-plugin + + + generate-rebel-xml + process-resources + + generate + + + + + + + elasticsearch-remote @@ -85,7 +109,7 @@ 80 0 - INFO + DEBUG PAPERTRAIL logs.papertrailapp.com 38143 @@ -139,7 +163,7 @@ 0 - WARN + DEBUG CONSOLE 64M true @@ -282,6 +306,26 @@ + + org.springframework.data + spring-data-cassandra + 1.3.1.RELEASE + + + com.datastax.cassandra + cassandra-driver-core + ${datastax.version} + + + com.datastax.cassandra + cassandra-driver-mapping + ${datastax.version} + + + com.rometools + rome + 1.5.0 + org.codehaus.groovy groovy-all @@ -323,6 +367,7 @@ ${selenium.version} test + org.apache.directory.server apacheds-all @@ -393,18 +438,18 @@ src/main/webapp/assets/**.* UTF-8 - 1.6 + 1.8 - 3.2.3.RELEASE - 3.1.3.RELEASE + 4.2.2.RELEASE + 4.0.3.RELEASE 0.20.6 1.6.11 1.7.5 1.0.13 4.2.2 4.2.2 - 1.2.6 + 2.2.3 1.1-4 3.1-09 1.0.2 @@ -420,13 +465,14 @@ 4.11 1.3 1.3.5 - 1.2.0.1 + 2.1.3.1 1.9.0 1.0 1.2.1 1.7 2.2.0 2.11.0 + 2.1.9 2.31.0 @@ -436,7 +482,7 @@ 2.14 2.3 8.1.13.v20130916 - 1.2.1-1 + 2.1.7-1 1.7.0 2.9 Tomcat @@ -467,6 +513,40 @@ + + io.springfox + springfox-swagger2 + 2.2.2 + + + org.mapstruct + mapstruct + + + + + org.mapstruct + mapstruct-jdk8 + 1.0.0.Final + + + + ch.qos.logback + logback-core + ${logback.version} + + + + ch.qos.logback + logback-classic + ${logback.version} + + + + javax.validation + validation-api + ${javax.validation.api.version} + com.fasterxml.jackson.datatype jackson-datatype-json-org @@ -483,9 +563,14 @@ ${jackson.version} - com.notnoop.apns - apns - 0.2.3 + org.hibernate + hibernate-validator + ${hibernate.validator.version} + + + joda-time + joda-time + ${jodatime.version} com.yammer.metrics @@ -553,84 +638,6 @@ jstl ${jstl.version} - - javax.validation - validation-api - ${javax.validation.api.version} - - - joda-time - joda-time - ${jodatime.version} - - - - ch.qos.logback - logback-core - ${logback.version} - - - - ch.qos.logback - logback-classic - ${logback.version} - - - - net.sf.ehcache - ehcache-core - 2.6.5 - - - net.sf.ehcache - ehcache-web - 2.0.4 - - - org.apache.camel - camel-core - ${camel.version} - - - org.apache.camel - camel-spring - ${camel.version} - - - org.apache.camel - camel-script - ${camel.version} - - - org.apache.camel - camel-rss - ${camel.version} - - - org.apache.camel - camel-twitter - ${camel.version} - - - org.apache.camel - camel-stream - ${camel.version} - - - org.apache.httpcomponents - httpcore - ${httpclient.core.version} - - - org.apache.httpcomponents - httpclient - ${httpclient.client.version} - - - org.apache.geronimo.javamail - geronimo-javamail_1.4_mail - 1.8.2 - org.apache.openjpa openjpa @@ -655,58 +662,19 @@ jar compile + - org.atmosphere - atmosphere-annotations - 1.1.0.RC3 + org.apache.geronimo.javamail + geronimo-javamail_1.4_mail + 1.8.2 + org.elasticsearch elasticsearch ${elasticsearch.version} - - org.hectorclient - hector-core - ${hector.version} - - - javax.servlet - servlet-api - - - - - org.hectorclient - hector-object-mapper - ${hector.mapper.version} - - - org.mortbay.jetty - servlet-api - - - - - org.hibernate - hibernate-validator - ${hibernate.validator.version} - - - org.pegdown - pegdown - ${pegdown.version} - - - org.slf4j - slf4j-api - ${slf4j.version} - - - org.slf4j - jcl-over-slf4j - ${slf4j.version} - + org.springframework spring-aop @@ -787,6 +755,37 @@ spring-security-web ${spring.security.version} + + com.rometools + rome + 1.5.0 + + + org.pegdown + pegdown + ${pegdown.version} + + + com.datastax.cassandra + cassandra-driver-core + ${datastax.version} + + + com.datastax.cassandra + cassandra-driver-mapping + ${datastax.version} + + + + com.notnoop.apns + apns + 0.2.3 + + + org.atmosphere + atmosphere-annotations + 1.1.0.RC3 + org.pac4j pac4j-oauth @@ -797,44 +796,54 @@ spring-security-pac4j 1.2.4 + + - rome - rome - ${rome.version} + org.cassandraunit + cassandra-unit + 2.1.9.2 + test + + + + org.springframework.data + spring-data-cassandra + 1.3.1.RELEASE - - org.apache.cassandra - cassandra-all - ${cassandra.version} - - - javax.servlet - servlet-api - - - org.mortbay.jetty - servlet-api - - - org.mortbay.jetty - jetty - - - org.mortbay.jetty - jetty-util - - - test + org.apache.camel + camel-core + ${camel.version} - com.jayway.jsonpath - json-path - 0.8.1 - test + org.apache.camel + camel-spring + ${camel.version} + + + org.apache.camel + camel-script + ${camel.version} + + + org.apache.camel + camel-rss + ${camel.version} + + + org.apache.camel + camel-twitter + ${camel.version} + + org.apache.camel + camel-stream + ${camel.version} + + + org.springframework spring-test @@ -865,18 +874,6 @@ ${awaitility.version} test - - org.cassandraunit - cassandra-unit - ${cassandra.unit.version} - test - - - hamcrest-all - org.hamcrest - - - org.mockito mockito-all @@ -889,6 +886,28 @@ ${camel.version} test + + net.sf.ehcache + ehcache-core + 2.6.5 + + + net.sf.ehcache + ehcache-web + 2.0.4 + + + + org.slf4j + slf4j-log4j12 + 1.7.7 + + + + com.google.guava + guava + 18.0 + @@ -957,8 +976,8 @@ ${maven.surefire.version} ${project.testresult.directory}/surefire-reports - -XX:MaxPermSize=128m -Xmx256m ${surefireArgLine} - ${skip.unit.tests} + ${surefireArgLine} + alphabetical - - org.codehaus.mojo - cassandra-maven-plugin - ${maven.cassandra.version} - - - org.apache.cassandra - cassandra-all - ${cassandra.version} - - - org.mortbay.jetty jetty-maven-plugin @@ -1070,6 +1077,20 @@ + + org.zeroturnaround + jrebel-maven-plugin + 1.1.5 + + + generate-rebel-xml + process-resources + + generate + + + + diff --git a/services/src/main/java/fr/ippon/tatami/config/ApplicationConfiguration.java b/services/src/main/java/fr/ippon/tatami/config/ApplicationConfiguration.java index 03ff7fde0..2ec2dcc54 100644 --- a/services/src/main/java/fr/ippon/tatami/config/ApplicationConfiguration.java +++ b/services/src/main/java/fr/ippon/tatami/config/ApplicationConfiguration.java @@ -1,14 +1,26 @@ package fr.ippon.tatami.config; -import org.apache.thrift.transport.TTransportException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.*; import org.springframework.core.env.Environment; +import org.springframework.http.ResponseEntity; +import org.springframework.util.StopWatch; +import org.springframework.web.bind.annotation.RequestMethod; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; import javax.annotation.PostConstruct; import javax.inject.Inject; +import javax.servlet.ServletContext; import java.io.IOException; +import java.time.LocalDate; +import java.util.Date; + +import static springfox.documentation.builders.PathSelectors.regex; @Configuration @PropertySource({"classpath:/META-INF/tatami/tatami.properties", @@ -32,6 +44,40 @@ public class ApplicationConfiguration { @Inject private Environment env; + public static final String DEFAULT_INCLUDE_PATTERN = "/tatami/.*"; + + @Bean + public Docket swaggerSpringfoxDocket() { + log.debug("Starting Swagger"); + StopWatch watch = new StopWatch(); + watch.start(); + ApiInfo apiInfo = new ApiInfo( + env.getProperty("swagger.title"), + env.getProperty("swagger.description"), + env.getProperty("swagger.version"), + env.getProperty("swagger.termsOfServiceUrl"), + env.getProperty("swagger.contact"), + env.getProperty("swagger.license"), + env.getProperty("swagger.licenseUrl")); + + Docket docket = new Docket(DocumentationType.SWAGGER_2) + .apiInfo(apiInfo) + .select() + .apis(RequestHandlerSelectors.any()) + .paths(PathSelectors.any()) + .build() + .directModelSubstitute(LocalDate.class, + String.class) + .genericModelSubstitutes(ResponseEntity.class) + .useDefaultResponseMessages(false) + .enableUrlTemplating(true) + .pathMapping("/tatami"); + watch.stop(); + log.debug("Started Swagger in {} ms", watch.getTotalTimeMillis()); + return docket; + } + + /** * Initializes Tatami. *

@@ -43,7 +89,7 @@ public class ApplicationConfiguration { * - "tatamibot" : for enabling the Tatami bot */ @PostConstruct - public void initTatami() throws IOException, TTransportException { + public void initTatami() throws IOException { log.debug("Looking for Spring profiles... Available profiles are \"metrics\", \"tatamibot\" and \"apple-push\""); if (env.getActiveProfiles().length == 0) { log.debug("No Spring profile configured, running with default configuration"); diff --git a/services/src/main/java/fr/ippon/tatami/config/AsyncConfiguration.java b/services/src/main/java/fr/ippon/tatami/config/AsyncConfiguration.java index db426ed88..4287a6b1a 100644 --- a/services/src/main/java/fr/ippon/tatami/config/AsyncConfiguration.java +++ b/services/src/main/java/fr/ippon/tatami/config/AsyncConfiguration.java @@ -4,6 +4,8 @@ import fr.ippon.tatami.service.elasticsearch.ElasticsearchSearchService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; +import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.AsyncConfigurer; @@ -37,4 +39,9 @@ public Executor getAsyncExecutor() { executor.initialize(); return executor; } + + @Override + public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { + return new SimpleAsyncUncaughtExceptionHandler(); + } } diff --git a/services/src/main/java/fr/ippon/tatami/config/CassandraConfiguration.java b/services/src/main/java/fr/ippon/tatami/config/CassandraConfiguration.java index 8bfdc7dac..c6fb6b64a 100644 --- a/services/src/main/java/fr/ippon/tatami/config/CassandraConfiguration.java +++ b/services/src/main/java/fr/ippon/tatami/config/CassandraConfiguration.java @@ -1,28 +1,43 @@ package fr.ippon.tatami.config; -import me.prettyprint.cassandra.connection.HOpTimer; -import me.prettyprint.cassandra.connection.MetricsOpTimer; -import me.prettyprint.cassandra.model.ConfigurableConsistencyLevel; -import me.prettyprint.cassandra.service.CassandraHostConfigurator; -import me.prettyprint.cassandra.service.ThriftCfDef; -import me.prettyprint.cassandra.service.ThriftCluster; -import me.prettyprint.cassandra.service.ThriftKsDef; -import me.prettyprint.hector.api.Cluster; -import me.prettyprint.hector.api.HConsistencyLevel; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.ddl.ColumnFamilyDefinition; -import me.prettyprint.hector.api.ddl.ComparatorType; -import me.prettyprint.hector.api.ddl.KeyspaceDefinition; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hom.EntityManagerImpl; +import com.datastax.driver.core.*; +import com.datastax.driver.core.policies.LatencyAwarePolicy; +import com.datastax.driver.core.policies.LoadBalancingPolicy; +import com.datastax.driver.core.policies.ReconnectionPolicy; +import com.datastax.driver.core.policies.RetryPolicy; +import com.google.common.collect.Lists; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.BeanFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; +import org.springframework.data.cassandra.config.CassandraEntityClassScanner; +import org.springframework.data.cassandra.config.CassandraSessionFactoryBean; +import org.springframework.data.cassandra.convert.CassandraConverter; +import org.springframework.data.cassandra.convert.MappingCassandraConverter; +import org.springframework.data.cassandra.core.CassandraAdminOperations; +import org.springframework.data.cassandra.core.CassandraAdminTemplate; +import org.springframework.data.cassandra.mapping.BasicCassandraMappingContext; +import org.springframework.data.cassandra.mapping.CassandraMappingContext; +import org.springframework.util.StringUtils; import javax.annotation.PreDestroy; import javax.inject.Inject; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static com.datastax.driver.core.ProtocolOptions.Compression.LZ4; +import static com.datastax.driver.core.ProtocolOptions.Compression.NONE; +import static com.datastax.driver.core.ProtocolOptions.Compression.SNAPPY; +import static com.datastax.driver.core.ProtocolVersion.V1; +import static com.datastax.driver.core.ProtocolVersion.V2; +import static com.datastax.driver.core.ProtocolVersion.V3; /** * Cassandra configuration file. @@ -32,6 +47,22 @@ @Configuration public class CassandraConfiguration { + public static final String CASSANDRA_CLUSTER_NAME = "cassandra.cluster"; + public static final String CASSANDRA_PORT = "cassandra.port"; + public static final String CASSANDRA_PROTOCOL_VERSION = "cassandra.protocolVersion"; + public static final String CASSANDRA_COMPRESSION = "cassandra.compression"; + public static final String CASSANDRA_LOAD_BALANCING_POLICY = "cassandra.loadBalancingPolicy"; + public static final String CASSANDRA_CONSISTENCY = "cassandra.consistency"; + public static final String CASSANDRA_SERIAL_CONSISTENCY = "cassandra.serialConsistency"; + public static final String CASSANDRA_FETCH_SIZE = "cassandra.fetchSize"; + public static final String CASSANDRA_RECONNECTION_POLICY = "cassandra.reconnectionPolicy"; + public static final String CASSANDRA_RETRY_POLICY = "cassandra.retryPolicy"; + public static final String CASSANDRA_USER = "cassandra.user"; + public static final String CASSANDRA_PASSWORD = "cassandra.password"; + public static final String CASSANDRA_CONNECT_TIMEOUT_MILLIS = "cassandra.connectTimeoutMillis"; + public static final String CASSANDRA_READ_TIMEOUT_MILLIS = "cassandra.readTimeoutMillis"; + public static final String CASSANDRA_SSL_ENABLED = "cassandra.sslEnabled"; + public static final String CASSANDRA_CONTACT_POINTS = "cassandra.contactPoints"; private final Logger log = LoggerFactory.getLogger(CassandraConfiguration.class); @Inject @@ -42,121 +73,687 @@ public class CassandraConfiguration { @PreDestroy public void destroy() { log.info("Closing Hector connection pool"); - myCluster.getConnectionManager().shutdown(); - HFactory.shutdownCluster(myCluster); } - @Bean - public Keyspace keyspaceOperator() { - log.info("Configuring Cassandra keyspace"); - String cassandraHost = env.getProperty("cassandra.host"); - String cassandraClusterName = env.getProperty("cassandra.clusterName"); - String cassandraKeyspace = env.getProperty("cassandra.keyspace"); - - CassandraHostConfigurator cassandraHostConfigurator = new CassandraHostConfigurator(cassandraHost); - cassandraHostConfigurator.setMaxActive(100); - if (env.acceptsProfiles(Constants.SPRING_PROFILE_METRICS)) { - log.debug("Cassandra Metrics monitoring enabled"); - HOpTimer hOpTimer = new MetricsOpTimer(cassandraClusterName); - cassandraHostConfigurator.setOpTimer(hOpTimer); - } - ThriftCluster cluster = new ThriftCluster(cassandraClusterName, cassandraHostConfigurator); - this.myCluster = cluster; // Keep a pointer to the cluster, as Hector is buggy and can't find it again... - ConfigurableConsistencyLevel consistencyLevelPolicy = new ConfigurableConsistencyLevel(); - consistencyLevelPolicy.setDefaultReadConsistencyLevel(HConsistencyLevel.ONE); - - KeyspaceDefinition keyspaceDef = cluster.describeKeyspace(cassandraKeyspace); - if (keyspaceDef == null) { - log.warn("Keyspace \" {} \" does not exist, creating it!", cassandraKeyspace); - keyspaceDef = new ThriftKsDef(cassandraKeyspace); - cluster.addKeyspace(keyspaceDef, true); - - addColumnFamily(cluster, ColumnFamilyKeys.USER_CF, 0); - addColumnFamily(cluster, ColumnFamilyKeys.FRIENDS_CF, 0); - addColumnFamily(cluster, ColumnFamilyKeys.FOLLOWERS_CF, 0); - addColumnFamily(cluster, ColumnFamilyKeys.STATUS_CF, 0); - addColumnFamily(cluster, ColumnFamilyKeys.DOMAIN_CF, 0); - addColumnFamily(cluster, ColumnFamilyKeys.REGISTRATION_CF, 0); - addColumnFamily(cluster, ColumnFamilyKeys.RSS_CF, 0); - addColumnFamily(cluster, ColumnFamilyKeys.MAILDIGEST_CF, 0); - addColumnFamily(cluster, ColumnFamilyKeys.SHARES_CF, 0); - addColumnFamily(cluster, ColumnFamilyKeys.DISCUSSION_CF, 0); - addColumnFamily(cluster, ColumnFamilyKeys.USER_TAGS_CF, 0); - addColumnFamily(cluster, ColumnFamilyKeys.TAG_FOLLOWERS_CF, 0); - addColumnFamily(cluster, ColumnFamilyKeys.GROUP_MEMBERS_CF, 0); - addColumnFamily(cluster, ColumnFamilyKeys.USER_GROUPS_CF, 0); - addColumnFamily(cluster, ColumnFamilyKeys.GROUP_CF, 0); - addColumnFamily(cluster, ColumnFamilyKeys.GROUP_DETAILS_CF, 0); - addColumnFamily(cluster, ColumnFamilyKeys.ATTACHMENT_CF, 0); - addColumnFamily(cluster, ColumnFamilyKeys.AVATAR_CF, 0); - addColumnFamily(cluster, ColumnFamilyKeys.DOMAIN_CONFIGURATION_CF, 0); - addColumnFamily(cluster, ColumnFamilyKeys.TATAMIBOT_CONFIGURATION_CF, 0); - addColumnFamily(cluster, ColumnFamilyKeys.APPLE_DEVICE_CF, 0); - - addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.TIMELINE_CF, 0); - addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.TIMELINE_SHARES_CF, 0); - addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.MENTIONLINE_CF, 0); - addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.USERLINE_CF, 0); - addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.USERLINE_SHARES_CF, 0); - addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.FAVLINE_CF, 0); - addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.TAGLINE_CF, 0); - addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.TRENDS_CF, 0); - addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.USER_TRENDS_CF, 0); - addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.GROUPLINE_CF, 0); - addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.USER_ATTACHMENT_CF, 0); - addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.STATUS_ATTACHMENT_CF, 0); - addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.DOMAINLINE_CF, 0); - addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.DOMAIN_TATAMIBOT_CF, 0); - - addColumnFamilyCounter(cluster, ColumnFamilyKeys.COUNTER_CF, 0); - addColumnFamilyCounter(cluster, ColumnFamilyKeys.TAG_COUNTER_CF, 0); - addColumnFamilyCounter(cluster, ColumnFamilyKeys.GROUP_COUNTER_CF, 0); - addColumnFamilyCounter(cluster, ColumnFamilyKeys.DAYLINE_CF, 0); - - //Tatami Bot CF - addColumnFamily(cluster, ColumnFamilyKeys.TATAMIBOT_DUPLICATE_CF, 0); - } - return HFactory.createKeyspace(cassandraKeyspace, cluster, consistencyLevelPolicy); + + + + + @Inject + BeanFactory beanFactory; + + + + /** + * Parse the load balancing policy. + */ + public LoadBalancingPolicy parseLbPolicy(String loadBalancingPolicyString) throws InstantiationException, + IllegalAccessException, ClassNotFoundException, NoSuchMethodException, SecurityException, + IllegalArgumentException, InvocationTargetException { + String lb_regex = "([a-zA-Z]*Policy)(\\()(.*)(\\))"; + Pattern lb_pattern = Pattern.compile(lb_regex); + if (!loadBalancingPolicyString.contains("(")) { + loadBalancingPolicyString += "()"; + } + Matcher lb_matcher = lb_pattern.matcher(loadBalancingPolicyString); + + if (lb_matcher.matches()) { + if (lb_matcher.groupCount() > 0) { + // Primary LB policy has been specified + String primaryLoadBalancingPolicy = lb_matcher.group(1); + String loadBalancingPolicyParams = lb_matcher.group(3); + return getLbPolicy(primaryLoadBalancingPolicy, loadBalancingPolicyParams); + } + } + return null; } - @Bean - public EntityManagerImpl entityManager(Keyspace keyspace) { - String[] packagesToScan = {"fr.ippon.tatami.domain", "fr.ippon.tatami.bot.config"}; - return new EntityManagerImpl(keyspace, packagesToScan); + /** + * Get the load balancing policy. + */ + public LoadBalancingPolicy getLbPolicy(String lbString, String parameters) throws ClassNotFoundException, + NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException { + LoadBalancingPolicy policy = null; + if (!lbString.contains(".")) { + lbString = "com.datastax.driver.core.policies." + lbString; + } + + if (parameters.length() > 0) { + // Child policy or parameters have been specified + String paramsRegex = "([^,]+\\(.+?\\))|([^,]+)"; + Pattern param_pattern = Pattern.compile(paramsRegex); + Matcher lb_matcher = param_pattern.matcher(parameters); + + ArrayList paramList = Lists.newArrayList(); + ArrayList primaryParametersClasses = Lists.newArrayList(); + int nb = 0; + while (lb_matcher.find()) { + if (lb_matcher.groupCount() > 0) { + try { + if (lb_matcher.group().contains("(") && !lb_matcher.group().trim().startsWith("(")) { + // We are dealing with child policies here + primaryParametersClasses.add(LoadBalancingPolicy.class); + // Parse and add child policy to the parameter list + paramList.add(parseLbPolicy(lb_matcher.group())); + nb++; + } else { + // We are dealing with parameters that are not policies here + String param = lb_matcher.group(); + if (param.contains("'") || param.contains("\"")) { + primaryParametersClasses.add(String.class); + paramList.add(new String(param.trim().replace("'", "").replace("\"", ""))); + } else if (param.contains(".") || param.toLowerCase().contains("(double)") || param + .toLowerCase().contains("(float)")) { + // gotta allow using float or double + if (param.toLowerCase().contains("(double)")) { + primaryParametersClasses.add(double.class); + paramList.add(Double.parseDouble(param.replace("(double)", "").trim())); + } else { + primaryParametersClasses.add(float.class); + paramList.add(Float.parseFloat(param.replace("(float)", "").trim())); + } + } else { + if (param.toLowerCase().contains("(long)")) { + primaryParametersClasses.add(long.class); + paramList.add(Long.parseLong(param.toLowerCase().replace("(long)", "").trim())); + } else { + primaryParametersClasses.add(int.class); + paramList.add(Integer.parseInt(param.toLowerCase().replace("(int)", "").trim())); + } + } + nb++; + } + } catch (Exception e) { + log.error("Could not parse the Cassandra load balancing policy! " + e.getMessage()); + } + } + } + if (nb > 0) { + // Instantiate load balancing policy with parameters + if (lbString.toLowerCase().contains("latencyawarepolicy")) { + //special sauce for the latency aware policy which uses a builder subclass to instantiate + LatencyAwarePolicy.Builder builder = LatencyAwarePolicy.builder((LoadBalancingPolicy) paramList.get(0)); + + builder.withExclusionThreshold((Double) paramList.get(1)); + builder.withScale((Long) paramList.get(2), TimeUnit.MILLISECONDS); + builder.withRetryPeriod((Long) paramList.get(3), TimeUnit.MILLISECONDS); + builder.withUpdateRate((Long) paramList.get(4), TimeUnit.MILLISECONDS); + builder.withMininumMeasurements((Integer) paramList.get(5)); + + return builder.build(); + + } else { + Class clazz = Class.forName(lbString); + Constructor constructor = clazz.getConstructor(primaryParametersClasses.toArray(new + Class[primaryParametersClasses.size()])); + + return (LoadBalancingPolicy) constructor.newInstance(paramList.toArray(new Object[paramList.size + ()])); + } + } else { + // Only one policy has been specified, with no parameter or child policy + Class clazz = Class.forName(lbString); + policy = (LoadBalancingPolicy) clazz.newInstance(); + return policy; + } + } else { + // Only one policy has been specified, with no parameter or child policy + Class clazz = Class.forName(lbString); + policy = (LoadBalancingPolicy) clazz.newInstance(); + return policy; + } + } + + /** + * Parse the RetryPolicy policy. + */ + public RetryPolicy parseRetryPolicy(String retryPolicyString) throws InstantiationException, + IllegalAccessException, ClassNotFoundException, NoSuchMethodException, SecurityException, + IllegalArgumentException, InvocationTargetException, NoSuchFieldException { + + if (!retryPolicyString.contains(".")) { + retryPolicyString = "com.datastax.driver.core.policies." + retryPolicyString; + Class clazz = Class.forName(retryPolicyString); + Field field = clazz.getDeclaredField("INSTANCE"); + RetryPolicy policy = (RetryPolicy) field.get(null); + return policy; + } + return null; + } + + /** + * Parse the reconnection policy. + */ + public ReconnectionPolicy parseReconnectionPolicy(String reconnectionPolicyString) throws + InstantiationException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, + SecurityException, IllegalArgumentException, InvocationTargetException { + String lb_regex = "([a-zA-Z]*Policy)(\\()(.*)(\\))"; + Pattern lb_pattern = Pattern.compile(lb_regex); + Matcher lb_matcher = lb_pattern.matcher(reconnectionPolicyString); + if (lb_matcher.matches()) { + if (lb_matcher.groupCount() > 0) { + // Primary LB policy has been specified + String primaryReconnectionPolicy = lb_matcher.group(1); + String reconnectionPolicyParams = lb_matcher.group(3); + return getReconnectionPolicy(primaryReconnectionPolicy, reconnectionPolicyParams); + } + } + return null; + } + + /** + * Get the reconnection policy. + */ + public ReconnectionPolicy getReconnectionPolicy(String rcString, String parameters) throws + ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, + IllegalAccessException, IllegalArgumentException, InvocationTargetException { + ReconnectionPolicy policy = null; + //ReconnectionPolicy childPolicy = null; + if (!rcString.contains(".")) { + rcString = "com.datastax.driver.core.policies." + rcString; + } + + if (parameters.length() > 0) { + // Child policy or parameters have been specified + String paramsRegex = "([^,]+\\(.+?\\))|([^,]+)"; + Pattern param_pattern = Pattern.compile(paramsRegex); + Matcher lb_matcher = param_pattern.matcher(parameters); + + ArrayList paramList = Lists.newArrayList(); + ArrayList primaryParametersClasses = Lists.newArrayList(); + int nb = 0; + while (lb_matcher.find()) { + if (lb_matcher.groupCount() > 0) { + try { + if (lb_matcher.group().contains("(") && !lb_matcher.group().trim().startsWith("(")) { + // We are dealing with child policies here + primaryParametersClasses.add(LoadBalancingPolicy.class); + // Parse and add child policy to the parameter list + paramList.add(parseReconnectionPolicy(lb_matcher.group())); + nb++; + } else { + // We are dealing with parameters that are not policies here + String param = lb_matcher.group(); + if (param.contains("'") || param.contains("\"")) { + primaryParametersClasses.add(String.class); + paramList.add(new String(param.trim().replace("'", "").replace("\"", ""))); + } else if (param.contains(".") || param.toLowerCase().contains("(double)") || param + .toLowerCase().contains("(float)")) { + // gotta allow using float or double + if (param.toLowerCase().contains("(double)")) { + primaryParametersClasses.add(double.class); + paramList.add(Double.parseDouble(param.replace("(double)", "").trim())); + } else { + primaryParametersClasses.add(float.class); + paramList.add(Float.parseFloat(param.replace("(float)", "").trim())); + } + } else { + if (param.toLowerCase().contains("(long)")) { + primaryParametersClasses.add(long.class); + paramList.add(Long.parseLong(param.toLowerCase().replace("(long)", "").trim())); + } else { + primaryParametersClasses.add(int.class); + paramList.add(Integer.parseInt(param.toLowerCase().replace("(int)", "").trim())); + } + } + nb++; + } + } catch (Exception e) { + log.error("Could not parse the Cassandra reconnection policy! " + e.getMessage()); + } + } + } + + if (nb > 0) { + // Instantiate load balancing policy with parameters + Class clazz = Class.forName(rcString); + Constructor constructor = clazz.getConstructor(primaryParametersClasses.toArray(new + Class[primaryParametersClasses.size()])); + + return (ReconnectionPolicy) constructor.newInstance(paramList.toArray(new Object[paramList.size()])); + } + // Only one policy has been specified, with no parameter or child policy + Class clazz = Class.forName(rcString); + policy = (ReconnectionPolicy) clazz.newInstance(); + return policy; + } + Class clazz = Class.forName(rcString); + policy = (ReconnectionPolicy) clazz.newInstance(); + return policy; } - private void addColumnFamily(ThriftCluster cluster, String cfName, int rowCacheKeysToSave) { + public com.datastax.driver.core.Cluster createCluster() { + com.datastax.driver.core.Cluster.Builder builder = com.datastax.driver.core.Cluster.builder() + .withClusterName(env.getProperty(CASSANDRA_CLUSTER_NAME)); + if (env.getProperty(CASSANDRA_PORT) != null) { + builder.withPort(Integer.parseInt(env.getProperty(CASSANDRA_PORT))); + } + + String protocolVersion = env.getProperty(CASSANDRA_PROTOCOL_VERSION); + if (V1.name().equals(protocolVersion)) { + builder.withProtocolVersion(V1); + } else if (V2.name().equals(protocolVersion)) { + builder.withProtocolVersion(V2); + } else if (V3.name().equals(protocolVersion)) { + builder.withProtocolVersion(V3); + } + + // Manage compression protocol + if (SNAPPY.name().equals(env.getProperty(CASSANDRA_COMPRESSION))) { + builder.withCompression(SNAPPY); + } else if (LZ4.name().equals(env.getProperty(CASSANDRA_COMPRESSION))) { + builder.withCompression(LZ4); + } else { + builder.withCompression(NONE); + } + + // Manage the load balancing policy + String loadBalancingPolicy = env.getProperty(CASSANDRA_LOAD_BALANCING_POLICY); + if (!StringUtils.isEmpty(loadBalancingPolicy)) { + try { + builder.withLoadBalancingPolicy(parseLbPolicy(loadBalancingPolicy)); + } catch (ClassNotFoundException e) { + log.warn("The load balancing policy could not be loaded, falling back to the default policy", e); + } catch (InstantiationException e) { + log.warn("The load balancing policy could not be instanced, falling back to the default policy", e); + } catch (IllegalAccessException e) { + log.warn("The load balancing policy could not be created, falling back to the default policy", e); + } catch (ClassCastException e) { + log.warn("The load balancing policy does not implement the correct interface, falling back to the " + + "default policy", e); + } catch (NoSuchMethodException e) { + log.warn("The load balancing policy could not be created, falling back to the default policy", e); + } catch (SecurityException e) { + log.warn("The load balancing policy could not be created, falling back to the default policy", e); + } catch (IllegalArgumentException e) { + log.warn("The load balancing policy could not be created, falling back to the default policy", e); + } catch (InvocationTargetException e) { + log.warn("The load balancing policy could not be created, falling back to the default policy", e); + } + } + + // Manage query options + QueryOptions queryOptions = new QueryOptions(); + if (env.getProperty(CASSANDRA_CONSISTENCY) != null) { + ConsistencyLevel consistencyLevel = ConsistencyLevel.valueOf(env.getProperty(CASSANDRA_CONSISTENCY)); + queryOptions.setConsistencyLevel(consistencyLevel); + } + if (env.getProperty(CASSANDRA_SERIAL_CONSISTENCY) != null) { + ConsistencyLevel serialConsistencyLevel = ConsistencyLevel.valueOf(env.getProperty(CASSANDRA_SERIAL_CONSISTENCY)); + queryOptions.setSerialConsistencyLevel(serialConsistencyLevel); + } + if (env.getProperty(CASSANDRA_FETCH_SIZE) != null) { + queryOptions.setFetchSize(Integer.parseInt(env.getProperty(CASSANDRA_FETCH_SIZE))); + } + builder.withQueryOptions(queryOptions); - String cassandraKeyspace = this.env.getProperty("cassandra.keyspace"); + // Manage the reconnection policy + if (!StringUtils.isEmpty(env.getProperty(CASSANDRA_RECONNECTION_POLICY))) { + try { + builder.withReconnectionPolicy(parseReconnectionPolicy(env.getProperty(CASSANDRA_RECONNECTION_POLICY))); + } catch (ClassNotFoundException e) { + log.warn("The reconnection policy could not be loaded, falling back to the default policy", e); + } catch (InstantiationException e) { + log.warn("The reconnection policy could not be instanced, falling back to the default policy", e); + } catch (IllegalAccessException e) { + log.warn("The reconnection policy could not be created, falling back to the default policy", e); + } catch (ClassCastException e) { + log.warn("The reconnection policy does not implement the correct interface, falling back to the " + + "default policy", e); + } catch (NoSuchMethodException e) { + log.warn("The reconnection policy could not be created, falling back to the default policy", e); + } catch (SecurityException e) { + log.warn("The reconnection policy could not be created, falling back to the default policy", e); + } catch (IllegalArgumentException e) { + log.warn("The reconnection policy could not be created, falling back to the default policy", e); + } catch (InvocationTargetException e) { + log.warn("The reconnection policy could not be created, falling back to the default policy", e); + } + } + + // Manage the retry policy + if (!StringUtils.isEmpty(env.getProperty(CASSANDRA_RETRY_POLICY))) { + try { + builder.withRetryPolicy(parseRetryPolicy(env.getProperty(CASSANDRA_RETRY_POLICY))); + } catch (ClassNotFoundException e) { + log.warn("The retry policy could not be loaded, falling back to the default policy", e); + } catch (InstantiationException e) { + log.warn("The retry policy could not be instanced, falling back to the default policy", e); + } catch (IllegalAccessException e) { + log.warn("The retry policy could not be created, falling back to the default policy", e); + } catch (ClassCastException e) { + log.warn("The retry policy does not implement the correct interface, falling back to the default " + + "policy", e); + } catch (NoSuchMethodException e) { + log.warn("The retry policy could not be created, falling back to the default policy", e); + } catch (SecurityException e) { + log.warn("The retry policy could not be created, falling back to the default policy", e); + } catch (IllegalArgumentException e) { + log.warn("The retry policy could not be created, falling back to the default policy", e); + } catch (InvocationTargetException e) { + log.warn("The retry policy could not be created, falling back to the default policy", e); + } catch (NoSuchFieldException e) { + log.warn("The retry policy could not be created, falling back to the default policy", e); + } + } + + if (!StringUtils.isEmpty(env.getProperty(CASSANDRA_USER)) && + !StringUtils.isEmpty(env.getProperty(CASSANDRA_PASSWORD))) { + builder.withCredentials(env.getProperty(CASSANDRA_USER), env.getProperty(CASSANDRA_PASSWORD)); + } - ColumnFamilyDefinition cfd = - HFactory.createColumnFamilyDefinition(cassandraKeyspace, cfName); + // Manage socket options - cfd.setRowCacheKeysToSave(rowCacheKeysToSave); - cluster.addColumnFamily(cfd); + SocketOptions socketOptions = new SocketOptions(); + if (env.getProperty(CASSANDRA_CONNECT_TIMEOUT_MILLIS) != null) { + socketOptions.setConnectTimeoutMillis(Integer.parseInt(env.getProperty(CASSANDRA_CONNECT_TIMEOUT_MILLIS))); + } + if (env.getProperty(CASSANDRA_READ_TIMEOUT_MILLIS) != null) { + socketOptions.setReadTimeoutMillis(Integer.parseInt(env.getProperty(CASSANDRA_READ_TIMEOUT_MILLIS))); + } + builder.withSocketOptions(socketOptions); + + // Manage SSL + if (Boolean.valueOf(env.getProperty(CASSANDRA_SSL_ENABLED))) { + builder.withSSL(); + } + + // Manage the contact points + builder.addContactPoints(StringUtils.commaDelimitedListToStringArray(env.getProperty(CASSANDRA_CONTACT_POINTS))); + com.datastax.driver.core.Cluster cluster = builder.build(); + + try { + validateKeyspace(cluster); + } catch (Exception e) { + log.error("Could not validate keyspace",e); + } + + return cluster; } - private void addColumnFamilySortedbyUUID(ThriftCluster cluster, String cfName, int rowCacheKeysToSave) { + private void validateKeyspace(Cluster cluster) throws Exception { + Session session = cluster.newSession(); + String keyspace = env.getProperty("cassandra.keyspace"); + ResultSet results = session.execute("SELECT * FROM system.schema_keyspaces " + + "WHERE keyspace_name = '"+keyspace+"';"); + if (results.isExhausted()) { + session.execute("CREATE KEYSPACE IF NOT EXISTS "+keyspace+" WITH replication " + + "= {'class':'SimpleStrategy', 'replication_factor':3};"); + createTables(session, keyspace); + session.close(); + } - String cassandraKeyspace = this.env.getProperty("cassandra.keyspace"); + } - ColumnFamilyDefinition cfd = - HFactory.createColumnFamilyDefinition(cassandraKeyspace, cfName); + private void createTables(Session session, String keyspace) { + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".user (\n" + + " login varchar,\n" + + " password varchar,\n" + + " username varchar,\n" + + " firstname varchar,\n" + + " lastname varchar,\n" + + " domain varchar,\n" + + " activated boolean,\n" + + " avatar varchar,\n" + + " jobTitle varchar,\n" + + " activation_key varchar,\n" + + " reset_key varchar,\n" + + " phoneNumber varchar,\n" + + " openIdUrl varchar,\n" + + " preferences_mention_email boolean,\n" + + " rssUid varchar,\n" + + " weekly_digest_subscription boolean,\n" + + " daily_digest_subscription boolean,\n" + + " attachmentsSize bigint,\n" + + " PRIMARY KEY(login)\n" + + ");"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".status (\n" + + " statusId timeuuid,\n" + + " type varchar,\n" + + " login varchar,\n" + + " username varchar,\n" + + " domain varchar,\n" + + " statusDate timestamp,\n" + + " geoLocalization varchar,\n" + + " removed boolean,\n" + + " groupId varchar,\n" + + " statusPrivate boolean,\n" + + " hasAttachments boolean,\n" + + " content varchar,\n" + + " discussionId varchar,\n" + + " replyTo varchar,\n" + + " replyToUsername varchar,\n" + + " detailsAvailable boolean,\n" + + " originalStatusId timeuuid,\n" + + " followerLogin varchar,\n" + + " PRIMARY KEY(statusId)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".timeline (\n" + + " key varchar,\n" + + " status timeuuid,\n" + + " PRIMARY KEY(key,status)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".domain (\n" + + " domainId varchar,\n" + + " login varchar,\n" + + " created timeuuid,\n" + + " PRIMARY KEY(domainId, login)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".counter (\n" + + " login varchar,\n" + + " STATUS_COUNTER counter,\n" + + " FOLLOWERS_COUNTER counter,\n" + + " FRIENDS_COUNTER counter,\n" + + " PRIMARY KEY(login)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".friends (\n" + + " login varchar,\n" + + " friendLogin varchar,\n" + + " PRIMARY KEY(login,friendLogin)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".followers (\n" + + " key varchar,\n" + + " login varchar,\n" + + " PRIMARY KEY(key,login)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".dayline (\n" + + " domainDay varchar,\n" + + " username varchar,\n" + + " statusCount counter,\n" + + " PRIMARY KEY(domainDay, username)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".tagline (\n" + + " key varchar,\n" + + " status timeuuid,\n" + + " PRIMARY KEY(key, status)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".userline (\n" + + " key varchar,\n" + + " status timeuuid,\n" + + " PRIMARY KEY(key, status)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".shares (\n" + + " status timeuuid,\n" + + " login varchar,\n" + + " PRIMARY KEY(status, login)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".tagFollowers (\n" + + " key varchar,\n" + + " login varchar,\n" + + " PRIMARY KEY(key, login)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".favline (\n" + + " key varchar,\n" + + " status timeuuid,\n" + + " PRIMARY KEY(key, status)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".domainline (\n" + + " key varchar,\n" + + " status timeuuid,\n" + + " PRIMARY KEY(key, status)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".mentionline (\n" + + " key varchar,\n" + + " status timeuuid,\n" + + " PRIMARY KEY(key, status)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".UserlineShares (\n" + + " key varchar,\n" + + " status timeuuid,\n" + + " PRIMARY KEY(key, status)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".groupline (\n" + + " key varchar,\n" + + " status timeuuid,\n" + + " PRIMARY KEY(key, status)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".group (\n" + + " id timeuuid,\n" + + " domain varchar,\n" + + " name varchar,\n" + + " description varchar,\n" + + " publicGroup boolean,\n" + + " archivedGroup boolean,\n" + + " PRIMARY KEY(id, domain)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".userGroup (\n" + + " login varchar,\n" + + " groupId timeuuid,\n" + + " role varchar,\n" + + " PRIMARY KEY(login, groupId)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".groupMember (\n" + + " groupId timeuuid,\n" + + " login varchar,\n" + + " role varchar,\n" + + " PRIMARY KEY(groupId, login)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".userTags (\n" + + " login varchar,\n" + + " friendLogin varchar,\n" + + " time timestamp,\n" + + " PRIMARY KEY(login, friendLogin)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".trends (\n" + + " domain varchar,\n" + + " id timeuuid,\n" + + " tag varchar,\n" + + " PRIMARY KEY(domain, id)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".userTrends (\n" + + " login varchar,\n" + + " id timeuuid,\n" + + " tag varchar,\n" + + " PRIMARY KEY(login, id)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".avatar (\n" + + " id timeuuid,\n" + + " filename varchar,\n" + + " content blob,\n" + + " size bigint,\n" + + " creation_date timestamp,\n" + + " PRIMARY KEY(id)\n" + + ");\n"); + session.execute("CREATE INDEX IF NOT EXISTS ON "+keyspace+".avatar (filename);"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".mailDigest (\n" + + " digestId varchar,\n" + + " login varchar,\n" + + " created timestamp,\n" + + " PRIMARY KEY(digestId,login)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".timelineShares (\n" + + " key varchar,\n" + + " status timeuuid,\n" + + " PRIMARY KEY(key,status)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".appleDevice (\n" + + " login varchar,\n" + + " deviceId varchar,\n" + + " PRIMARY KEY(login,deviceId)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".appleDeviceUser (\n" + + " deviceId varchar,\n" + + " login varchar,\n" + + " PRIMARY KEY(deviceId,login)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".attachment (\n" + + " id timeuuid,\n" + + " filename varchar,\n" + + " content blob,\n" + + " thumbnail blob,\n" + + " size bigint,\n" + + " creation_date timestamp,\n" + + " PRIMARY KEY(id,filename)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".groupCounter (\n" + + " domain varchar,\n" + + " groupId timeuuid,\n" + + " counter counter,\n" + + " PRIMARY KEY(domain,groupId)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".TatamiBotDuplicate (\n" + + " Default varchar,\n" + + " PRIMARY KEY(Default)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".Registration (\n" + + " registration_key varchar,\n" + + " login varchar,\n" + + " PRIMARY KEY(registration_key,login)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".rss (\n" + + " rss_uid varchar,\n" + + " login varchar,\n" + + " PRIMARY KEY(rss_uid)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".statusAttachments (\n" + + " statusId timeuuid,\n" + + " attachmentId timeuuid,\n" + + " created timestamp,\n" + + " PRIMARY KEY(statusId,attachmentId)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".tagCounter (\n" + + " key varchar,\n" + + " TAG_COUNTER counter,\n" + + " PRIMARY KEY(key)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".userAttachments (\n" + + " login varchar,\n" + + " attachmentId timeuuid,\n" + + " PRIMARY KEY(login,attachmentId)\n" + + ");\n"); + session.execute("CREATE TABLE IF NOT EXISTS "+keyspace+".domainConfiguration (\n" + + " domain varchar,\n" + + " subscriptionLevel varchar,\n" + + " storageSize varchar,\n" + + " adminLogin varchar,\n" + + " PRIMARY KEY(domain)\n" + + ");\n"); + } - cfd.setRowCacheKeysToSave(rowCacheKeysToSave); - cfd.setComparatorType(ComparatorType.UUIDTYPE); - cluster.addColumnFamily(cfd); + @Bean + public CassandraMappingContext cassandraMapping() throws ClassNotFoundException { + BasicCassandraMappingContext bean = new BasicCassandraMappingContext(); + bean.setInitialEntitySet(CassandraEntityClassScanner.scan("fr.ippon.tatami")); + bean.setBeanClassLoader(beanFactory.getClass().getClassLoader()); + return bean; } - private void addColumnFamilyCounter(ThriftCluster cluster, String cfName, int rowCacheKeysToSave) { - String cassandraKeyspace = this.env.getProperty("cassandra.keyspace"); + @Bean + public CassandraConverter cassandraConverter() throws Exception { + return new MappingCassandraConverter(cassandraMapping()); + } - ThriftCfDef cfd = - new ThriftCfDef(cassandraKeyspace, cfName, ComparatorType.UTF8TYPE); + @Bean + public CassandraSessionFactoryBean session() throws Exception { + CassandraSessionFactoryBean session = new CassandraSessionFactoryBean(); + session.setCluster(createCluster()); + session.setConverter(cassandraConverter()); + session.setKeyspaceName(env.getProperty("cassandra.keyspace")); + return session; - cfd.setRowCacheKeysToSave(rowCacheKeysToSave); - cfd.setDefaultValidationClass(ComparatorType.COUNTERTYPE.getClassName()); - cluster.addColumnFamily(cfd); } + + } diff --git a/services/src/main/java/fr/ippon/tatami/config/ColumnFamilyKeys.java b/services/src/main/java/fr/ippon/tatami/config/ColumnFamilyKeys.java index 55b9e1f5e..f4b0ce7d7 100644 --- a/services/src/main/java/fr/ippon/tatami/config/ColumnFamilyKeys.java +++ b/services/src/main/java/fr/ippon/tatami/config/ColumnFamilyKeys.java @@ -11,7 +11,7 @@ private ColumnFamilyKeys() { public static final String USER_CF = "User"; - public static final String FRIENDS_CF = "Friends"; + public static final String FRIENDS = "friends"; public static final String FOLLOWERS_CF = "Followers"; @@ -25,13 +25,13 @@ private ColumnFamilyKeys() { public static final String FAVLINE_CF = "Favline"; - public static final String TAGLINE_CF = "Tagline"; + public static final String TAGLINE = "Tagline"; public static final String TIMELINE_CF = "Timeline"; public static final String TIMELINE_SHARES_CF = "TimelineShares"; - public static final String MENTIONLINE_CF = "Mentionline"; + public static final String MENTIONLINE = "mentionLine"; public static final String USERLINE_CF = "Userline"; @@ -67,7 +67,7 @@ private ColumnFamilyKeys() { public static final String GROUP_COUNTER_CF = "GroupCounter"; - public static final String GROUPLINE_CF = "Groupline"; + public static final String GROUPLINE = "Groupline"; public static final String TATAMIBOT_DUPLICATE_CF = "TatamiBotDuplicate"; @@ -79,7 +79,7 @@ private ColumnFamilyKeys() { public static final String DOMAIN_CONFIGURATION_CF = "DomainConfiguration"; - public static final String DOMAINLINE_CF = "Domainline"; + public static final String DOMAINLINE = "domainLine"; public static final String DOMAIN_TATAMIBOT_CF = "DomainTatamibot"; diff --git a/services/src/main/java/fr/ippon/tatami/config/DispatcherServletConfig.java b/services/src/main/java/fr/ippon/tatami/config/DispatcherServletConfig.java index 4bc757ed3..99958792b 100644 --- a/services/src/main/java/fr/ippon/tatami/config/DispatcherServletConfig.java +++ b/services/src/main/java/fr/ippon/tatami/config/DispatcherServletConfig.java @@ -24,6 +24,7 @@ import org.springframework.web.servlet.view.JstlView; import org.springframework.web.servlet.view.UrlBasedViewResolver; import org.springframework.web.servlet.view.json.MappingJackson2JsonView; +import springfox.documentation.swagger2.annotations.EnableSwagger2; import javax.inject.Inject; import javax.servlet.http.HttpServletRequest; @@ -34,6 +35,7 @@ @Configuration @ComponentScan("fr.ippon.tatami.web") @EnableWebMvc +@EnableSwagger2 @PropertySource({"classpath:/META-INF/tatami/tatami.properties", "classpath:/META-INF/tatami/customization.properties"}) @ImportResource("classpath:META-INF/spring/applicationContext-metrics.xml") diff --git a/services/src/main/java/fr/ippon/tatami/config/MetricsConfiguration.java b/services/src/main/java/fr/ippon/tatami/config/MetricsConfiguration.java index 7afd96861..e26392e50 100644 --- a/services/src/main/java/fr/ippon/tatami/config/MetricsConfiguration.java +++ b/services/src/main/java/fr/ippon/tatami/config/MetricsConfiguration.java @@ -5,7 +5,6 @@ import fr.ippon.tatami.config.metrics.CassandraHealthCheck; import fr.ippon.tatami.config.metrics.JavaMailHealthCheck; import fr.ippon.tatami.service.MailService; -import me.prettyprint.hector.api.Keyspace; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Configuration; @@ -23,8 +22,8 @@ public class MetricsConfiguration { @Inject private Environment env; - @Inject - private Keyspace keyspaceOperator; +// @Inject +// private Keyspace keyspaceOperator; @Inject private MailService mailService; @@ -33,7 +32,7 @@ public class MetricsConfiguration { public void initMetrics() { if (env.acceptsProfiles(Constants.SPRING_PROFILE_METRICS)) { log.debug("Initializing Metrics healthchecks"); - HealthChecks.register(new CassandraHealthCheck(keyspaceOperator)); +// HealthChecks.register(new CassandraHealthCheck(keyspaceOperator)); HealthChecks.register(new JavaMailHealthCheck(mailService)); String graphiteHost = env.getProperty("tatami.metrics.graphite.host"); diff --git a/services/src/main/java/fr/ippon/tatami/config/metrics/CassandraHealthCheck.java b/services/src/main/java/fr/ippon/tatami/config/metrics/CassandraHealthCheck.java index ec046041e..90f570e05 100644 --- a/services/src/main/java/fr/ippon/tatami/config/metrics/CassandraHealthCheck.java +++ b/services/src/main/java/fr/ippon/tatami/config/metrics/CassandraHealthCheck.java @@ -1,37 +1,46 @@ package fr.ippon.tatami.config.metrics; import com.yammer.metrics.core.HealthCheck; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.exceptions.HectorException; import static fr.ippon.tatami.config.ColumnFamilyKeys.DOMAIN_CF; -import static me.prettyprint.hector.api.factory.HFactory.createRangeSlicesQuery; /** * Metrics HealthCheck for Cassandra. */ public class CassandraHealthCheck extends HealthCheck { - - private final Keyspace keyspaceOperator; - - public CassandraHealthCheck(Keyspace keyspaceOperator) { - super("Cassandra"); - this.keyspaceOperator = keyspaceOperator; + /** + * Create a new {@link HealthCheck} instance with the given name. + * + * @param name the name of the health check (and, ideally, the name of the underlying + * component the health check tests) + */ + protected CassandraHealthCheck(String name) { + super(name); } @Override - public Result check() throws Exception { - try { - createRangeSlicesQuery(keyspaceOperator, - StringSerializer.get(), StringSerializer.get(), StringSerializer.get()) - .setColumnFamily(DOMAIN_CF) - .setRange(null, null, false, 1) - .execute() - .get(); - return Result.healthy(); - } catch (HectorException he) { - return Result.unhealthy("Cannot connect to Cassandra Cluster : " + keyspaceOperator.getKeyspaceName()); - } + protected Result check() throws Exception { + return null; } + + +// public CassandraHealthCheck(Keyspace keyspaceOperator) { +// super("Cassandra"); +// this.keyspaceOperator = keyspaceOperator; +// } +// +// @Override +// public Result check() throws Exception { +// try { +// createRangeSlicesQuery(keyspaceOperator, +// StringSerializer.get(), StringSerializer.get(), StringSerializer.get()) +// .setColumnFamily(DOMAIN_CF) +// .setRange(null, null, false, 1) +// .execute() +// .get(); +// return Result.healthy(); +// } catch (HectorException he) { +// return Result.unhealthy("Cannot connect to Cassandra Cluster : " + keyspaceOperator.getKeyspaceName()); +// } +// } } diff --git a/services/src/main/java/fr/ippon/tatami/domain/Avatar.java b/services/src/main/java/fr/ippon/tatami/domain/Avatar.java index 84fb30372..520c656c4 100644 --- a/services/src/main/java/fr/ippon/tatami/domain/Avatar.java +++ b/services/src/main/java/fr/ippon/tatami/domain/Avatar.java @@ -19,6 +19,9 @@ public class Avatar implements Serializable { private long size; + public Avatar() { + } + public String getAvatarId() { return avatarId; } diff --git a/services/src/main/java/fr/ippon/tatami/domain/DomainConfiguration.java b/services/src/main/java/fr/ippon/tatami/domain/DomainConfiguration.java index 880f3a5bf..1a06dd848 100644 --- a/services/src/main/java/fr/ippon/tatami/domain/DomainConfiguration.java +++ b/services/src/main/java/fr/ippon/tatami/domain/DomainConfiguration.java @@ -5,10 +5,7 @@ import java.io.Serializable; import java.util.Properties; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Table; +import com.datastax.driver.mapping.annotations.*; import org.apache.commons.io.IOUtils; @@ -17,7 +14,6 @@ * * @author Julien Dubois */ -@Entity @Table(name = "DomainConfiguration") public class DomainConfiguration implements Serializable { @@ -74,7 +70,7 @@ public static class SubscriptionAndStorageSizeOptions { } - @Id + @PartitionKey private String domain; @Column(name = "subscriptionLevel") diff --git a/services/src/main/java/fr/ippon/tatami/domain/Group.java b/services/src/main/java/fr/ippon/tatami/domain/Group.java index f02a58946..992c74133 100644 --- a/services/src/main/java/fr/ippon/tatami/domain/Group.java +++ b/services/src/main/java/fr/ippon/tatami/domain/Group.java @@ -4,13 +4,14 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import java.io.Serializable; +import java.util.UUID; /** * A group. */ public class Group implements Comparable, Serializable, Cloneable { - private String groupId; + private UUID groupId; private boolean publicGroup; @@ -29,11 +30,11 @@ public class Group implements Comparable, Serializable, Cloneable { private boolean administrator; - public String getGroupId() { + public UUID getGroupId() { return groupId; } - public void setGroupId(String groupId) { + public void setGroupId(UUID groupId) { this.groupId = groupId; } diff --git a/services/src/main/java/fr/ippon/tatami/domain/User.java b/services/src/main/java/fr/ippon/tatami/domain/User.java index 8bd2beef0..5d30b6658 100644 --- a/services/src/main/java/fr/ippon/tatami/domain/User.java +++ b/services/src/main/java/fr/ippon/tatami/domain/User.java @@ -1,14 +1,12 @@ package fr.ippon.tatami.domain; +import com.datastax.driver.mapping.annotations.*; import com.fasterxml.jackson.annotation.JsonIgnore; import fr.ippon.tatami.domain.validation.ContraintsUserCreation; import org.hibernate.validator.constraints.Email; import org.hibernate.validator.constraints.NotEmpty; +import org.springframework.data.cassandra.mapping.PrimaryKey; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Table; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import javax.validation.groups.Default; @@ -19,15 +17,15 @@ * * @author Julien Dubois */ -@Entity -@Table(name = "User") +@Table(name = "user") public class User implements Serializable { + @NotEmpty(message = "Login is mandatory.", groups = {ContraintsUserCreation.class, Default.class}) @NotNull(message = "Login is mandatory.", groups = {ContraintsUserCreation.class, Default.class}) @Email(message = "Email is invalid.") - @Id @JsonIgnore + @PartitionKey private String login; @Column(name = "password") @@ -86,10 +84,13 @@ public class User implements Serializable { @Column(name="activated") private Boolean activated=true; + @Transient private long statusCount; + @Transient private long friendsCount; + @Transient private long followersCount; diff --git a/services/src/main/java/fr/ippon/tatami/domain/line/TimeLine.java b/services/src/main/java/fr/ippon/tatami/domain/line/TimeLine.java new file mode 100644 index 000000000..6a405bc2b --- /dev/null +++ b/services/src/main/java/fr/ippon/tatami/domain/line/TimeLine.java @@ -0,0 +1,35 @@ +package fr.ippon.tatami.domain.line; + +import com.datastax.driver.mapping.annotations.Column; +import com.datastax.driver.mapping.annotations.PartitionKey; +import com.datastax.driver.mapping.annotations.Table; + +import java.util.UUID; + +/** + * Created by lnorregaard on 25/11/15. + */ +@Table(name="timeline") +public class TimeLine { + @Column + private String login; + + @Column + private UUID statusId; + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + public UUID getStatusId() { + return statusId; + } + + public void setStatusId(UUID statusId) { + this.statusId = statusId; + } +} diff --git a/services/src/main/java/fr/ippon/tatami/domain/status/AbstractStatus.java b/services/src/main/java/fr/ippon/tatami/domain/status/AbstractStatus.java index f312029a3..10a5babac 100644 --- a/services/src/main/java/fr/ippon/tatami/domain/status/AbstractStatus.java +++ b/services/src/main/java/fr/ippon/tatami/domain/status/AbstractStatus.java @@ -1,126 +1,37 @@ package fr.ippon.tatami.domain.status; -import javax.validation.constraints.NotNull; import java.io.Serializable; import java.util.Date; +import java.util.UUID; /** * Parent class for all statuses. */ -public abstract class AbstractStatus implements Serializable { +public interface AbstractStatus extends Serializable { - private String statusId; + UUID getStatusId(); - @NotNull - private StatusType type; + StatusType getType(); - @NotNull - private String login; + String getLogin(); - @NotNull - private String username; + Date getStatusDate(); - @NotNull - private String domain; + String getGeoLocalization(); - private Date statusDate; + void setStatusId(UUID uuid); - public String getGeoLocalization() { - return geoLocalization; - } + void setStatusDate(Date date); - public void setGeoLocalization(String geoLocalization) { - this.geoLocalization = geoLocalization; - } + String getUsername(); - private String geoLocalization; + String getDomain(); - private boolean removed; + void setLogin(String string); - public String getStatusId() { - return statusId; - } + void setUsername(String string); - public void setStatusId(String statusId) { - this.statusId = statusId; - } + void setDomain(String domain); - public StatusType getType() { - return type; - } - - public void setType(StatusType type) { - this.type = type; - } - - public String getLogin() { - return login; - } - - public void setLogin(String login) { - this.login = login; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getDomain() { - return domain; - } - - public void setDomain(String domain) { - this.domain = domain; - } - - public Date getStatusDate() { - return statusDate; - } - - public void setStatusDate(Date statusDate) { - this.statusDate = statusDate; - } - - public boolean isRemoved() { - return removed; - } - - public void setRemoved(boolean removed) { - this.removed = removed; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - AbstractStatus that = (AbstractStatus) o; - - if (statusId != null ? !statusId.equals(that.statusId) : that.statusId != null) return false; - - return true; - } - - @Override - public int hashCode() { - return statusId != null ? statusId.hashCode() : 0; - } - - @Override - public String toString() { - return "AbstractStatus{" + - "statusId='" + statusId + '\'' + - ", type=" + type + - ", login='" + login + '\'' + - ", username='" + username + '\'' + - ", domain='" + domain + '\'' + - ", statusDate=" + statusDate + - ", geoLocalization='" + geoLocalization + '\'' + - ", removed=" + removed + - '}'; - } + void setRemoved(boolean removed); } diff --git a/services/src/main/java/fr/ippon/tatami/domain/status/MentionFriend.java b/services/src/main/java/fr/ippon/tatami/domain/status/MentionFriend.java index 38f9ff80a..7fb74183e 100644 --- a/services/src/main/java/fr/ippon/tatami/domain/status/MentionFriend.java +++ b/services/src/main/java/fr/ippon/tatami/domain/status/MentionFriend.java @@ -1,9 +1,107 @@ package fr.ippon.tatami.domain.status; +import com.datastax.driver.mapping.annotations.Column; +import com.datastax.driver.mapping.annotations.PartitionKey; + +import javax.validation.constraints.NotNull; +import java.util.Date; +import java.util.UUID; + /** * Mention a user that someone started following him. */ -public class MentionFriend extends AbstractStatus { +public class MentionFriend implements AbstractStatus { + @PartitionKey + private UUID statusId; + + @NotNull + @Column + private StatusType type; + + @NotNull + @Column + private String login; + + @NotNull + @Column + private String username; + + @NotNull + @Column + private String domain; + + @Column + private Date statusDate; + + public String getGeoLocalization() { + return geoLocalization; + } + + public void setGeoLocalization(String geoLocalization) { + this.geoLocalization = geoLocalization; + } + + @Column + private String geoLocalization; + + @Column + private boolean removed; + + public UUID getStatusId() { + return statusId; + } + + public void setStatusId(UUID statusId) { + this.statusId = statusId; + } + + public StatusType getType() { + return type; + } + + public void setType(StatusType type) { + this.type = type; + } + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getDomain() { + return domain; + } + + public void setDomain(String domain) { + this.domain = domain; + } + + public Date getStatusDate() { + return statusDate; + } + + public void setStatusDate(Date statusDate) { + this.statusDate = statusDate; + } + + public boolean isRemoved() { + return removed; + } + + public void setRemoved(boolean removed) { + this.removed = removed; + } private String followerLogin; diff --git a/services/src/main/java/fr/ippon/tatami/domain/status/Share.java b/services/src/main/java/fr/ippon/tatami/domain/status/Share.java index 52d5cacea..6c775e4e2 100644 --- a/services/src/main/java/fr/ippon/tatami/domain/status/Share.java +++ b/services/src/main/java/fr/ippon/tatami/domain/status/Share.java @@ -1,9 +1,108 @@ package fr.ippon.tatami.domain.status; +import com.datastax.driver.mapping.annotations.Column; +import com.datastax.driver.mapping.annotations.PartitionKey; + +import javax.validation.constraints.NotNull; +import java.util.Date; +import java.util.UUID; + /** * A status that is shared. */ -public class Share extends AbstractStatus { +public class Share implements AbstractStatus { + + @PartitionKey + private UUID statusId; + + @NotNull + @Column + private StatusType type; + + @NotNull + @Column + private String login; + + @NotNull + @Column + private String username; + + @NotNull + @Column + private String domain; + + @Column + private Date statusDate; + + public String getGeoLocalization() { + return geoLocalization; + } + + public void setGeoLocalization(String geoLocalization) { + this.geoLocalization = geoLocalization; + } + + @Column + private String geoLocalization; + + @Column + private boolean removed; + + public UUID getStatusId() { + return statusId; + } + + public void setStatusId(UUID statusId) { + this.statusId = statusId; + } + + public StatusType getType() { + return type; + } + + public void setType(StatusType type) { + this.type = type; + } + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getDomain() { + return domain; + } + + public void setDomain(String domain) { + this.domain = domain; + } + + public Date getStatusDate() { + return statusDate; + } + + public void setStatusDate(Date statusDate) { + this.statusDate = statusDate; + } + + public boolean isRemoved() { + return removed; + } + + public void setRemoved(boolean removed) { + this.removed = removed; + } private String originalStatusId; diff --git a/services/src/main/java/fr/ippon/tatami/domain/status/Status.java b/services/src/main/java/fr/ippon/tatami/domain/status/Status.java index 6246b30c3..5267a256a 100644 --- a/services/src/main/java/fr/ippon/tatami/domain/status/Status.java +++ b/services/src/main/java/fr/ippon/tatami/domain/status/Status.java @@ -1,50 +1,157 @@ package fr.ippon.tatami.domain.status; +import com.datastax.driver.mapping.annotations.Column; +import com.datastax.driver.mapping.annotations.PartitionKey; +import com.datastax.driver.mapping.annotations.Table; +import com.datastax.driver.mapping.annotations.Transient; import fr.ippon.tatami.domain.Attachment; import org.hibernate.validator.constraints.NotEmpty; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import java.util.Collection; +import java.util.Date; +import java.util.UUID; /** * A status. * * @author Julien Dubois */ -public class Status extends AbstractStatus { +@Table(name="status") +public class Status implements AbstractStatus { + @PartitionKey + private UUID statusId; + @NotNull + @Column + private StatusType type; + + @NotNull + @Column + private String login; + + @NotNull + @Column + private String username; + + @NotNull + @Column + private String domain; + + @Column + private Date statusDate; + + public String getGeoLocalization() { + return geoLocalization; + } + + public void setGeoLocalization(String geoLocalization) { + this.geoLocalization = geoLocalization; + } + + @Column + private String geoLocalization; + + @Column + private boolean removed; + + public UUID getStatusId() { + return statusId; + } + + public void setStatusId(UUID statusId) { + this.statusId = statusId; + } + + public StatusType getType() { + return type; + } + + public void setType(StatusType type) { + this.type = type; + } + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getDomain() { + return domain; + } + + public void setDomain(String domain) { + this.domain = domain; + } + + public Date getStatusDate() { + return statusDate; + } + + public void setStatusDate(Date statusDate) { + this.statusDate = statusDate; + } + + public boolean isRemoved() { + return removed; + } + + public void setRemoved(boolean removed) { + this.removed = removed; + } + + + @Column private String groupId; + @Column private Boolean statusPrivate; + @Column private Boolean hasAttachments; + @Transient private Collection attachments; @NotNull @NotEmpty(message = "Content field is mandatory.") @Size(min = 1, max = 2048) + @Column private String content; /** * If this status is a reply, the statusId of the original status. */ + @Column private String discussionId; /** * If this status is a reply, the statusId of the status that is being replied to. */ + @Column private String replyTo; /** * If this status is a reply, the username of the status that is being replied to. */ + @Column private String replyToUsername; + @Transient private boolean detailsAvailable; - private Boolean removed; public String getGroupId() { return groupId; @@ -118,14 +225,6 @@ public void setDetailsAvailable(boolean detailsAvailable) { this.detailsAvailable = detailsAvailable; } - public Boolean getRemoved() { - return removed; - } - - public void setRemoved(Boolean removed) { - this.removed = removed; - } - @Override public String toString() { return "Status{" + diff --git a/services/src/main/java/fr/ippon/tatami/repository/AvatarRepository.java b/services/src/main/java/fr/ippon/tatami/repository/AvatarRepository.java index 4f70fd81e..21180df51 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/AvatarRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/AvatarRepository.java @@ -10,4 +10,5 @@ public interface AvatarRepository { Avatar findAvatarById(String avatarId); + Avatar findAvatarByFilename(String filename); } diff --git a/services/src/main/java/fr/ippon/tatami/repository/GroupCounterRepository.java b/services/src/main/java/fr/ippon/tatami/repository/GroupCounterRepository.java index c97bdf905..b37ff705b 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/GroupCounterRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/GroupCounterRepository.java @@ -1,5 +1,7 @@ package fr.ippon.tatami.repository; +import java.util.UUID; + /** * The Group Counter Repository. * @@ -7,11 +9,11 @@ */ public interface GroupCounterRepository { - long getGroupCounter(String domain, String groupId); + long getGroupCounter(String domain, UUID groupId); - void incrementGroupCounter(String domain, String groupId); + void incrementGroupCounter(String domain, UUID groupId); - void decrementGroupCounter(String domain, String groupId); + void decrementGroupCounter(String domain, UUID groupId); void deleteGroupCounter(String domain, String groupId); } diff --git a/services/src/main/java/fr/ippon/tatami/repository/GroupDetailsRepository.java b/services/src/main/java/fr/ippon/tatami/repository/GroupDetailsRepository.java deleted file mode 100644 index 7ec0edacb..000000000 --- a/services/src/main/java/fr/ippon/tatami/repository/GroupDetailsRepository.java +++ /dev/null @@ -1,17 +0,0 @@ -package fr.ippon.tatami.repository; - -import fr.ippon.tatami.domain.Group; - -/** - * The Group Details Repository. - * - * @author Julien Dubois - */ -public interface GroupDetailsRepository { - - void createGroupDetails(String groupId, String name, String description, boolean publicGroup); - - Group getGroupDetails(String groupId); - - void editGroupDetails(String groupId, String name, String description, boolean archivedGroup); -} diff --git a/services/src/main/java/fr/ippon/tatami/repository/GroupMembersRepository.java b/services/src/main/java/fr/ippon/tatami/repository/GroupMembersRepository.java index 0e90578a9..079320b9e 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/GroupMembersRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/GroupMembersRepository.java @@ -1,6 +1,7 @@ package fr.ippon.tatami.repository; import java.util.Map; +import java.util.UUID; /** * The Group members Repository. @@ -9,11 +10,11 @@ */ public interface GroupMembersRepository { - void addMember(String groupId, String login); + void addMember(UUID groupId, String login); - void addAdmin(String groupId, String login); + void addAdmin(UUID groupId, String login); - void removeMember(String groupId, String login); + void removeMember(UUID groupId, String login); - Map findMembers(String groupId); + Map findMembers(UUID groupId); } diff --git a/services/src/main/java/fr/ippon/tatami/repository/GroupRepository.java b/services/src/main/java/fr/ippon/tatami/repository/GroupRepository.java index 0e5f917c9..b7a4ab346 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/GroupRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/GroupRepository.java @@ -2,6 +2,8 @@ import fr.ippon.tatami.domain.Group; +import java.util.UUID; + /** * The Group Repository. * @@ -9,7 +11,11 @@ */ public interface GroupRepository { - String createGroup(String domain); + UUID createGroup(String domain, String name, String description, boolean publicGroup); + + Group getGroupById(String domain, UUID groupId); + + Group getGroupByGroupId(UUID groupId); - Group getGroupById(String domain, String groupId); + void editGroupDetails(UUID groupId, String name, String description, boolean archivedGroup); } diff --git a/services/src/main/java/fr/ippon/tatami/repository/GrouplineRepository.java b/services/src/main/java/fr/ippon/tatami/repository/GrouplineRepository.java index 648f47e10..0030b63f7 100755 --- a/services/src/main/java/fr/ippon/tatami/repository/GrouplineRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/GrouplineRepository.java @@ -2,6 +2,7 @@ import java.util.Collection; import java.util.List; +import java.util.UUID; /** * The Groupline Repository. @@ -13,7 +14,7 @@ public interface GrouplineRepository { /** * Add a status to the Group line. */ - void addStatusToGroupline(String groupId, String statusId); + void addStatusToGroupline(UUID groupId, String statusId); /** * Remove a collection of statuses from the Group line. diff --git a/services/src/main/java/fr/ippon/tatami/repository/UserGroupRepository.java b/services/src/main/java/fr/ippon/tatami/repository/UserGroupRepository.java index 3eadc8818..d98fa2219 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/UserGroupRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/UserGroupRepository.java @@ -2,6 +2,7 @@ import java.util.Collection; import java.util.List; +import java.util.UUID; /** * The User group Repository. @@ -10,13 +11,13 @@ */ public interface UserGroupRepository { - void addGroupAsMember(String login, String groupId); + void addGroupAsMember(String login, UUID groupId); - void addGroupAsAdmin(String login, String groupId); + void addGroupAsAdmin(String login, UUID groupId); - void removeGroup(String login, String groupId); + void removeGroup(String login, UUID groupId); - List findGroups(String login); + List findGroups(String login); - Collection findGroupsAsAdmin(String login); + Collection findGroupsAsAdmin(String login); } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/AbstractCassandraFollowerRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/AbstractCassandraFollowerRepository.java index 253140f74..ce3254da5 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/AbstractCassandraFollowerRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/AbstractCassandraFollowerRepository.java @@ -1,14 +1,9 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.querybuilder.QueryBuilder; import fr.ippon.tatami.config.Constants; -import me.prettyprint.cassandra.serializers.LongSerializer; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.cassandra.service.template.ColumnFamilyResult; -import me.prettyprint.cassandra.service.template.ColumnFamilyTemplate; -import me.prettyprint.cassandra.service.template.ThriftColumnFamilyTemplate; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; import javax.annotation.PostConstruct; import javax.inject.Inject; @@ -16,45 +11,32 @@ import java.util.Calendar; import java.util.Collection; +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; + /** * Abstract class for managing followers : users who follow another user or a tag. */ public abstract class AbstractCassandraFollowerRepository { - private ColumnFamilyTemplate template; - @Inject - private Keyspace keyspaceOperator; + private Session session; - @PostConstruct - public void init() { - template = new ThriftColumnFamilyTemplate(keyspaceOperator, - getFollowersCF(), - StringSerializer.get(), - StringSerializer.get()); - - template.setCount(Constants.CASSANDRA_MAX_COLUMNS); - } + protected abstract String getFollowersCF(); - void addFollower(String key, String followerKey) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.insert(key, getFollowersCF(), HFactory.createColumn(followerKey, - Calendar.getInstance().getTimeInMillis(), StringSerializer.get(), LongSerializer.get())); - } + public void addFollower(String key, String login) { + Calendar cal = Calendar.getInstance(); + Statement statement = QueryBuilder.insertInto(getFollowersCF()) + .value("key", key) + .value("login", login); + session.execute(statement); - void removeFollower(String key, String followerKey) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.delete(key, getFollowersCF(), followerKey, StringSerializer.get()); } - Collection findFollowers(String key) { - ColumnFamilyResult result = template.queryColumns(key); - Collection followers = new ArrayList(); - for (String columnName : result.getColumnNames()) { - followers.add(columnName); - } - return followers; + public void removeFollower(String key, String login) { + Statement statement = QueryBuilder.delete() + .from(getFollowersCF()) + .where(eq("key",key)) + .and(eq("login",login)); + session.execute(statement); } - - protected abstract String getFollowersCF(); } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/AbstractCassandraFriendRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/AbstractCassandraFriendRepository.java index c887b2d14..b3f576161 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/AbstractCassandraFriendRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/AbstractCassandraFriendRepository.java @@ -1,60 +1,51 @@ package fr.ippon.tatami.repository.cassandra; -import fr.ippon.tatami.config.Constants; -import me.prettyprint.cassandra.serializers.LongSerializer; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.cassandra.service.template.ColumnFamilyResult; -import me.prettyprint.cassandra.service.template.ColumnFamilyTemplate; -import me.prettyprint.cassandra.service.template.ThriftColumnFamilyTemplate; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; - -import javax.annotation.PostConstruct; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.querybuilder.QueryBuilder; + import javax.inject.Inject; -import java.util.ArrayList; -import java.util.Calendar; import java.util.List; +import java.util.stream.Collectors; + +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; /** * Abstract class for managing friends : users or tags that a user follows. */ public abstract class AbstractCassandraFriendRepository { - private ColumnFamilyTemplate friendsTemplate; - @Inject - private Keyspace keyspaceOperator; + Session session; - @PostConstruct - public void init() { - friendsTemplate = new ThriftColumnFamilyTemplate(keyspaceOperator, - getFriendsCF(), - StringSerializer.get(), - StringSerializer.get()); + protected abstract String getFriendsTable(); - friendsTemplate.setCount(Constants.CASSANDRA_MAX_COLUMNS); - } + public void addFriend(String login, String friendTag) { + Statement statement = QueryBuilder.insertInto(getFriendsTable()) + .value("login", login) + .value("friendLogin", friendTag); + session.execute(statement); - void addFriend(String key, String friendKey) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.insert(key, getFriendsCF(), HFactory.createColumn(friendKey, - Calendar.getInstance().getTimeInMillis(), StringSerializer.get(), LongSerializer.get())); } - void removeFriend(String key, String friendKey) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.delete(key, getFriendsCF(), friendKey, StringSerializer.get()); + public void removeFriend(String login, String friendTag) { + Statement statement = QueryBuilder.delete().from(getFriendsTable()) + .where(eq("login", login)) + .and(eq("friendLogin", friendTag)); + session.execute(statement); } - List findFriends(String key) { - ColumnFamilyResult result = friendsTemplate.queryColumns(key); - List friends = new ArrayList(); - for (String columnName : result.getColumnNames()) { - friends.add(columnName); - } - return friends; + public List findFriends(String login) { + Statement statement = QueryBuilder.select() + .column("friendLogin") + .from(getFriendsTable()) + .where(eq("login", login)); + ResultSet results = session.execute(statement); + return results + .all() + .stream() + .map(e -> e.getString("friendLogin")) + .collect(Collectors.toList()); } - - protected abstract String getFriendsCF(); } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/AbstractCassandraLineRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/AbstractCassandraLineRepository.java index f68e64403..9374b6ab4 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/AbstractCassandraLineRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/AbstractCassandraLineRepository.java @@ -1,155 +1,125 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.*; +import com.datastax.driver.core.querybuilder.QueryBuilder; +import com.datastax.driver.core.querybuilder.Select; +import com.datastax.driver.core.utils.UUIDs; import fr.ippon.tatami.domain.status.Share; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.cassandra.serializers.UUIDSerializer; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.beans.ColumnSlice; -import me.prettyprint.hector.api.beans.HColumn; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; -import me.prettyprint.hector.api.query.ColumnQuery; -import me.prettyprint.hector.api.query.QueryResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; -import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; + +import static com.datastax.driver.core.querybuilder.QueryBuilder.*; -import static me.prettyprint.hector.api.factory.HFactory.createSliceQuery; /** * This abstract class contains commun functions for Timeline and Userline. *

* Timeline and Userline have the same structure : - * - Key : login + * - Key : key * - Name : status Id * - Value : "" * * @author Julien Dubois */ public abstract class AbstractCassandraLineRepository { + @Inject + Session session; private final Logger log = LoggerFactory.getLogger(AbstractCassandraLineRepository.class); - @Inject - protected Keyspace keyspaceOperator; /** * Add a status to the CF. */ - protected void addStatus(String key, String cf, String statusId) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.insert(key, cf, HFactory.createColumn(UUID.fromString(statusId), - "", UUIDSerializer.get(), StringSerializer.get())); + protected void addStatus(String key, String table, String statusId) { + Statement statement = QueryBuilder.insertInto(table) + .value("key", key) + .value("status", UUID.fromString(statusId)); + session.execute(statement); } /** * Add a status with a time-to-live. */ - protected void addStatus(String key, String cf, String statusId, int ttl) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.insert(key, cf, HFactory.createColumn(UUID.fromString(statusId), - "", ttl, UUIDSerializer.get(), StringSerializer.get())); + protected void addStatus(String key, String table, String statusId, int ttl) { + Statement statement = QueryBuilder.insertInto(table) + .value("key", key) + .value("status", UUID.fromString(statusId)); + session.execute(statement); } + public abstract PreparedStatement getDeleteByIdStmt(); + /** * Remove a collection of statuses. */ - protected void removeStatuses(String key, String cf, Collection statusIdsToDelete) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); + protected void removeStatuses(String key, String table, Collection statusIdsToDelete) { + BatchStatement batch = new BatchStatement(); for (String statusId : statusIdsToDelete) { - mutator.addDeletion(key, cf, UUID.fromString(statusId), UUIDSerializer.get()); + batch.add(getDeleteByIdStmt().bind() + .setString("key", key) + .setUUID("statusId", UUID.fromString(statusId))); } - mutator.execute(); + session.execute(batch); } - List getLineFromCF(String cf, String login, int size, String start, String finish) { - List> result; - if (finish != null) { - ColumnSlice query = createSliceQuery(keyspaceOperator, - StringSerializer.get(), UUIDSerializer.get(), StringSerializer.get()) - .setColumnFamily(cf) - .setKey(login) - .setRange(UUID.fromString(finish), null, true, size) - .execute() - .get(); - - result = query.getColumns().subList(1, query.getColumns().size()); - } else if (start != null) { - ColumnSlice query = createSliceQuery(keyspaceOperator, - StringSerializer.get(), UUIDSerializer.get(), StringSerializer.get()) - .setColumnFamily(cf) - .setKey(login) - .setRange(null, UUID.fromString(start), true, size) - .execute() - .get(); - - int maxIndex = query.getColumns().size() - 1; - if (maxIndex < 0) { - maxIndex = 0; - } - result = query.getColumns().subList(0, maxIndex); - } else { - ColumnSlice query = createSliceQuery(keyspaceOperator, - StringSerializer.get(), UUIDSerializer.get(), StringSerializer.get()) - .setColumnFamily(cf) - .setKey(login) - .setRange(null, null, true, size) - .execute() - .get(); - - result = query.getColumns(); - } - - List line = new ArrayList(); - for (HColumn column : result) { - line.add(column.getName().toString()); + protected List getLineFromTable(String table, String key, int size, String start, String finish) { + Select.Where where = QueryBuilder.select() + .column("status") + .from(table) + .where(eq("key", key)); + if(finish != null) { + where.and(lt("status", UUID.fromString(finish))); + } else if(start != null) { + where.and(gt("status",UUID.fromString(start))); + }else if (size > 0) { + where.limit(size); } - return line; + where.orderBy(desc("status")); + Statement statement = where; + ResultSet results = session.execute(statement); + return results + .all() + .stream() + .map(e -> e.getUUID("status").toString()) + .collect(Collectors.toList()); } - void shareStatus(String login, + protected void shareStatus(String login, Share share, String columnFamily, String sharesColumnFamily) { - QueryResult> isStatusAlreadyinTimeline = - findByLoginAndStatusId(columnFamily, login, UUID.fromString(share.getOriginalStatusId())); - - if (isStatusAlreadyinTimeline.get() == null) { - QueryResult> isStatusAlreadyShared = - findByLoginAndStatusId(sharesColumnFamily, login, UUID.fromString(share.getOriginalStatusId())); - - if (isStatusAlreadyShared.get() == null) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - - mutator.insert(login, columnFamily, HFactory.createColumn(UUID.fromString(share.getStatusId()), - "", UUIDSerializer.get(), StringSerializer.get())); - - mutator.insert(login, sharesColumnFamily, HFactory.createColumn(UUID.fromString(share.getOriginalStatusId()), - "", UUIDSerializer.get(), StringSerializer.get())); - } else { - - log.debug("Shared status {} is already shared in {}", share.getOriginalStatusId(), columnFamily); - - } + if (!findByLoginAndStatusId(columnFamily,login,share.getStatusId()) && + !findByLoginAndStatusId(sharesColumnFamily,login,share.getStatusId())) { + Statement statement = QueryBuilder.insertInto(columnFamily) + .value("key", login) + .value("status", share.getStatusId()); + session.execute(statement); + statement = QueryBuilder.insertInto(sharesColumnFamily) + .value("key", login) + .value("status", share.getStatusId()); + session.execute(statement); } else { - log.debug("Shared status {} is already present in {}", share.getOriginalStatusId(), columnFamily); } } - QueryResult> findByLoginAndStatusId(String columnFamily, String login, UUID statusId) { - ColumnQuery columnQuery = - HFactory.createColumnQuery(keyspaceOperator, StringSerializer.get(), - UUIDSerializer.get(), StringSerializer.get()); + protected boolean findByLoginAndStatusId(String columnFamily, String key, UUID statusId) { + Statement statement = QueryBuilder.select() + .column("key") + .from(columnFamily) + .where(eq("key", key)) + .and(eq("status",statusId)); - columnQuery.setColumnFamily(columnFamily).setKey(login).setName(statusId); - return columnQuery.execute(); + ResultSet results = session.execute(statement); + return !results.isExhausted(); } } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraAppleDeviceRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraAppleDeviceRepository.java index da81fe765..1251fbf27 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraAppleDeviceRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraAppleDeviceRepository.java @@ -1,12 +1,10 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.querybuilder.QueryBuilder; import fr.ippon.tatami.repository.AppleDeviceRepository; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.beans.ColumnSlice; -import me.prettyprint.hector.api.beans.HColumn; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Repository; @@ -15,8 +13,10 @@ import javax.inject.Inject; import java.util.ArrayList; import java.util.Collection; +import java.util.stream.Collectors; + +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; -import static me.prettyprint.hector.api.factory.HFactory.createSliceQuery; /** * Cassandra implementation of the AppleDevice repository. @@ -36,38 +36,38 @@ public class CassandraAppleDeviceRepository implements AppleDeviceRepository { private final Logger log = LoggerFactory.getLogger(CassandraAppleDeviceRepository.class); @Inject - private Keyspace keyspaceOperator; + private Session session; @Override public void createAppleDevice(String login, String deviceId) { log.debug("Creating Apple Device for user {} : {}", login, deviceId); - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.insert(login, ColumnFamilyKeys.APPLE_DEVICE_CF, HFactory.createColumn(deviceId, - "", StringSerializer.get(), StringSerializer.get())); + Statement statement = QueryBuilder.insertInto(ColumnFamilyKeys.APPLE_DEVICE_CF) + .value("login", login) + .value("deviceId", deviceId); + session.execute(statement); } @Override public void removeAppleDevice(String login, String deviceId) { log.debug("Deleting Apple Device for user {} : {}", login, deviceId); - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.delete(login, ColumnFamilyKeys.APPLE_DEVICE_CF, deviceId, StringSerializer.get()); + Statement statement = QueryBuilder.delete().from(ColumnFamilyKeys.APPLE_DEVICE_CF) + .where(eq("login", login)) + .and(eq("deviceId", deviceId)); + session.execute(statement); } @Override public Collection findAppleDevices(String login) { - Collection deviceIds = new ArrayList(); - ColumnSlice result = createSliceQuery(keyspaceOperator, - StringSerializer.get(), StringSerializer.get(), StringSerializer.get()) - .setColumnFamily(ColumnFamilyKeys.APPLE_DEVICE_CF) - .setKey(login) - .setRange(null, null, false, Integer.MAX_VALUE) - .execute() - .get(); - - for (HColumn column : result.getColumns()) { - deviceIds.add(column.getName()); - } - return deviceIds; + Statement statement = QueryBuilder.select() + .column("deviceId") + .from(ColumnFamilyKeys.APPLE_DEVICE_CF) + .where(eq("login", login)); + ResultSet results = session.execute(statement); + return results + .all() + .stream() + .map(e -> e.getString("deviceId")) + .collect(Collectors.toList()); } } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraAppleDeviceUserRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraAppleDeviceUserRepository.java index 9ff0a1af0..c362c0490 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraAppleDeviceUserRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraAppleDeviceUserRepository.java @@ -1,18 +1,20 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.querybuilder.QueryBuilder; +import fr.ippon.tatami.config.ColumnFamilyKeys; import fr.ippon.tatami.repository.AppleDeviceUserRepository; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.beans.HColumn; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; -import me.prettyprint.hector.api.query.ColumnQuery; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Repository; import javax.inject.Inject; +import java.util.stream.Collectors; + +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; import static fr.ippon.tatami.config.ColumnFamilyKeys.APPLE_DEVICE_USER_CF; /** @@ -35,39 +37,36 @@ public class CassandraAppleDeviceUserRepository implements AppleDeviceUserReposi private static final String USER_LOGIN = "USER_LOGIN"; @Inject - private Keyspace keyspaceOperator; + private Session session; @Override public void createAppleDeviceForUser(String deviceId, String login) { log.debug("Mapping Apple device id to user {} : {}", deviceId, login); - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.insert(deviceId, APPLE_DEVICE_USER_CF, HFactory.createColumn(USER_LOGIN, - login, StringSerializer.get(), StringSerializer.get())); + Statement statement = QueryBuilder.insertInto(ColumnFamilyKeys.APPLE_DEVICE_USER_CF) + .value("deviceId", deviceId) + .value("login", login); + session.execute(statement); } @Override public void removeAppleDeviceForUser(String deviceId) { log.debug("Removing mapping of Apple device id {}", deviceId); - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.addDeletion(deviceId, APPLE_DEVICE_USER_CF); - mutator.execute(); + Statement statement = QueryBuilder.delete().from(ColumnFamilyKeys.APPLE_DEVICE_USER_CF) + .where(eq("deviceId", deviceId)); + session.execute(statement); } @Override public String findLoginForDeviceId(String deviceId) { log.debug("Finding user of Apple device id {}", deviceId); - ColumnQuery query = HFactory.createStringColumnQuery(keyspaceOperator); - HColumn column = - query.setColumnFamily(APPLE_DEVICE_USER_CF) - .setKey(deviceId) - .setName(USER_LOGIN) - .execute() - .get(); - - if (column != null) { - return column.getValue(); - } else { - return null; + Statement statement = QueryBuilder.select() + .column("login") + .from(ColumnFamilyKeys.APPLE_DEVICE_USER_CF) + .where(eq("deviceId", deviceId)); + ResultSet results = session.execute(statement); + if (!results.isExhausted()) { + return results.one().getString("login"); } + return null; } } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraAttachmentRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraAttachmentRepository.java index 887dc49da..b0b8cc0fd 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraAttachmentRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraAttachmentRepository.java @@ -1,17 +1,15 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.querybuilder.QueryBuilder; +import com.datastax.driver.core.utils.UUIDs; +import fr.ippon.tatami.config.ColumnFamilyKeys; import fr.ippon.tatami.domain.Attachment; +import fr.ippon.tatami.domain.Avatar; import fr.ippon.tatami.repository.AttachmentRepository; -import me.prettyprint.cassandra.serializers.BytesArraySerializer; -import me.prettyprint.cassandra.serializers.DateSerializer; -import me.prettyprint.cassandra.serializers.LongSerializer; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.cassandra.utils.TimeUUIDUtils; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.beans.HColumn; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; -import me.prettyprint.hector.api.query.ColumnQuery; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,8 +19,12 @@ import javax.inject.Inject; +import java.nio.ByteBuffer; import java.util.Date; +import java.util.UUID; +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; +import static com.datastax.driver.core.querybuilder.QueryBuilder.set; import static fr.ippon.tatami.config.ColumnFamilyKeys.ATTACHMENT_CF; @Repository @@ -37,41 +39,40 @@ public class CassandraAttachmentRepository implements AttachmentRepository { private final String CREATION_DATE = "creation_date"; @Inject - private Keyspace keyspaceOperator; + private Session session; @Override public void createAttachment(Attachment attachment) { - String attachmentId = TimeUUIDUtils.getUniqueTimeUUIDinMillis().toString(); - log.debug("Creating attachment : {}", attachment); - - attachment.setAttachmentId(attachmentId); - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - - mutator.insert(attachmentId, ATTACHMENT_CF, HFactory.createColumn(CONTENT, - attachment.getContent(), StringSerializer.get(), BytesArraySerializer.get())); - - mutator.insert(attachmentId, ATTACHMENT_CF, HFactory.createColumn(THUMBNAIL, - attachment.getThumbnail(), StringSerializer.get(), BytesArraySerializer.get())); - - mutator.insert(attachmentId, ATTACHMENT_CF, HFactory.createColumn(FILENAME, - attachment.getFilename(), StringSerializer.get(), StringSerializer.get())); - - mutator.insert(attachmentId, ATTACHMENT_CF, HFactory.createColumn(SIZE, - attachment.getSize(), StringSerializer.get(), LongSerializer.get())); - - mutator.insert(attachmentId, ATTACHMENT_CF, HFactory.createColumn(CREATION_DATE, - attachment.getCreationDate(), StringSerializer.get(), DateSerializer.get())); + ByteBuffer content = null; + ByteBuffer thumbnail = null; + if (attachment.getContent() != null) { + content = ByteBuffer.wrap(attachment.getContent()); + } + if (attachment.getThumbnail() != null) { + thumbnail = ByteBuffer.wrap(attachment.getThumbnail()); + } + UUID attachmentId = UUIDs.timeBased(); + log.debug("Creating attachment : {}", attachment); + attachment.setAttachmentId(attachmentId.toString()); + Statement statement = QueryBuilder.insertInto(ATTACHMENT_CF) + .value("id", UUID.fromString(attachment.getAttachmentId())) + .value(FILENAME, attachment.getFilename()) + .value(CONTENT, content) + .value(THUMBNAIL, thumbnail) + .value(SIZE,attachment.getSize()) + .value(CREATION_DATE,attachment.getCreationDate()); + session.execute(statement); } @Override @CacheEvict(value = "attachment-cache", key = "#attachment.attachmentId") public void deleteAttachment(Attachment attachment) { log.debug("Deleting attachment : {}", attachment); - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.addDeletion(attachment.getAttachmentId(), ATTACHMENT_CF); - mutator.execute(); + Statement statement = QueryBuilder.delete().from(ATTACHMENT_CF) + .where(eq("id", UUID.fromString(attachment.getAttachmentId()))); + session.execute(statement); } @Override @@ -88,34 +89,25 @@ public Attachment findAttachmentById(String attachmentId) { if (attachment == null) { return null; } - - ColumnQuery queryAttachment = HFactory.createColumnQuery(keyspaceOperator, - StringSerializer.get(), StringSerializer.get(), BytesArraySerializer.get()); - - HColumn columnAttachment = - queryAttachment.setColumnFamily(ATTACHMENT_CF) - .setKey(attachmentId) - .setName(CONTENT) - .execute() - .get(); - - attachment.setContent(columnAttachment.getValue()); - - ColumnQuery queryThumbnail = HFactory.createColumnQuery(keyspaceOperator, - StringSerializer.get(), StringSerializer.get(), BytesArraySerializer.get()); - - HColumn columnThumbnail = - queryThumbnail.setColumnFamily(ATTACHMENT_CF) - .setKey(attachmentId) - .setName(THUMBNAIL) - .execute() - .get(); - if(columnThumbnail != null && columnThumbnail.getValue().length > 0) { - attachment.setThumbnail(columnThumbnail.getValue()); - attachment.setHasThumbnail(true); - } - else { - attachment.setHasThumbnail(false); + Statement statement = QueryBuilder.select() + .column(CONTENT) + .from(ATTACHMENT_CF) + .where(eq("id", UUID.fromString(attachmentId))); + + ResultSet results = session.execute(statement); + attachment.setContent(results.one().getBytes(CONTENT).array()); + + statement = QueryBuilder.select() + .column(THUMBNAIL) + .from(ATTACHMENT_CF) + .where(eq("id", UUID.fromString(attachmentId))); + + results = session.execute(statement); + attachment.setThumbnail(results.one().getBytes(THUMBNAIL).array()); + if (attachment.getThumbnail() != null && attachment.getThumbnail().length > 0) { + attachment.setHasThumbnail(true); + } else { + attachment.setHasThumbnail(false); } return attachment; } @@ -125,68 +117,40 @@ public Attachment findAttachmentMetadataById(String attachmentId) { if (attachmentId == null) { return null; } - Attachment attachment = new Attachment(); - attachment.setAttachmentId(attachmentId); - - ColumnQuery queryFilename = HFactory.createColumnQuery(keyspaceOperator, - StringSerializer.get(), StringSerializer.get(), StringSerializer.get()); - - HColumn columnFilename = - queryFilename.setColumnFamily(ATTACHMENT_CF) - .setKey(attachmentId) - .setName(FILENAME) - .execute() - .get(); - - if (columnFilename != null && columnFilename.getValue() != null) { - attachment.setFilename(columnFilename.getValue()); - } else { - return null; - } - - ColumnQuery querySize = HFactory.createColumnQuery(keyspaceOperator, - StringSerializer.get(), StringSerializer.get(), LongSerializer.get()); - - HColumn columnSize = - querySize.setColumnFamily(ATTACHMENT_CF) - .setKey(attachmentId) - .setName(SIZE) - .execute() - .get(); - - if (columnSize != null && columnSize.getValue() != null) { - attachment.setSize(columnSize.getValue()); - } else { - return null; - } - - ColumnQuery queryCreationDate = HFactory.createColumnQuery(keyspaceOperator, - StringSerializer.get(), StringSerializer.get(), DateSerializer.get()); - - HColumn columnCreationDate = - queryCreationDate.setColumnFamily(ATTACHMENT_CF) - .setKey(attachmentId) - .setName(CREATION_DATE) - .execute() - .get(); - - if (columnCreationDate != null && columnCreationDate.getValue() != null) { - attachment.setCreationDate(columnCreationDate.getValue()); - } else { - attachment.setCreationDate(new Date()); + Statement statement = QueryBuilder.select() + .column(FILENAME) + .column(SIZE) + .column(CREATION_DATE) + .from(ATTACHMENT_CF) + .where(eq("id", UUID.fromString(attachmentId))); + + ResultSet results = session.execute(statement); + if (!results.isExhausted()) { + Row row = results.one(); + Attachment attachment = new Attachment(); + attachment.setAttachmentId(attachmentId); + attachment.setFilename(row.getString(FILENAME)); + attachment.setSize(row.getLong(SIZE)); + attachment.setCreationDate(row.getDate(CREATION_DATE)); + if (attachment.getCreationDate() == null) { + attachment.setCreationDate(new Date()); + } + return attachment; } - - return attachment; + return null; } @Override public Attachment updateThumbnail(Attachment attach) { log.debug("Updating thumbnail : {}", attach); - - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - - mutator.insert(attach.getAttachmentId(), ATTACHMENT_CF, HFactory.createColumn(THUMBNAIL, - attach.getThumbnail(), StringSerializer.get(), BytesArraySerializer.get())); + ByteBuffer thumbnail = null; + if (attach.getThumbnail() != null) { + thumbnail = ByteBuffer.wrap(attach.getThumbnail()); + } + Statement statement = QueryBuilder.update(ATTACHMENT_CF) + .with(set(THUMBNAIL, thumbnail)) + .where(eq("id", UUID.fromString(attach.getAttachmentId()))); + session.execute(statement); return attach; } } \ No newline at end of file diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraAvatarRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraAvatarRepository.java index 626cc36a5..8747eb08d 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraAvatarRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraAvatarRepository.java @@ -1,17 +1,14 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.querybuilder.QueryBuilder; +import com.datastax.driver.core.utils.UUIDs; import fr.ippon.tatami.domain.Avatar; import fr.ippon.tatami.repository.AvatarRepository; -import me.prettyprint.cassandra.serializers.BytesArraySerializer; -import me.prettyprint.cassandra.serializers.DateSerializer; -import me.prettyprint.cassandra.serializers.LongSerializer; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.cassandra.utils.TimeUUIDUtils; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.beans.HColumn; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; -import me.prettyprint.hector.api.query.ColumnQuery; +import io.netty.buffer.ByteBuf; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cache.annotation.CacheEvict; @@ -19,8 +16,13 @@ import org.springframework.stereotype.Repository; import javax.inject.Inject; +import java.nio.ByteBuffer; import java.util.Date; +import java.util.UUID; +import java.util.stream.Collectors; +import static com.datastax.driver.core.querybuilder.QueryBuilder.addAll; +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; import static fr.ippon.tatami.config.ColumnFamilyKeys.AVATAR_CF; @Repository @@ -34,40 +36,34 @@ public class CassandraAvatarRepository implements AvatarRepository { private final String CREATION_DATE = "creation_date"; @Inject - private Keyspace keyspaceOperator; + private Session session; - @Override - public void createAvatar(Avatar avatar) { - - String avatarId = TimeUUIDUtils.getUniqueTimeUUIDinMillis().toString(); - log.debug("Creating avatar : {}", avatar); - - - avatar.setAvatarId(avatarId); - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - - mutator.insert(avatarId, AVATAR_CF, HFactory.createColumn(CONTENT, - avatar.getContent(), StringSerializer.get(), BytesArraySerializer.get())); - - mutator.insert(avatarId, AVATAR_CF, HFactory.createColumn(FILENAME, - avatar.getFilename(), StringSerializer.get(), StringSerializer.get())); - - mutator.insert(avatarId, AVATAR_CF, HFactory.createColumn(SIZE, - avatar.getSize(), StringSerializer.get(), LongSerializer.get())); - mutator.insert(avatarId, AVATAR_CF, HFactory.createColumn(CREATION_DATE, - avatar.getCreationDate(), StringSerializer.get(), DateSerializer.get())); + @Override + public void createAvatar(Avatar avatar) { + ByteBuffer content = null; + if (avatar.getContent() != null) { + content = ByteBuffer.wrap(avatar.getContent()); + } + avatar.setAvatarId(UUIDs.timeBased().toString()); + Statement statement = QueryBuilder.insertInto("avatar") + .value("id", UUID.fromString(avatar.getAvatarId())) + .value(FILENAME, avatar.getFilename()) + .value(CONTENT, content) + .value(SIZE,avatar.getSize()) + .value(CREATION_DATE,avatar.getCreationDate()); + session.execute(statement); } @Override @CacheEvict(value = "avatar-cache") public void removeAvatar(String avatarId) { log.debug("Avatar deleted : {}", avatarId); - - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.addDeletion(avatarId, AVATAR_CF); - mutator.execute(); + Statement statement = QueryBuilder.delete() + .from("avatar") + .where(eq("id", UUID.fromString(avatarId))); + session.execute(statement); } @Override @@ -80,81 +76,66 @@ public Avatar findAvatarById(String avatarId) { Avatar avatar = this.findAttachmentMetadataById(avatarId); - if (avatar == null) { - return null; - } + if (avatar != null) { + Statement statement = QueryBuilder.select() + .column(CONTENT) + .from("avatar") + .where(eq("id", UUID.fromString(avatarId))); - ColumnQuery queryAttachment = HFactory.createColumnQuery(keyspaceOperator, - StringSerializer.get(), StringSerializer.get(), BytesArraySerializer.get()); - - HColumn columnAttachment = - queryAttachment.setColumnFamily(AVATAR_CF) - .setKey(avatarId) - .setName(CONTENT) - .execute() - .get(); + ResultSet results = session.execute(statement); + avatar.setContent(results.one().getBytes(CONTENT).array()); + } - avatar.setContent(columnAttachment.getValue()); return avatar; } - - Avatar findAttachmentMetadataById(String avatarId) { - if (avatarId == null) { - return null; - } - Avatar avatar = new Avatar(); - avatar.setAvatarId(avatarId); - - ColumnQuery queryFilename = HFactory.createColumnQuery(keyspaceOperator, - StringSerializer.get(), StringSerializer.get(), StringSerializer.get()); - - HColumn columnFilename = - queryFilename.setColumnFamily(AVATAR_CF) - .setKey(avatarId) - .setName(FILENAME) - .execute() - .get(); - - if (columnFilename != null && columnFilename.getValue() != null) { - avatar.setFilename(columnFilename.getValue()); - } else { + @Override + public Avatar findAvatarByFilename(String filename) { + if (filename == null) { return null; } - ColumnQuery querySize = HFactory.createColumnQuery(keyspaceOperator, - StringSerializer.get(), StringSerializer.get(), LongSerializer.get()); + Statement statement = QueryBuilder.select() + .column("id") + .column(FILENAME) + .column(SIZE) + .column(CREATION_DATE) + .from("avatar") + .where(eq("filename", filename)); - HColumn columnSize = - querySize.setColumnFamily(AVATAR_CF) - .setKey(avatarId) - .setName(SIZE) - .execute() - .get(); + ResultSet results = session.execute(statement); + return loadAvatar(results); + } - if (columnSize != null && columnSize.getValue() != null) { - avatar.setSize(columnSize.getValue()); - } else { - return null; + private Avatar loadAvatar(ResultSet results) { + if (!results.isExhausted()) { + Row row = results.one(); + Avatar avatar = new Avatar(); + avatar.setAvatarId(row.getUUID("id").toString()); + avatar.setFilename(row.getString(FILENAME)); + avatar.setSize(row.getLong(SIZE)); + avatar.setCreationDate(row.getDate(CREATION_DATE)); + return avatar; } + return null; + } - ColumnQuery queryCreationDate = HFactory.createColumnQuery(keyspaceOperator, - StringSerializer.get(), StringSerializer.get(), DateSerializer.get()); - - HColumn columnCreationDate = - queryCreationDate.setColumnFamily(AVATAR_CF) - .setKey(avatarId) - .setName(CREATION_DATE) - .execute() - .get(); - if (columnCreationDate != null && columnCreationDate.getValue() != null) { - avatar.setCreationDate(columnCreationDate.getValue()); - } else { - avatar.setCreationDate(new Date()); + Avatar findAttachmentMetadataById(String avatarId) { + if (avatarId == null) { + return null; } - return avatar; + Statement statement = QueryBuilder.select() + .column("id") + .column(FILENAME) + .column(SIZE) + .column(CREATION_DATE) + .from("avatar") + .where(eq("id", UUID.fromString(avatarId))); + + ResultSet results = session.execute(statement); + return loadAvatar(results); } } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraCounterRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraCounterRepository.java index 4f297fb23..444b5bb3a 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraCounterRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraCounterRepository.java @@ -1,20 +1,20 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.*; +import com.datastax.driver.core.querybuilder.QueryBuilder; import fr.ippon.tatami.repository.CounterRepository; -import me.prettyprint.cassandra.model.thrift.ThriftCounterColumnQuery; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.beans.HCounterColumn; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; -import me.prettyprint.hector.api.query.CounterQuery; import org.springframework.cache.annotation.CacheEvict; import org.springframework.stereotype.Repository; import fr.ippon.tatami.config.ColumnFamilyKeys; import javax.inject.Inject; -import static me.prettyprint.hector.api.factory.HFactory.createCounterColumn; +import java.util.stream.Collectors; + +import static com.datastax.driver.core.querybuilder.QueryBuilder.decr; +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; +import static com.datastax.driver.core.querybuilder.QueryBuilder.incr; + /** * Cassandra implementation of the Counter repository. @@ -29,14 +29,17 @@ @Repository public class CassandraCounterRepository implements CounterRepository { + public static final String COUNTER = "counter"; + public static final String LOGIN = "login"; + @Inject + Session session; + private static final String STATUS_COUNTER = "STATUS_COUNTER"; private static final String FOLLOWERS_COUNTER = "FOLLOWERS_COUNTER"; private static final String FRIENDS_COUNTER = "FRIENDS_COUNTER"; - @Inject - private Keyspace keyspaceOperator; @Override @CacheEvict(value = "user-cache", key = "#login") @@ -106,41 +109,43 @@ public void createStatusCounter(String login) { @Override public void deleteCounters(String login) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.addCounterDeletion(login, ColumnFamilyKeys.COUNTER_CF, STATUS_COUNTER, StringSerializer.get()); - mutator.addCounterDeletion(login, ColumnFamilyKeys.COUNTER_CF, FOLLOWERS_COUNTER, StringSerializer.get()); - mutator.addCounterDeletion(login, ColumnFamilyKeys.COUNTER_CF, FRIENDS_COUNTER, StringSerializer.get()); - mutator.execute(); + Statement statement = QueryBuilder.delete().from(COUNTER) + .where(eq(LOGIN, login)); + session.execute(statement); } private void createCounter(String counterName, String login) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.insertCounter(login, ColumnFamilyKeys.COUNTER_CF, - createCounterColumn(counterName, 0)); + Statement statement = QueryBuilder.update(COUNTER) + .with(incr(counterName,0)) + .where(eq(LOGIN,login)); + session.execute(statement); } private void incrementCounter(String counterName, String login) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.incrementCounter(login, ColumnFamilyKeys.COUNTER_CF, counterName, 1); + Statement statement = QueryBuilder.update(COUNTER) + .with(incr(counterName,1)) + .where(eq(LOGIN,login)); + session.execute(statement); } private void decrementCounter(String counterName, String login) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.decrementCounter(login, ColumnFamilyKeys.COUNTER_CF, counterName, 1); + Statement statement = QueryBuilder.update(COUNTER) + .with(decr(counterName,1)) + .where(eq(LOGIN,login)); + session.execute(statement); } private long getCounter(String counterName, String login) { - CounterQuery counter = - new ThriftCounterColumnQuery(keyspaceOperator, - StringSerializer.get(), - StringSerializer.get()); - - counter.setColumnFamily(ColumnFamilyKeys.COUNTER_CF).setKey(login).setName(counterName); - HCounterColumn counterColumn = counter.execute().get(); - if (counterColumn == null) { - return 0; + Statement statement = QueryBuilder.select() + .column(counterName) + .from(COUNTER) + .where(eq(LOGIN, login)); + ResultSet results = session.execute(statement); + Row row = results.one(); + if (row != null) { + return row.getLong(counterName); } else { - return counterColumn.getValue(); + return 0; } } } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraDaylineRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraDaylineRepository.java index 945fbd659..f40e3b695 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraDaylineRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraDaylineRepository.java @@ -1,24 +1,23 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.querybuilder.QueryBuilder; import fr.ippon.tatami.domain.UserStatusStat; import fr.ippon.tatami.domain.status.Status; import fr.ippon.tatami.repository.DaylineRepository; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.beans.CounterSlice; -import me.prettyprint.hector.api.beans.HCounterColumn; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; -import me.prettyprint.hector.api.query.SliceCounterQuery; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Repository; import javax.inject.Inject; import java.util.Collection; import java.util.TreeSet; +import java.util.stream.Collectors; +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; +import static com.datastax.driver.core.querybuilder.QueryBuilder.incr; import static fr.ippon.tatami.config.ColumnFamilyKeys.DAYLINE_CF; -import static me.prettyprint.hector.api.factory.HFactory.createCounterSliceQuery; /** * Cassandra implementation of the user repository. @@ -34,33 +33,34 @@ public class CassandraDaylineRepository implements DaylineRepository { @Inject - private Keyspace keyspaceOperator; + Session session; + + @Override public void addStatusToDayline(Status status, String day) { String key = getKey(status.getDomain(), day); - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.incrementCounter(key, DAYLINE_CF, status.getUsername(), 1); + Statement query = QueryBuilder.update("dayline") + .with(incr("statusCount", 1)) + // Use incr for counters + .where(eq("domainDay", key)).and(eq("username",status.getUsername())); + session.execute(query); } @Override @Cacheable("dayline-cache") public Collection getDayline(String domain, String day) { String key = getKey(domain, day); - Collection results = new TreeSet(); - SliceCounterQuery query = createCounterSliceQuery(keyspaceOperator, - StringSerializer.get(), StringSerializer.get()) - .setColumnFamily(DAYLINE_CF) - .setRange(null, null, false, Integer.MAX_VALUE) - .setKey(key); - - CounterSlice queryResult = query.execute().get(); - - for (HCounterColumn column : queryResult.getColumns()) { - UserStatusStat stat = new UserStatusStat(column.getName(), column.getValue()); - results.add(stat); - } - return results; + Statement statement = QueryBuilder.select() + .all() + .from("dayline") + .where(eq("domainDay", key)); + ResultSet results = session.execute(statement); + return results + .all() + .stream() + .map(e -> new UserStatusStat(e.getString("username"),e.getLong("statusCount"))) + .collect(Collectors.toCollection(TreeSet::new)); } /** diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraDiscussionRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraDiscussionRepository.java index 90e439fbc..bda3590f2 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraDiscussionRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraDiscussionRepository.java @@ -1,13 +1,6 @@ package fr.ippon.tatami.repository.cassandra; import fr.ippon.tatami.repository.DiscussionRepository; -import me.prettyprint.cassandra.serializers.LongSerializer; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.beans.ColumnSlice; -import me.prettyprint.hector.api.beans.HColumn; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; import org.springframework.cache.annotation.CacheEvict; import org.springframework.stereotype.Repository; @@ -17,7 +10,6 @@ import java.util.LinkedHashSet; import static fr.ippon.tatami.config.ColumnFamilyKeys.DISCUSSION_CF; -import static me.prettyprint.hector.api.factory.HFactory.createSliceQuery; /** * Cassandra implementation of the StatusDetails repository. @@ -32,47 +24,46 @@ @Repository public class CassandraDiscussionRepository implements DiscussionRepository { - @Inject - private Keyspace keyspaceOperator; @Override @CacheEvict(value = "status-cache", key = "#originalStatusId") public void addReplyToDiscussion(String originalStatusId, String replyStatusId) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.insert(originalStatusId, DISCUSSION_CF, - HFactory.createColumn( - Calendar.getInstance().getTimeInMillis(), - replyStatusId, - LongSerializer.get(), - StringSerializer.get())); +// Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); +// mutator.insert(originalStatusId, DISCUSSION_CF, +// HFactory.createColumn( +// Calendar.getInstance().getTimeInMillis(), +// replyStatusId, +// LongSerializer.get(), +// StringSerializer.get())); } @Override public Collection findStatusIdsInDiscussion(String originalStatusId) { - ColumnSlice result = createSliceQuery(keyspaceOperator, - StringSerializer.get(), LongSerializer.get(), StringSerializer.get()) - .setColumnFamily(DISCUSSION_CF) - .setKey(originalStatusId) - .setRange(null, null, false, Integer.MAX_VALUE) - .execute() - .get(); - - Collection statusIds = new LinkedHashSet(); - for (HColumn column : result.getColumns()) { - statusIds.add(column.getValue()); - } - return statusIds; +// ColumnSlice result = createSliceQuery(keyspaceOperator, +// StringSerializer.get(), LongSerializer.get(), StringSerializer.get()) +// .setColumnFamily(DISCUSSION_CF) +// .setKey(originalStatusId) +// .setRange(null, null, false, Integer.MAX_VALUE) +// .execute() +// .get(); +// +// Collection statusIds = new LinkedHashSet(); +// for (HColumn column : result.getColumns()) { +// statusIds.add(column.getValue()); +// } +// return statusIds; + return null; } @Override public boolean hasReply(String statusId) { - int zeroOrOne = HFactory.createCountQuery(keyspaceOperator, StringSerializer.get(), LongSerializer.get()) - .setColumnFamily(DISCUSSION_CF) - .setKey(statusId) - .setRange(null, null, 1) - .execute() - .get(); +// int zeroOrOne = HFactory.createCountQuery(keyspaceOperator, StringSerializer.get(), LongSerializer.get()) +// .setColumnFamily(DISCUSSION_CF) +// .setKey(statusId) +// .setRange(null, null, 1) +// .execute() +// .get(); - return zeroOrOne > 0; + return 1 > 0; } } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraDomainConfigurationRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraDomainConfigurationRepository.java index 8b4712a40..e3c7b7753 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraDomainConfigurationRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraDomainConfigurationRepository.java @@ -1,9 +1,19 @@ package fr.ippon.tatami.repository.cassandra; +import javax.annotation.PostConstruct; import javax.inject.Inject; +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; -import me.prettyprint.hom.EntityManagerImpl; +import com.datastax.driver.core.BatchStatement; +import com.datastax.driver.core.PreparedStatement; +import com.datastax.driver.core.Session; +import com.datastax.driver.mapping.Mapper; +import com.datastax.driver.mapping.MappingManager; +import fr.ippon.tatami.domain.Domain; +import fr.ippon.tatami.domain.User; +import fr.ippon.tatami.domain.validation.ContraintsUserCreation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Repository; @@ -11,6 +21,9 @@ import fr.ippon.tatami.domain.DomainConfiguration; import fr.ippon.tatami.repository.DomainConfigurationRepository; +import java.util.HashSet; +import java.util.Set; + /** * Cassandra implementation of the DomainConfiguration repository. * @@ -21,37 +34,63 @@ public class CassandraDomainConfigurationRepository implements DomainConfigurationRepository { private final Logger log = LoggerFactory.getLogger(CassandraDomainConfigurationRepository.class); - + @Inject - private EntityManagerImpl em; + private Session session; + + private Mapper mapper; + + private PreparedStatement findOneByDomainStmt; + + + private PreparedStatement deleteByLoginStmt; + + @PostConstruct + public void init() { + mapper = new MappingManager(session).mapper(DomainConfiguration.class); + findOneByDomainStmt = session.prepare( + "SELECT * " + + "FROM domainConfiguration " + + "WHERE domain = :domain"); + deleteByLoginStmt = session.prepare("DELETE FROM user " + + "WHERE login = :login"); + } @Override public void updateDomainConfiguration(DomainConfiguration domainConfiguration) { setDefaultValues(domainConfiguration); - em.persist(domainConfiguration); + BatchStatement batch = new BatchStatement(); + batch.add(mapper.saveQuery(domainConfiguration)); + session.execute(batch); } @Override public DomainConfiguration findDomainConfigurationByDomain(String domain) { - DomainConfiguration domainConfiguration; - try { - domainConfiguration = em.find(DomainConfiguration.class, domain); - } catch (Exception e) { - - log.debug("Exception while looking for domain {} : {}", domain, e.toString()); - - return null; - } + DomainConfiguration domainConfiguration = null; if (domainConfiguration == null) { domainConfiguration = new DomainConfiguration(); domainConfiguration.setDomain(domain); setDefaultValues(domainConfiguration); - em.persist(domainConfiguration); - } - if (domain.equals("ippon.fr")) { - domainConfiguration.setSubscriptionLevel(DomainConfiguration.SubscriptionAndStorageSizeOptions.IPPONSUSCRIPTION); - domainConfiguration.setStorageSize(DomainConfiguration.SubscriptionAndStorageSizeOptions.IPPONSIZE); } + +// try { +// domainConfiguration = em.find(DomainConfiguration.class, domain); +// } catch (Exception e) { +// +// log.debug("Exception while looking for domain {} : {}", domain, e.toString()); +// +// return null; +// } +// if (domainConfiguration == null) { +// domainConfiguration = new DomainConfiguration(); +// domainConfiguration.setDomain(domain); +// setDefaultValues(domainConfiguration); +//// em.persist(domainConfiguration); +// } +// if (domain.equals("ippon.fr")) { +// domainConfiguration.setSubscriptionLevel(DomainConfiguration.SubscriptionAndStorageSizeOptions.IPPONSUSCRIPTION); +// domainConfiguration.setStorageSize(DomainConfiguration.SubscriptionAndStorageSizeOptions.IPPONSIZE); +// } return domainConfiguration; } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraDomainRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraDomainRepository.java index 188aeb4c5..671116ecd 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraDomainRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraDomainRepository.java @@ -1,27 +1,22 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.querybuilder.QueryBuilder; +import com.datastax.driver.core.utils.UUIDs; import fr.ippon.tatami.config.Constants; import fr.ippon.tatami.domain.Domain; import fr.ippon.tatami.repository.DomainRepository; -import me.prettyprint.cassandra.serializers.LongSerializer; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.beans.ColumnSlice; -import me.prettyprint.hector.api.beans.HColumn; -import me.prettyprint.hector.api.beans.OrderedRows; -import me.prettyprint.hector.api.beans.Row; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; -import me.prettyprint.hector.api.query.QueryResult; -import me.prettyprint.hector.api.query.RangeSlicesQuery; import org.springframework.stereotype.Repository; import javax.inject.Inject; import java.util.*; +import java.util.stream.Collectors; +import static com.datastax.driver.core.querybuilder.QueryBuilder.*; import static fr.ippon.tatami.config.ColumnFamilyKeys.DOMAIN_CF; -import static me.prettyprint.hector.api.factory.HFactory.createRangeSlicesQuery; -import static me.prettyprint.hector.api.factory.HFactory.createSliceQuery; /** * Cassandra implementation of the Domain repository. @@ -36,14 +31,17 @@ @Repository public class CassandraDomainRepository implements DomainRepository { + public static final String DOMAIN_ID = "domainId"; @Inject - private Keyspace keyspaceOperator; + private Session session; @Override public void addUserInDomain(String domain, String login) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.insert(domain, DOMAIN_CF, HFactory.createColumn(login, - Calendar.getInstance().getTimeInMillis(), StringSerializer.get(), LongSerializer.get())); + Statement statement = QueryBuilder.insertInto("domain") + .value(DOMAIN_ID, domain) + .value("login", login) + .value("created", UUIDs.timeBased()); + session.execute(statement); } @Override @@ -53,71 +51,76 @@ public void updateUserInDomain(String domain, String login) { @Override public void deleteUserInDomain(String domain, String login) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.delete(domain, DOMAIN_CF, login, StringSerializer.get()); + Statement statement = QueryBuilder.delete() + .from("domain") + .where(eq(DOMAIN_ID,domain)) + .and(eq("login",login)); + session.execute(statement); } @Override public List getLoginsInDomain(String domain, int pagination) { int maxColumns = pagination + Constants.PAGINATION_SIZE; - List logins = new ArrayList(); - ColumnSlice result = createSliceQuery(keyspaceOperator, - StringSerializer.get(), StringSerializer.get(), StringSerializer.get()) - .setColumnFamily(DOMAIN_CF) - .setKey(domain) - .setRange(null, null, false, maxColumns) - .execute() - .get(); - - int index = 0; - for (HColumn column : result.getColumns()) { - // We take one more item, to display (or not) the "next" button if there is an item after the displayed list. - if (index > maxColumns) { - break; - } - if (index >= pagination) { - logins.add(column.getName()); - } - index++; - } - return logins; + Statement statement = QueryBuilder.select() + .column("login") + .from("domain") + .where(eq(DOMAIN_ID, domain)) + .limit(maxColumns+1); + + ResultSet results = session.execute(statement); + return results + .all() + .stream() + .skip(pagination) + .map(e -> e.getString("login")) + .collect(Collectors.toList()); } @Override public List getLoginsInDomain(String domain) { - List logins = new ArrayList(); - ColumnSlice result = createSliceQuery(keyspaceOperator, - StringSerializer.get(), StringSerializer.get(), StringSerializer.get()) - .setColumnFamily(DOMAIN_CF) - .setKey(domain) - .setRange(null, null, false, Integer.MAX_VALUE) - .execute() - .get(); - - for (HColumn column : result.getColumns()) { - logins.add(column.getName()); - } - return logins; + Statement statement = QueryBuilder.select() + .column("login") + .from("domain") + .where(eq(DOMAIN_ID, domain)); + + ResultSet results = session.execute(statement); + return results + .all() + .stream() + .map(e -> e.getString("login")) + .collect(Collectors.toList()); } @Override public Set getAllDomains() { - Set domains = new HashSet(); - RangeSlicesQuery query = createRangeSlicesQuery(keyspaceOperator, - StringSerializer.get(), StringSerializer.get(), StringSerializer.get()) - .setColumnFamily(DOMAIN_CF) - .setRange(null, null, false, Integer.MAX_VALUE) - .setRowCount(Constants.CASSANDRA_MAX_ROWS); - - QueryResult> result = query.execute(); - List> rows = result.get().getList(); - for (Row row : rows) { + Statement statement = QueryBuilder.select() + .distinct() + .column(DOMAIN_ID) + .from("domain"); + + ResultSet results = session.execute(statement); + Set domainMaps = new HashSet<>(); + + for (Row result : results) { + String domainId = result.getString(DOMAIN_ID); Domain domain = new Domain(); - domain.setName(row.getKey()); - domain.setNumberOfUsers(row.getColumnSlice().getColumns().size()); + domain.setName(domainId); + domain.setNumberOfUsers((int)getCountForDomain(domain.getName())); + domainMaps.add(domain); + } + return domainMaps; + } + + private long getCountForDomain(String name) { + Statement statement = QueryBuilder.select() + .countAll() + .from("domain") + .where(eq(DOMAIN_ID,name)); - domains.add(domain); + ResultSet results = session.execute(statement); + if (!results.isExhausted()) { + return results.one().getLong(0); } - return domains; + return 0L; } } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraDomainlineRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraDomainlineRepository.java index 4f3aaacdb..5973692c1 100755 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraDomainlineRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraDomainlineRepository.java @@ -1,14 +1,14 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.PreparedStatement; import fr.ippon.tatami.repository.DomainlineRepository; -import me.prettyprint.hector.api.Keyspace; import org.springframework.stereotype.Repository; -import javax.inject.Inject; +import javax.annotation.PostConstruct; import java.util.Collection; import java.util.List; -import static fr.ippon.tatami.config.ColumnFamilyKeys.DOMAINLINE_CF; +import static fr.ippon.tatami.config.ColumnFamilyKeys.DOMAINLINE; /** * Cassandra implementation of the Domain line repository. @@ -25,21 +25,42 @@ public class CassandraDomainlineRepository extends AbstractCassandraLineReposito private final static int COLUMN_TTL = 60 * 60 * 24 * 30; // The column is stored for 30 days. - @Inject - private Keyspace keyspaceOperator; + private PreparedStatement findByLoginStmt; + + private PreparedStatement deleteByIdStmt; + + + @PostConstruct + public void init() { + findByLoginStmt = session.prepare( + "SELECT * " + + "FROM " + DOMAINLINE+ + " WHERE key = :key"); + + deleteByIdStmt = session.prepare("DELETE FROM " + DOMAINLINE + + " WHERE key = :key " + + "AND status = :statusId"); + + } + @Override public void addStatusToDomainline(String domain, String statusId) { - addStatus(domain, DOMAINLINE_CF, statusId, COLUMN_TTL); + addStatus(domain, DOMAINLINE, statusId, COLUMN_TTL); } @Override public void removeStatusFromDomainline(String domain, Collection statusIdsToDelete) { - removeStatuses(domain, DOMAINLINE_CF, statusIdsToDelete); + removeStatuses(domain, DOMAINLINE, statusIdsToDelete); } @Override public List getDomainline(String domain, int size, String start, String finish) { - return getLineFromCF(DOMAINLINE_CF, domain, size, start, finish); + return getLineFromTable(DOMAINLINE, domain, size, start, finish); + } + + @Override + public PreparedStatement getDeleteByIdStmt() { + return deleteByIdStmt; } } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraFavoritelineRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraFavoritelineRepository.java index 11d466818..3244db57e 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraFavoritelineRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraFavoritelineRepository.java @@ -1,24 +1,27 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.BatchStatement; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.querybuilder.QueryBuilder; +import com.datastax.driver.core.querybuilder.Select; import fr.ippon.tatami.repository.FavoritelineRepository; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.cassandra.serializers.UUIDSerializer; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.beans.ColumnSlice; -import me.prettyprint.hector.api.beans.HColumn; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Repository; +import org.springframework.web.bind.annotation.SessionAttributes; import javax.inject.Inject; import java.util.ArrayList; import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; +import static com.datastax.driver.core.querybuilder.QueryBuilder.gt; +import static com.datastax.driver.core.querybuilder.QueryBuilder.lt; import static fr.ippon.tatami.config.ColumnFamilyKeys.FAVLINE_CF; -import static me.prettyprint.hector.api.factory.HFactory.createSliceQuery; /** * Cassandra implementation of the favoriteline repository. @@ -34,45 +37,49 @@ public class CassandraFavoritelineRepository implements FavoritelineRepository { @Inject - private Keyspace keyspaceOperator; + Session session; @Override @CacheEvict(value = "favorites-cache", key = "#login") public void addStatusToFavoriteline(String login, String statusId) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.insert(login, FAVLINE_CF, HFactory.createColumn(UUID.fromString(statusId), "", - UUIDSerializer.get(), StringSerializer.get())); + Statement statement = QueryBuilder.insertInto("favline") + .value("key", login) + .value("status", UUID.fromString(statusId)); + session.execute(statement); } @Override @CacheEvict(value = "favorites-cache", key = "#login") public void removeStatusFromFavoriteline(String login, String statusId) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.delete(login, FAVLINE_CF, UUID.fromString(statusId), UUIDSerializer.get()); + Statement statement = QueryBuilder.delete() + .from("favline") + .where(eq("key",login)) + .and(eq("status",UUID.fromString(statusId))); + session.execute(statement); } @Override @Cacheable("favorites-cache") public List getFavoriteline(String login) { - List line = new ArrayList(); - ColumnSlice result = createSliceQuery(keyspaceOperator, - StringSerializer.get(), UUIDSerializer.get(), StringSerializer.get()) - .setColumnFamily(FAVLINE_CF) - .setKey(login) - .setRange(null, null, true, 50) - .execute() - .get(); + Statement statement = QueryBuilder.select() + .column("status") + .from("favline") + .where(eq("key", login)) + .limit(50); + ResultSet results = session.execute(statement); - for (HColumn column : result.getColumns()) { - line.add(column.getName().toString()); - } - return line; + return results + .all() + .stream() + .map(e -> e.getUUID("status").toString()) + .collect(Collectors.toList()); } @Override public void deleteFavoriteline(String login) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.addDeletion(login, FAVLINE_CF); - mutator.execute(); + Statement statement = QueryBuilder.delete() + .from("favline") + .where(eq("key",login)); + session.execute(statement); } } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraFollowerRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraFollowerRepository.java index d6e0deba0..32d29b0fe 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraFollowerRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraFollowerRepository.java @@ -1,12 +1,25 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.querybuilder.QueryBuilder; +import com.datastax.driver.core.querybuilder.Select; +import com.datastax.driver.core.querybuilder.Select.Where; import fr.ippon.tatami.repository.FollowerRepository; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Repository; import fr.ippon.tatami.config.ColumnFamilyKeys; +import javax.inject.Inject; import java.util.Collection; +import java.util.UUID; +import java.util.stream.Collectors; + +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; +import static com.datastax.driver.core.querybuilder.QueryBuilder.gt; +import static com.datastax.driver.core.querybuilder.QueryBuilder.lt; /** * Cassandra implementation of the Follower repository. @@ -21,6 +34,9 @@ @Repository public class CassandraFollowerRepository extends AbstractCassandraFollowerRepository implements FollowerRepository { + @Inject + Session session; + @Override @CacheEvict(value = "followers-cache", key = "#login") public void addFollower(String login, String followerLogin) { @@ -36,7 +52,17 @@ public void removeFollower(String login, String followerLogin) { @Override @Cacheable("followers-cache") public Collection findFollowersForUser(String login) { - return super.findFollowers(login); + + Statement statement = QueryBuilder.select() + .column("login") + .from("followers") + .where(eq("key", login)); + ResultSet results = session.execute(statement); + return results + .all() + .stream() + .map(e -> e.getString("login")) + .collect(Collectors.toList()); } @Override diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraFriendRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraFriendRepository.java index 5e89b9a7e..5c044087b 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraFriendRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraFriendRepository.java @@ -7,7 +7,7 @@ import java.util.List; -import static fr.ippon.tatami.config.ColumnFamilyKeys.FRIENDS_CF; +import static fr.ippon.tatami.config.ColumnFamilyKeys.FRIENDS; /** * Cassandra implementation of the Friend repository. @@ -41,7 +41,7 @@ public List findFriendsForUser(String login) { } @Override - public String getFriendsCF() { - return FRIENDS_CF; + public String getFriendsTable() { + return FRIENDS; } } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraGroupCounterRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraGroupCounterRepository.java index db7fbdd7e..b822a7ec2 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraGroupCounterRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraGroupCounterRepository.java @@ -1,19 +1,22 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.querybuilder.QueryBuilder; +import fr.ippon.tatami.config.ColumnFamilyKeys; import fr.ippon.tatami.repository.GroupCounterRepository; -import me.prettyprint.cassandra.model.thrift.ThriftCounterColumnQuery; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; -import me.prettyprint.hector.api.query.CounterQuery; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Repository; import javax.inject.Inject; +import java.util.UUID; -import static fr.ippon.tatami.config.ColumnFamilyKeys.GROUP_COUNTER_CF; +import static com.datastax.driver.core.querybuilder.QueryBuilder.decr; +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; +import static com.datastax.driver.core.querybuilder.QueryBuilder.incr; /** * Cassandra implementation of the Group Counter repository. @@ -29,37 +32,48 @@ public class CassandraGroupCounterRepository implements GroupCounterRepository { @Inject - private Keyspace keyspaceOperator; + private Session session; @Override - public long getGroupCounter(String domain, String groupId) { - CounterQuery counter = - new ThriftCounterColumnQuery(keyspaceOperator, - StringSerializer.get(), - StringSerializer.get()); - - counter.setColumnFamily(GROUP_COUNTER_CF).setKey(domain).setName(groupId); - return counter.execute().get().getValue(); + public long getGroupCounter(String domain, UUID groupId) { + Statement statement = QueryBuilder.select() + .column("counter") + .from(ColumnFamilyKeys.GROUP_COUNTER_CF) + .where(eq("domain", domain)) + .and(eq("groupId",groupId)); + ResultSet results = session.execute(statement); + if (!results.isExhausted()) { + return results.one().getLong("counter"); + } else { + return 0; + } } protected final Logger log = LoggerFactory.getLogger(this.getClass().getCanonicalName()); @Override - public void incrementGroupCounter(String domain, String groupId) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.incrementCounter(domain, GROUP_COUNTER_CF, groupId, 1); + public void incrementGroupCounter(String domain, UUID groupId) { + Statement statement = QueryBuilder.update(ColumnFamilyKeys.GROUP_COUNTER_CF) + .with(incr("counter",1)) + .where(eq("domain",domain)) + .and(eq("groupId", groupId)); + session.execute(statement); } @Override - public void decrementGroupCounter(String domain, String groupId) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.decrementCounter(domain, GROUP_COUNTER_CF, groupId, 1); + public void decrementGroupCounter(String domain, UUID groupId) { + Statement statement = QueryBuilder.update(ColumnFamilyKeys.GROUP_COUNTER_CF) + .with(decr("counter",1)) + .where(eq("domain",domain)) + .and(eq("groupId", groupId)); + session.execute(statement); } @Override public void deleteGroupCounter(String domain, String groupId) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.addCounterDeletion(domain, GROUP_COUNTER_CF, groupId, StringSerializer.get()); - mutator.execute(); + Statement statement = QueryBuilder.delete().from(ColumnFamilyKeys.GROUP_COUNTER_CF) + .where(eq("domain", domain)) + .and(eq("groupId",groupId)); + session.execute(statement); } } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraGroupDetailsRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraGroupDetailsRepository.java deleted file mode 100644 index 823411fa7..000000000 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraGroupDetailsRepository.java +++ /dev/null @@ -1,92 +0,0 @@ -package fr.ippon.tatami.repository.cassandra; - -import fr.ippon.tatami.domain.Group; -import fr.ippon.tatami.repository.GroupDetailsRepository; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.beans.ColumnSlice; -import me.prettyprint.hector.api.beans.HColumn; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; -import org.springframework.stereotype.Repository; - -import javax.inject.Inject; - -import static fr.ippon.tatami.config.ColumnFamilyKeys.GROUP_DETAILS_CF; -import static me.prettyprint.hector.api.factory.HFactory.createSliceQuery; - -/** - * Cassandra implementation of the Group Details repository. - *

- * Structure : - * - Key = Group ID - * - Name / Value pairs of group details - * - * @author Julien Dubois - */ -@Repository -public class CassandraGroupDetailsRepository implements GroupDetailsRepository { - - private static final String NAME = "name"; - private static final String DESCRIPTION = "description"; - private static final String PUBLIC_GROUP = "publicGroup"; - private static final String ARCHIVED_GROUP = "archivedGroup"; - - @Inject - private Keyspace keyspaceOperator; - - @Override - public void createGroupDetails(String groupId, String name, String description, boolean publicGroup) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.insert(groupId, GROUP_DETAILS_CF, HFactory.createColumn(NAME, - name, StringSerializer.get(), StringSerializer.get())); - mutator.insert(groupId, GROUP_DETAILS_CF, HFactory.createColumn(DESCRIPTION, - description, StringSerializer.get(), StringSerializer.get())); - mutator.insert(groupId, GROUP_DETAILS_CF, HFactory.createColumn(PUBLIC_GROUP, - (Boolean.valueOf(publicGroup)).toString(), StringSerializer.get(), StringSerializer.get())); - mutator.insert(groupId, GROUP_DETAILS_CF, HFactory.createColumn(ARCHIVED_GROUP, - Boolean.FALSE.toString(), StringSerializer.get(), StringSerializer.get())); - } - - @Override - public void editGroupDetails(String groupId, String name, String description, boolean archivedGroup) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.insert(groupId, GROUP_DETAILS_CF, HFactory.createColumn(NAME, - name, StringSerializer.get(), StringSerializer.get())); - mutator.insert(groupId, GROUP_DETAILS_CF, HFactory.createColumn(DESCRIPTION, - description, StringSerializer.get(), StringSerializer.get())); - mutator.insert(groupId, GROUP_DETAILS_CF, HFactory.createColumn(ARCHIVED_GROUP, - (Boolean.valueOf(archivedGroup)).toString(), StringSerializer.get(), StringSerializer.get())); - } - - @Override - public Group getGroupDetails(String groupId) { - Group group = new Group(); - group.setGroupId(groupId); - group.setPublicGroup(false); - ColumnSlice result = createSliceQuery(keyspaceOperator, - StringSerializer.get(), StringSerializer.get(), StringSerializer.get()) - .setColumnFamily(GROUP_DETAILS_CF) - .setKey(groupId) - .setRange(null, null, false, 4) - .execute() - .get(); - - for (HColumn column : result.getColumns()) { - if (column.getName().equals(NAME)) { - group.setName(column.getValue()); - } else if (column.getName().equals(DESCRIPTION)) { - group.setDescription(column.getValue()); - } else if (column.getName().equals(PUBLIC_GROUP)) { - if (column.getValue().equals(Boolean.TRUE.toString())) { - group.setPublicGroup(true); - } - } else if (column.getName().equals(ARCHIVED_GROUP)) { - if (column.getValue().equals(Boolean.TRUE.toString())) { - group.setArchivedGroup(true); - } - } - } - return group; - } -} diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraGroupMembersRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraGroupMembersRepository.java index 2dadde33b..d7a95503f 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraGroupMembersRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraGroupMembersRepository.java @@ -1,21 +1,19 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.querybuilder.QueryBuilder; import fr.ippon.tatami.config.GroupRoles; import fr.ippon.tatami.repository.GroupMembersRepository; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.beans.ColumnSlice; -import me.prettyprint.hector.api.beans.HColumn; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; import org.springframework.stereotype.Repository; import javax.inject.Inject; -import java.util.HashMap; import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; -import static fr.ippon.tatami.config.ColumnFamilyKeys.GROUP_MEMBERS_CF; -import static me.prettyprint.hector.api.factory.HFactory.createSliceQuery; +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; /** * Cassandra implementation of the Group members repository. @@ -30,43 +28,52 @@ @Repository public class CassandraGroupMembersRepository implements GroupMembersRepository { + public static final String GROUP_MEMBER = "groupMember"; + public static final String LOGIN = "login"; + public static final String ROLE = "role"; + public static final String GROUP_ID = "groupId"; @Inject - private Keyspace keyspaceOperator; + Session session; @Override - public void addMember(String groupId, String login) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.insert(groupId, GROUP_MEMBERS_CF, HFactory.createColumn(login, - GroupRoles.MEMBER, StringSerializer.get(), StringSerializer.get())); + public void addMember(UUID groupId, String login) { + Statement statement = QueryBuilder.insertInto(GROUP_MEMBER) + .value(GROUP_ID, groupId) + .value(LOGIN, login) + .value(ROLE, GroupRoles.MEMBER); + session.execute(statement); } @Override - public void addAdmin(String groupId, String login) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.insert(groupId, GROUP_MEMBERS_CF, HFactory.createColumn(login, - GroupRoles.ADMIN, StringSerializer.get(), StringSerializer.get())); + public void addAdmin(UUID groupId, String login) { + Statement statement = QueryBuilder.insertInto(GROUP_MEMBER) + .value(GROUP_ID, groupId) + .value(LOGIN, login) + .value(ROLE, GroupRoles.MEMBER); + session.execute(statement); } @Override - public void removeMember(String groupId, String login) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.delete(groupId, GROUP_MEMBERS_CF, login, StringSerializer.get()); + public void removeMember(UUID groupId, String login) { + Statement statement = QueryBuilder.delete().from(GROUP_MEMBER) + .where(eq(GROUP_ID, groupId)) + .and(eq(LOGIN, login)); + session.execute(statement); + } @Override - public Map findMembers(String groupId) { - Map members = new HashMap(); - ColumnSlice result = createSliceQuery(keyspaceOperator, - StringSerializer.get(), StringSerializer.get(), StringSerializer.get()) - .setColumnFamily(GROUP_MEMBERS_CF) - .setKey(groupId) - .setRange(null, null, false, Integer.MAX_VALUE) - .execute() - .get(); - - for (HColumn column : result.getColumns()) { - members.put(column.getName(), column.getValue()); - } - return members; + public Map findMembers(UUID groupId) { + Statement statement = QueryBuilder.select() + .all() + .from(GROUP_MEMBER) + .where(eq(GROUP_ID, groupId)); + ResultSet results = session.execute(statement); + return results + .all() + .stream() + .collect(Collectors.toMap( + e -> e.getString(LOGIN), + e -> e.getString(ROLE))); } } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraGroupRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraGroupRepository.java index 58035e52d..88c7ea186 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraGroupRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraGroupRepository.java @@ -1,19 +1,21 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.querybuilder.QueryBuilder; +import com.datastax.driver.core.utils.UUIDs; +import fr.ippon.tatami.config.ColumnFamilyKeys; import fr.ippon.tatami.domain.Group; import fr.ippon.tatami.repository.GroupRepository; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.cassandra.utils.TimeUUIDUtils; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.beans.HColumn; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; -import me.prettyprint.hector.api.query.ColumnQuery; import org.springframework.stereotype.Repository; import javax.inject.Inject; -import static fr.ippon.tatami.config.ColumnFamilyKeys.GROUP_CF; +import java.util.UUID; + +import static com.datastax.driver.core.querybuilder.QueryBuilder.*; /** * Cassandra implementation of the Group repository. @@ -29,32 +31,69 @@ public class CassandraGroupRepository implements GroupRepository { @Inject - private Keyspace keyspaceOperator; + private Session session; + + private static final String NAME = "name"; + private static final String DESCRIPTION = "description"; + private static final String PUBLIC_GROUP = "publicGroup"; + private static final String ARCHIVED_GROUP = "archivedGroup"; - @Override - public String createGroup(String domain) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - String groupId = TimeUUIDUtils.getUniqueTimeUUIDinMillis().toString(); - mutator.insert(domain, GROUP_CF, HFactory.createColumn(groupId, - "", StringSerializer.get(), StringSerializer.get())); + @Override + public UUID createGroup(String domain, String name, String description, boolean publicGroup) { + UUID groupId = UUIDs.timeBased(); + Statement statement = QueryBuilder.insertInto(ColumnFamilyKeys.GROUP_CF) + .value("id", groupId) + .value("domain", domain) + .value(NAME,name) + .value(DESCRIPTION, description) + .value(PUBLIC_GROUP,publicGroup) + .value(ARCHIVED_GROUP,false); + session.execute(statement); return groupId; } @Override - public Group getGroupById(String domain, String groupId) { - ColumnQuery query = HFactory.createStringColumnQuery(keyspaceOperator); - HColumn column = - query.setColumnFamily(GROUP_CF) - .setKey(domain) - .setName(groupId) - .execute() - .get(); - - if (column != null) { + public void editGroupDetails(UUID groupId, String name, String description, boolean archivedGroup) { + Statement statement = QueryBuilder.update(ColumnFamilyKeys.GROUP_CF) + .with(set(NAME,name)) + .and(set(DESCRIPTION,description)) + .and(set(ARCHIVED_GROUP,archivedGroup)) + .where(eq("id",groupId)); + session.execute(statement); + } + + @Override + public Group getGroupById(String domain, UUID groupId) { + Statement statement = QueryBuilder.select() + .all() + .from(ColumnFamilyKeys.GROUP_CF) + .where(eq("id", groupId)); + ResultSet results = session.execute(statement); + Row row = results.one(); + return getGroupFromRow(row); + } + + @Override + public Group getGroupByGroupId(UUID groupId) { + Statement statement = QueryBuilder.select() + .all() + .from(ColumnFamilyKeys.GROUP_CF) + .where(eq("id", groupId)); + ResultSet results = session.execute(statement); + Row row = results.one(); + return getGroupFromRow(row); + } + + private Group getGroupFromRow(Row row) { + if (row != null) { Group group = new Group(); - group.setDomain(domain); - group.setGroupId(groupId); + group.setGroupId(row.getUUID("id")); + group.setName(row.getString("name")); + group.setDomain(row.getString("domain")); + group.setDescription(row.getString(DESCRIPTION)); + group.setPublicGroup(row.getBool(PUBLIC_GROUP)); + group.setArchivedGroup(row.getBool(ARCHIVED_GROUP)); return group; } else { return null; diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraGrouplineRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraGrouplineRepository.java index 3f6eb088f..258b964c7 100755 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraGrouplineRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraGrouplineRepository.java @@ -1,13 +1,16 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.PreparedStatement; import fr.ippon.tatami.repository.GrouplineRepository; -import me.prettyprint.hector.api.Keyspace; import org.springframework.stereotype.Repository; import fr.ippon.tatami.config.ColumnFamilyKeys; -import javax.inject.Inject; +import javax.annotation.PostConstruct; import java.util.Collection; import java.util.List; +import java.util.UUID; + +import static fr.ippon.tatami.config.ColumnFamilyKeys.GROUPLINE; /** * Cassandra implementation of the Group line repository. @@ -22,21 +25,41 @@ @Repository public class CassandraGrouplineRepository extends AbstractCassandraLineRepository implements GrouplineRepository { - @Inject - private Keyspace keyspaceOperator; + private PreparedStatement findByLoginStmt; + + private PreparedStatement deleteByIdStmt; + + + @PostConstruct + public void init() { + findByLoginStmt = session.prepare( + "SELECT * " + + "FROM " + GROUPLINE+ + " WHERE key = :key"); + + deleteByIdStmt = session.prepare("DELETE FROM " + GROUPLINE + + " WHERE key = :key " + + "AND status = :statusId"); + + } @Override - public void addStatusToGroupline(String groupId, String statusId) { - addStatus(groupId, ColumnFamilyKeys.GROUPLINE_CF, statusId); + public void addStatusToGroupline(UUID groupId, String statusId) { + addStatus(groupId.toString(), ColumnFamilyKeys.GROUPLINE, statusId); } @Override public void removeStatusesFromGroupline(String groupId, Collection statusIdsToDelete) { - removeStatuses(groupId, ColumnFamilyKeys.GROUPLINE_CF, statusIdsToDelete); + removeStatuses(groupId, ColumnFamilyKeys.GROUPLINE, statusIdsToDelete); } @Override public List getGroupline(String groupId, int size, String start, String finish) { - return getLineFromCF(ColumnFamilyKeys.GROUPLINE_CF, groupId, size, start, finish); + return getLineFromTable(ColumnFamilyKeys.GROUPLINE, groupId, size, start, finish); + } + + @Override + public PreparedStatement getDeleteByIdStmt() { + return deleteByIdStmt; } } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraIdempotentRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraIdempotentRepository.java index 173ef1050..70cffc4f8 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraIdempotentRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraIdempotentRepository.java @@ -1,17 +1,21 @@ package fr.ippon.tatami.repository.cassandra; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.beans.HColumn; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; -import me.prettyprint.hector.api.query.ColumnQuery; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.querybuilder.QueryBuilder; +import fr.ippon.tatami.config.ColumnFamilyKeys; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import fr.ippon.tatami.repository.IdempotentRepository; import javax.inject.Inject; +import java.text.CollationKey; +import java.util.UUID; + +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; /** * Used to de-deplucate Camel messages. @@ -28,7 +32,7 @@ public class CassandraIdempotentRepository implements IdempotentRepository { private final static int COLUMN_TTL = 60 * 60 * 24 * 30; // The column is stored for 30 days. @Inject - private Keyspace keyspaceOperator; + private Session session; @Override public boolean add(String key) { @@ -37,18 +41,10 @@ public boolean add(String key) { return false; } else { log.debug("Adding new message to the idempotent repository"); - HColumn column = - HFactory.createColumn( - key, - "", - COLUMN_TTL, - StringSerializer.get(), - StringSerializer.get()); - - Mutator mutator = - HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - - mutator.insert(KEY, TATAMIBOT_DUPLICATE_CF, column); + Statement statement = QueryBuilder.insertInto(ColumnFamilyKeys.TATAMIBOT_DUPLICATE_CF) + .value(KEY, key) + .using(QueryBuilder.ttl(COLUMN_TTL)); + session.execute(statement); return true; } } @@ -57,22 +53,20 @@ public boolean add(String key) { public boolean contains(String key) { log.debug("Test message duplication with key : {}", key); - ColumnQuery query = HFactory.createStringColumnQuery(keyspaceOperator); - - HColumn column = - query.setColumnFamily(TATAMIBOT_DUPLICATE_CF) - .setKey(KEY) - .setName(key) - .execute() - .get(); + Statement statement = QueryBuilder.select() + .all() + .from(ColumnFamilyKeys.TATAMIBOT_DUPLICATE_CF) + .where(eq(KEY, key)); - return column != null; + ResultSet results = session.execute(statement); + return !results.isExhausted(); } @Override public boolean remove(String key) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.delete(KEY, TATAMIBOT_DUPLICATE_CF, key, StringSerializer.get()); + Statement statement = QueryBuilder.delete().from(TATAMIBOT_DUPLICATE_CF) + .where(eq(KEY, key)); + session.execute(statement); return true; } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraMailDigestRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraMailDigestRepository.java index 2048bf1b0..bc8c6bcc5 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraMailDigestRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraMailDigestRepository.java @@ -1,15 +1,13 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.querybuilder.QueryBuilder; +import com.datastax.driver.core.utils.UUIDs; import fr.ippon.tatami.config.Constants; import fr.ippon.tatami.domain.DigestType; import fr.ippon.tatami.repository.MailDigestRepository; -import me.prettyprint.cassandra.serializers.LongSerializer; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.beans.ColumnSlice; -import me.prettyprint.hector.api.beans.HColumn; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; import org.springframework.stereotype.Repository; import fr.ippon.tatami.config.ColumnFamilyKeys; @@ -17,8 +15,10 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.List; +import java.util.stream.Collectors; + +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; -import static me.prettyprint.hector.api.factory.HFactory.createSliceQuery; /** * MailDigestRepository implementation for cassandra @@ -37,49 +37,44 @@ public class CassandraMailDigestRepository implements MailDigestRepository { @Inject - private Keyspace keyspaceOperator; + private Session session; @Override public void subscribeToDigest(DigestType digestType, String login, String domain, String day) { - Calendar cal = Calendar.getInstance(); - - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.insert(buildKey(digestType, domain, day), ColumnFamilyKeys.MAILDIGEST_CF, - HFactory.createColumn(login, cal.getTimeInMillis(), StringSerializer.get(), LongSerializer.get())); + Statement statement = QueryBuilder.insertInto("mailDigest") + .value("digestId", buildKey(digestType, domain, day)) + .value("login", login) + .value("created", cal.getTimeInMillis()); + session.execute(statement); } @Override public void unsubscribeFromDigest(DigestType digestType, String login, String domain, String day) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.delete(buildKey(digestType, domain, day), ColumnFamilyKeys.MAILDIGEST_CF, login, StringSerializer.get()); + Statement statement = QueryBuilder.delete() + .from("mailDigest") + .where(eq("digestId",buildKey(digestType, domain, day))) + .and(eq("login",login)); + session.execute(statement); } @Override public List getLoginsRegisteredToDigest(DigestType digestType, String domain, String day, int pagination) { + int maxColumns = pagination + Constants.PAGINATION_SIZE; + Statement statement = QueryBuilder.select() + .column("login") + .from("mailDigest") + .where(eq("digestId", buildKey(digestType, domain, day))) + .limit(maxColumns+1); - List logins = new ArrayList(); - ColumnSlice result = createSliceQuery(keyspaceOperator, - StringSerializer.get(), StringSerializer.get(), StringSerializer.get()) - .setColumnFamily(ColumnFamilyKeys.MAILDIGEST_CF) - .setKey(buildKey(digestType, domain, day)) - .setRange(null, null, false, Integer.MAX_VALUE) - .execute() - .get(); - - int index = 0; - for (HColumn column : result.getColumns()) { - // We take one more item, to display (or not) the "next" button if there is an item after the displayed list. - if (index > pagination + Constants.PAGINATION_SIZE) { - break; - } - if (index >= pagination) { - logins.add(column.getName()); - } - index++; - } - return logins; + ResultSet results = session.execute(statement); + return results + .all() + .stream() + .skip(pagination) + .map(e -> e.getString("login")) + .collect(Collectors.toList()); } /** diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraMentionlineRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraMentionlineRepository.java index 9cc33f7bd..19d988331 100755 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraMentionlineRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraMentionlineRepository.java @@ -1,9 +1,13 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.PreparedStatement; +import com.datastax.driver.mapping.MappingManager; +import fr.ippon.tatami.domain.status.Status; import fr.ippon.tatami.repository.MentionlineRepository; import org.springframework.stereotype.Repository; import fr.ippon.tatami.config.ColumnFamilyKeys; +import javax.annotation.PostConstruct; import java.util.Collection; import java.util.List; @@ -20,18 +24,42 @@ @Repository public class CassandraMentionlineRepository extends AbstractCassandraLineRepository implements MentionlineRepository { + private PreparedStatement findByLoginStmt; + + private PreparedStatement deleteByIdStmt; + + + @PostConstruct + public void init() { + findByLoginStmt = session.prepare( + "SELECT * " + + "FROM mentionline " + + "WHERE key = :key"); + + deleteByIdStmt = session.prepare("DELETE FROM mentionline " + + "WHERE key = :key " + + "AND status = :statusId"); + + } + + @Override public void addStatusToMentionline(String mentionedLogin, String statusId) { - addStatus(mentionedLogin, ColumnFamilyKeys.MENTIONLINE_CF, statusId); + addStatus(mentionedLogin, ColumnFamilyKeys.MENTIONLINE, statusId); } @Override public void removeStatusesFromMentionline(String mentionedLogin, Collection statusIdsToDelete) { - removeStatuses(mentionedLogin, ColumnFamilyKeys.MENTIONLINE_CF, statusIdsToDelete); + removeStatuses(mentionedLogin, ColumnFamilyKeys.MENTIONLINE, statusIdsToDelete); } @Override public List getMentionline(String login, int size, String start, String finish) { - return getLineFromCF(ColumnFamilyKeys.MENTIONLINE_CF, login, size, start, finish); + return getLineFromTable("mentionLine", login, size, start, finish); + } + + @Override + public PreparedStatement getDeleteByIdStmt() { + return deleteByIdStmt; } } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraRegistrationRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraRegistrationRepository.java index 7f045a342..14ff31cf6 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraRegistrationRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraRegistrationRepository.java @@ -1,24 +1,21 @@ package fr.ippon.tatami.repository.cassandra; -import com.google.common.collect.Maps; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.querybuilder.QueryBuilder; import fr.ippon.tatami.repository.RegistrationRepository; import fr.ippon.tatami.service.util.RandomUtil; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.beans.ColumnSlice; -import me.prettyprint.hector.api.beans.HColumn; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; -import me.prettyprint.hector.api.query.ColumnQuery; -import me.prettyprint.hector.api.query.SliceQuery; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Repository; import javax.inject.Inject; -import java.util.List; +import java.util.HashMap; import java.util.Map; +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; import static fr.ippon.tatami.config.ColumnFamilyKeys.REGISTRATION_CF; /** @@ -41,34 +38,30 @@ public class CassandraRegistrationRepository implements RegistrationRepository { private final static int COLUMN_TTL = 60 * 60 * 24 * 2; // The column is stored for 2 days. @Inject - private Keyspace keyspaceOperator; + private Session session; @Override public String generateRegistrationKey(String login) { String key = RandomUtil.generateRegistrationKey(); - HColumn column = HFactory.createColumn(key, - login, COLUMN_TTL, StringSerializer.get(), StringSerializer.get()); - - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.insert(ROW_KEY, REGISTRATION_CF, column); + Statement statement = QueryBuilder.insertInto(REGISTRATION_CF) + .value(ROW_KEY, key) + .value("login",login) + .using(QueryBuilder.ttl(COLUMN_TTL)); + session.execute(statement); return key; } @Override public String getLoginByRegistrationKey(String registrationKey) { - ColumnQuery query = HFactory.createStringColumnQuery(keyspaceOperator); - HColumn column = - query.setColumnFamily(REGISTRATION_CF) - .setKey(ROW_KEY) - .setName(registrationKey) - .execute() - .get(); - - if (column != null) { - return column.getValue(); - } else { - return null; + Statement statement = QueryBuilder.select() + .all() + .from(REGISTRATION_CF) + .where(eq(ROW_KEY, registrationKey)); + ResultSet results = session.execute(statement); + if (!results.isExhausted()) { + return results.one().getString("login"); } + return null; } /** @@ -77,23 +70,14 @@ public String getLoginByRegistrationKey(String registrationKey) { * Other limitation : if a login is associated to multiple registrationKey */ public Map _getAllRegistrationKeyByLogin() { - log.warn("Calling _getAllRegistrationKeyByLogin() is only for testing purposes!"); - Map registrationKeyByLogin = Maps.newHashMap(); - SliceQuery sliceQuery = HFactory.createSliceQuery(keyspaceOperator, - StringSerializer.get(), StringSerializer.get(), StringSerializer.get()); - - ColumnSlice columnSlice = - sliceQuery.setColumnFamily(REGISTRATION_CF) - .setKey(ROW_KEY) - .setRange(null, null, false, 10000) - .execute().get(); - - List> columns = columnSlice.getColumns(); - - for (HColumn hColumn : columns) { - // WARN : here we don't handle multiple registrationKey for one login - registrationKeyByLogin.put(hColumn.getValue(), hColumn.getName()); - log.debug("Key={}|Value={}", hColumn.getValue(), hColumn.getName()); + Statement statement = QueryBuilder.select() + .all() + .from(REGISTRATION_CF) + .limit(10000); + ResultSet results = session.execute(statement); + Map registrationKeyByLogin = new HashMap<>(); + for (Row row : results.all()) { + registrationKeyByLogin.put(row.getString("login"),row.getString(ROW_KEY)); } return registrationKeyByLogin; } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraRssUidRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraRssUidRepository.java index ae435cb51..34f52c7e0 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraRssUidRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraRssUidRepository.java @@ -1,17 +1,17 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.querybuilder.QueryBuilder; +import fr.ippon.tatami.config.ColumnFamilyKeys; import fr.ippon.tatami.repository.RssUidRepository; import fr.ippon.tatami.service.util.RandomUtil; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.beans.HColumn; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; -import me.prettyprint.hector.api.query.ColumnQuery; import org.springframework.stereotype.Repository; import javax.inject.Inject; +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; import static fr.ippon.tatami.config.ColumnFamilyKeys.RSS_CF; /** @@ -27,39 +27,35 @@ public class CassandraRssUidRepository implements RssUidRepository { private final static String ROW_KEY = "rss_uid"; @Inject - private Keyspace keyspaceOperator; + private Session session; @Override public String generateRssUid(String login) { String key = RandomUtil.generateRegistrationKey(); - HColumn column = HFactory.createColumn(key, - login, StringSerializer.get(), StringSerializer.get()); - - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.insert(ROW_KEY, RSS_CF, column); + Statement statement = QueryBuilder.insertInto(ColumnFamilyKeys.RSS_CF) + .value(ROW_KEY, key) + .value("login",login); + session.execute(statement); return key; } @Override public String getLoginByRssUid(String rssUid) { - ColumnQuery query = HFactory.createStringColumnQuery(keyspaceOperator); - HColumn column = - query.setColumnFamily(RSS_CF) - .setKey(ROW_KEY) - .setName(rssUid) - .execute() - .get(); - - if (column != null) { - return column.getValue(); - } else { - return null; + Statement statement = QueryBuilder.select() + .all() + .from(ColumnFamilyKeys.RSS_CF) + .where(eq(ROW_KEY, rssUid)); + ResultSet results = session.execute(statement); + if (!results.isExhausted()) { + return results.one().getString("login"); } + return null; } @Override public void removeRssUid(String rssUid) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.delete(ROW_KEY, RSS_CF, rssUid, StringSerializer.get()); + Statement statement = QueryBuilder.delete().from(RSS_CF) + .where(eq(ROW_KEY, rssUid)); + session.execute(statement); } } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraSharesRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraSharesRepository.java index f695eacfa..034027f3a 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraSharesRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraSharesRepository.java @@ -1,13 +1,11 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.querybuilder.QueryBuilder; +import fr.ippon.tatami.config.ColumnFamilyKeys; import fr.ippon.tatami.repository.SharesRepository; -import me.prettyprint.cassandra.serializers.LongSerializer; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.beans.ColumnSlice; -import me.prettyprint.hector.api.beans.HColumn; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Repository; @@ -16,9 +14,11 @@ import java.util.Calendar; import java.util.Collection; import java.util.LinkedHashSet; +import java.util.UUID; +import java.util.stream.Collectors; +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; import static fr.ippon.tatami.config.ColumnFamilyKeys.SHARES_CF; -import static me.prettyprint.hector.api.factory.HFactory.createSliceQuery; /** * Cassandra implementation of the Shares repository. @@ -35,47 +35,41 @@ public class CassandraSharesRepository implements SharesRepository { @Inject - private Keyspace keyspaceOperator; + private Session session; @Override @CacheEvict(value = "shared-cache", key = "#statusId") public void newShareByLogin(String statusId, String sharedByLogin) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.insert(statusId, SHARES_CF, - HFactory.createColumn( - Calendar.getInstance().getTimeInMillis(), - sharedByLogin, - LongSerializer.get(), - StringSerializer.get())); + Statement statement = QueryBuilder.insertInto(SHARES_CF) + .value("status", UUID.fromString(statusId)) + .value("login",sharedByLogin); + session.execute(statement); } @Override @Cacheable("shared-cache") public Collection findLoginsWhoSharedAStatus(String statusId) { - ColumnSlice result = createSliceQuery(keyspaceOperator, - StringSerializer.get(), LongSerializer.get(), StringSerializer.get()) - .setColumnFamily(SHARES_CF) - .setKey(statusId) - .setRange(null, null, false, 100) // Limit to 100 logins - .execute() - .get(); - - Collection sharedByLogins = new LinkedHashSet(); - for (HColumn column : result.getColumns()) { - sharedByLogins.add(column.getValue()); - } - return sharedByLogins; + Statement statement = QueryBuilder.select() + .column("login") + .from(SHARES_CF) + .where(eq("status", UUID.fromString(statusId))) + .limit(100); + ResultSet results = session.execute(statement); + return results + .all() + .stream() + .map(e -> e.getString("login")) + .collect(Collectors.toList()); } @Override public boolean hasBeenShared(String statusId) { - int zeroOrOne = HFactory.createCountQuery(keyspaceOperator, StringSerializer.get(), LongSerializer.get()) - .setColumnFamily(SHARES_CF) - .setKey(statusId) - .setRange(null, null, 1) - .execute() - .get(); - - return zeroOrOne > 0; + Statement statement = QueryBuilder.select() + .column("login") + .from(SHARES_CF) + .where(eq("status", UUID.fromString(statusId))) + .limit(1); + ResultSet results = session.execute(statement); + return !results.isExhausted(); } } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraStatusAttachmentRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraStatusAttachmentRepository.java index beda13172..ff40dd3bd 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraStatusAttachmentRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraStatusAttachmentRepository.java @@ -1,16 +1,11 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.querybuilder.QueryBuilder; import fr.ippon.tatami.config.Constants; import fr.ippon.tatami.repository.StatusAttachmentRepository; -import me.prettyprint.cassandra.serializers.LongSerializer; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.cassandra.serializers.UUIDSerializer; -import me.prettyprint.cassandra.service.template.ColumnFamilyResult; -import me.prettyprint.cassandra.service.template.ColumnFamilyTemplate; -import me.prettyprint.cassandra.service.template.ThriftColumnFamilyTemplate; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; import org.springframework.stereotype.Repository; import javax.annotation.PostConstruct; @@ -19,7 +14,10 @@ import java.util.Calendar; import java.util.Collection; import java.util.UUID; +import java.util.stream.Collectors; +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; +import static fr.ippon.tatami.config.ColumnFamilyKeys.SHARES_CF; import static fr.ippon.tatami.config.ColumnFamilyKeys.STATUS_ATTACHMENT_CF; /** @@ -36,41 +34,39 @@ public class CassandraStatusAttachmentRepository implements StatusAttachmentRepository { - private ColumnFamilyTemplate attachmentsTemplate; @Inject - private Keyspace keyspaceOperator; - - @PostConstruct - public void init() { - attachmentsTemplate = new ThriftColumnFamilyTemplate(keyspaceOperator, - STATUS_ATTACHMENT_CF, - StringSerializer.get(), - UUIDSerializer.get()); - - attachmentsTemplate.setCount(Constants.CASSANDRA_MAX_COLUMNS); - } + private Session session; @Override public void addAttachmentId(String statusId, String attachmentId) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.insert(statusId, STATUS_ATTACHMENT_CF, HFactory.createColumn(UUID.fromString(attachmentId), - Calendar.getInstance().getTimeInMillis(), UUIDSerializer.get(), LongSerializer.get())); + Statement statement = QueryBuilder.insertInto(STATUS_ATTACHMENT_CF) + .value("statusId", UUID.fromString(statusId)) + .value("attachmentId",UUID.fromString(attachmentId)) + .value("created",System.currentTimeMillis()); + session.execute(statement); } @Override public void removeAttachmentId(String statusId, String attachmentId) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.delete(statusId, STATUS_ATTACHMENT_CF, UUID.fromString(attachmentId), UUIDSerializer.get()); + Statement statement = QueryBuilder.delete().from(STATUS_ATTACHMENT_CF) + .where(eq("statusId", UUID.fromString(statusId))) + .and(eq("attachmentId", UUID.fromString(attachmentId))); + session.execute(statement); } @Override public Collection findAttachmentIds(String statusId) { - ColumnFamilyResult result = attachmentsTemplate.queryColumns(statusId); - Collection attachmentIds = new ArrayList(); - for (UUID columnName : result.getColumnNames()) { - attachmentIds.add(columnName.toString()); - } - return attachmentIds; + Statement statement = QueryBuilder.select() + .column("attachmentId") + .from(STATUS_ATTACHMENT_CF) + .where(eq("statusId", UUID.fromString(statusId))) + .limit(Constants.CASSANDRA_MAX_ROWS); + ResultSet results = session.execute(statement); + return results + .all() + .stream() + .map(e -> e.getUUID("attachmentId").toString()) + .collect(Collectors.toList()); } } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraStatusRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraStatusRepository.java index 970df0afa..2dc34b042 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraStatusRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraStatusRepository.java @@ -1,25 +1,21 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.*; +import com.datastax.driver.core.querybuilder.Insert; +import com.datastax.driver.core.querybuilder.QueryBuilder; +import com.datastax.driver.core.utils.UUIDs; +import com.datastax.driver.mapping.Mapper; +import com.datastax.driver.mapping.MappingManager; import fr.ippon.tatami.domain.Attachment; import fr.ippon.tatami.domain.Group; import fr.ippon.tatami.repository.*; import fr.ippon.tatami.service.util.DomainUtil; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.cassandra.service.template.ColumnFamilyResult; -import me.prettyprint.cassandra.service.template.ColumnFamilyTemplate; -import me.prettyprint.cassandra.service.template.ColumnFamilyUpdater; -import me.prettyprint.cassandra.service.template.ThriftColumnFamilyTemplate; -import me.prettyprint.cassandra.utils.TimeUUIDUtils; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Repository; -import fr.ippon.tatami.config.ColumnFamilyKeys; import fr.ippon.tatami.domain.status.*; import javax.annotation.PostConstruct; @@ -65,15 +61,12 @@ public class CassandraStatusRepository implements StatusRepository { //Mention Friend private static final String FOLLOWER_LOGIN = "followerLogin"; + //Bean validation private static final ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); private static final Validator validator = factory.getValidator(); //Cassandra Template - ColumnFamilyTemplate template; - - @Inject - private Keyspace keyspaceOperator; @Inject private DiscussionRepository discussionRepository; @@ -87,15 +80,26 @@ public class CassandraStatusRepository implements StatusRepository { @Inject private AttachmentRepository attachmentRepository; + private PreparedStatement findOneByIdStmt; + + + private PreparedStatement deleteByIdStmt; + + + @Inject + Session session; + + private Mapper mapper; @PostConstruct public void init() { - template = - new ThriftColumnFamilyTemplate( - keyspaceOperator, - ColumnFamilyKeys.STATUS_CF, - StringSerializer.get(), - StringSerializer.get()); + mapper = new MappingManager(session).mapper(Status.class); + findOneByIdStmt = session.prepare( + "SELECT * " + + "FROM status " + + "WHERE statusId = :statusId"); + deleteByIdStmt = session.prepare("DELETE FROM status " + + "WHERE statusId = :statusId"); } @@ -112,66 +116,52 @@ public Status createStatus(String login, throws ConstraintViolationException { Status status = new Status(); + status.setStatusId(UUIDs.timeBased()); status.setLogin(login); status.setType(StatusType.STATUS); String username = DomainUtil.getUsernameFromLogin(login); status.setUsername(username); String domain = DomainUtil.getDomainFromLogin(login); status.setDomain(domain); + status.setStatusPrivate(statusPrivate); status.setContent(content); Set> constraintViolations = validator.validate(status); if (!constraintViolations.isEmpty()) { if (log.isDebugEnabled()) { - for (ConstraintViolation cv : constraintViolations) { - log.debug("Constraint violation: {}", cv.getMessage()); - } + constraintViolations.forEach(e -> log.debug("Constraint violation: {}", e.getMessage())); } - throw new ConstraintViolationException(new HashSet>(constraintViolations)); + throw new ConstraintViolationException(new HashSet<>(constraintViolations)); } - - ColumnFamilyUpdater updater = this.createBaseStatus(status); - - updater.setString(CONTENT, content); - - status.setStatusPrivate(statusPrivate); - updater.setBoolean(STATUS_PRIVATE, statusPrivate); - if (group != null) { - String groupId = group.getGroupId(); - status.setGroupId(groupId); - updater.setString(GROUP_ID, groupId); + UUID groupId = group.getGroupId(); + status.setGroupId(groupId.toString()); } if (attachmentIds != null && attachmentIds.size() > 0) { status.setHasAttachments(true); - updater.setBoolean(HAS_ATTACHMENTS, true); } if (discussionId != null) { status.setDiscussionId(discussionId); - updater.setString(DISCUSSION_ID, discussionId); } if (replyTo != null) { status.setReplyTo(replyTo); - updater.setString(REPLY_TO, replyTo); } if (replyToUsername != null) { status.setReplyToUsername(replyToUsername); - updater.setString(REPLY_TO_USERNAME, replyToUsername); } if(geoLocalization!=null) { status.setGeoLocalization(geoLocalization); - updater.setString(GEO_LOCALIZATION, geoLocalization); } + status.setStatusDate(new Date()); + BatchStatement batch = new BatchStatement(); + batch.add(mapper.saveQuery(status)); + session.execute(batch); - log.debug("Persisting Status : {}", status); - - - template.update(updater); return status; } @@ -184,17 +174,38 @@ public Share createShare(String login, String originalStatusId) { share.setUsername(username); String domain = DomainUtil.getDomainFromLogin(login); share.setDomain(domain); - ColumnFamilyUpdater updater = this.createBaseStatus(share); - updater.setString(ORIGINAL_STATUS_ID, originalStatusId); + Insert inserter = this.createBaseStatus(share); share.setOriginalStatusId(originalStatusId); - + inserter = inserter.value("originalStatusId",UUID.fromString(originalStatusId)); log.debug("Persisting Share : {}", share); - - template.update(updater); + session.execute(inserter); return share; } + private Insert createBaseStatus(AbstractStatus abstractStatus) { + + abstractStatus.setStatusId(UUIDs.timeBased()); + abstractStatus.setStatusDate(Calendar.getInstance().getTime()); + if (abstractStatus.getLogin() == null) { + throw new IllegalStateException("Login cannot be null for status: " + abstractStatus); + } + if (abstractStatus.getUsername() == null) { + throw new IllegalStateException("Username cannot be null for status: " + abstractStatus); + } + if (abstractStatus.getDomain() == null) { + throw new IllegalStateException("Domain cannot be null for status: " + abstractStatus); + } + + return QueryBuilder.insertInto("status") + .value("statusId",abstractStatus.getStatusId()) + .value("statusDate",abstractStatus.getStatusDate()) + .value("login", abstractStatus.getLogin()) + .value("username",abstractStatus.getUsername()) + .value("domain",abstractStatus.getDomain()) + .value("type",abstractStatus.getType().name()); + } + @Override public Announcement createAnnouncement(String login, String originalStatusId) { Announcement announcement = new Announcement(); @@ -204,14 +215,12 @@ public Announcement createAnnouncement(String login, String originalStatusId) { announcement.setUsername(username); String domain = DomainUtil.getDomainFromLogin(login); announcement.setDomain(domain); - ColumnFamilyUpdater updater = this.createBaseStatus(announcement); - updater.setString(ORIGINAL_STATUS_ID, originalStatusId); + Insert inserter = this.createBaseStatus(announcement); announcement.setOriginalStatusId(originalStatusId); - + inserter = inserter.value("originalStatusId",UUID.fromString(originalStatusId)); log.debug("Persisting Announcement : {}", announcement); - - template.update(updater); + session.execute(inserter); return announcement; } @@ -224,14 +233,12 @@ public MentionFriend createMentionFriend(String login, String followerLogin) { mentionFriend.setUsername(username); String domain = DomainUtil.getDomainFromLogin(login); mentionFriend.setDomain(domain); - ColumnFamilyUpdater updater = this.createBaseStatus(mentionFriend); - - updater.setString(FOLLOWER_LOGIN, followerLogin); - - log.debug("Persisting MentionFriend : {}", mentionFriend); - - template.update(updater); + Insert inserter = this.createBaseStatus(mentionFriend); + mentionFriend.setFollowerLogin(followerLogin); + inserter = inserter.value("followerLogin",followerLogin); + log.debug("Persisting Announcement : {}", mentionFriend); + session.execute(inserter); return mentionFriend; } @@ -244,53 +251,16 @@ public MentionShare createMentionShare(String login, String originalStatusId) { mentionShare.setUsername(username); String domain = DomainUtil.getDomainFromLogin(login); mentionShare.setDomain(domain); - ColumnFamilyUpdater updater = this.createBaseStatus(mentionShare); - updater.setString(ORIGINAL_STATUS_ID, originalStatusId); + Insert inserter = this.createBaseStatus(mentionShare); mentionShare.setOriginalStatusId(originalStatusId); + inserter = inserter.value("originalStatusId",UUID.fromString(originalStatusId)); + log.debug("Persisting Announcement : {}", mentionShare); + session.execute(inserter); - - log.debug("Persisting MentionShare : {}", mentionShare); - - template.update(updater); return mentionShare; } - private ColumnFamilyUpdater createBaseStatus(AbstractStatus abstractStatus) { - // Generate statusId and statusDate for all statuses - String statusId = TimeUUIDUtils.getUniqueTimeUUIDinMillis().toString(); - abstractStatus.setStatusId(statusId); - ColumnFamilyUpdater updater = template.createUpdater(statusId); - - Date statusDate = Calendar.getInstance().getTime(); - updater.setDate(STATUS_DATE, statusDate); - abstractStatus.setStatusDate(statusDate); - - // Persist common data : login, username, domain, type - String login = abstractStatus.getLogin(); - if (login == null) { - throw new IllegalStateException("Login cannot be null for status: " + abstractStatus); - } - updater.setString(LOGIN, login); - - String username = abstractStatus.getUsername(); - if (username == null) { - throw new IllegalStateException("Username cannot be null for status: " + abstractStatus); - } - updater.setString(USERNAME, username); - - String domain = abstractStatus.getDomain(); - if (domain == null) { - throw new IllegalStateException("Domain cannot be null for status: " + abstractStatus); - } - updater.setString(DOMAIN, domain); - - updater.setString(TYPE, abstractStatus.getType().name()); - - return updater; - } - - @Override @Cacheable("status-cache") public AbstractStatus findStatusById(String statusId) { @@ -300,69 +270,99 @@ public AbstractStatus findStatusById(String statusId) { if (log.isTraceEnabled()) { log.trace("Finding status : " + statusId); } - - ColumnFamilyResult result = template.queryColumns(statusId); - - if (result.hasResults() == false) { - return null; // No status was found + BoundStatement stmt = findOneByIdStmt.bind(); + stmt.setUUID("statusId", UUID.fromString(statusId)); + ResultSet rs = session.execute(stmt); + if (rs.isExhausted()) { + return null; } + Row row = rs.one(); AbstractStatus status = null; - String type = result.getString(TYPE); + String type = row.getString(TYPE); if (type == null || type.equals(StatusType.STATUS.name())) { - status = findStatus(result, statusId); + status = findStatus(row, statusId); } else if (type.equals(StatusType.SHARE.name())) { - status = findShare(result); + status = findShare(row); } else if (type.equals(StatusType.ANNOUNCEMENT.name())) { - status = findAnnouncement(result); + status = findAnnouncement(row); } else if (type.equals(StatusType.MENTION_FRIEND.name())) { - status = findMentionFriend(result); + status = findMentionFriend(row); } else if (type.equals(StatusType.MENTION_SHARE.name())) { - status = findMentionShare(result); + status = findMentionShare(row); } else { throw new IllegalStateException("Status has an unknown type: " + type); } if (status == null) { // Status was not found, or was removed return null; } - status.setStatusId(statusId); - status.setLogin(result.getString(LOGIN)); - status.setUsername(result.getString(USERNAME)); + status.setStatusId(UUID.fromString(statusId)); + status.setLogin(row.getString(LOGIN)); + status.setUsername(row.getString(USERNAME)); - String domain = result.getString(DOMAIN); + String domain = row.getString(DOMAIN); if (domain != null) { status.setDomain(domain); } else { throw new IllegalStateException("Status cannot have a null domain: " + status); } - status.setStatusDate(result.getDate(STATUS_DATE)); - Boolean removed = result.getBoolean(REMOVED); + status.setStatusDate(row.getDate(STATUS_DATE)); + Boolean removed = row.getBool(REMOVED); if (removed != null) { status.setRemoved(removed); } return status; + + } + + private AbstractStatus findMentionShare(Row result) { + MentionShare mentionShare = new MentionShare(); + mentionShare.setType(StatusType.MENTION_SHARE); + mentionShare.setOriginalStatusId(result.getUUID(ORIGINAL_STATUS_ID).toString()); + return mentionShare; + } + + private AbstractStatus findMentionFriend(Row result) { + MentionFriend mentionFriend = new MentionFriend(); + mentionFriend.setType(StatusType.MENTION_FRIEND); + mentionFriend.setFollowerLogin(result.getString(FOLLOWER_LOGIN)); + return mentionFriend; + } + + private AbstractStatus findAnnouncement(Row result) { + Announcement announcement = new Announcement(); + announcement.setType(StatusType.ANNOUNCEMENT); + announcement.setOriginalStatusId(result.getUUID(ORIGINAL_STATUS_ID).toString()); + return announcement; + } + + private AbstractStatus findShare(Row result) { + Share share = new Share(); + share.setType(StatusType.SHARE); + share.setOriginalStatusId(result.getUUID(ORIGINAL_STATUS_ID).toString()); + return share; } - private Status findStatus(ColumnFamilyResult result, String statusId) { + private AbstractStatus findStatus(Row result, String statusId) { Status status = new Status(); - status.setStatusId(statusId); + status.setStatusId(UUID.fromString(statusId)); status.setType(StatusType.STATUS); status.setContent(result.getString(CONTENT)); - status.setStatusPrivate(result.getBoolean(STATUS_PRIVATE)); + status.setStatusPrivate(result.getBool(STATUS_PRIVATE)); status.setGroupId(result.getString(GROUP_ID)); - status.setHasAttachments(result.getBoolean(HAS_ATTACHMENTS)); + status.setHasAttachments(result.getBool(HAS_ATTACHMENTS)); status.setDiscussionId(result.getString(DISCUSSION_ID)); status.setReplyTo(result.getString(REPLY_TO)); status.setReplyToUsername(result.getString(REPLY_TO_USERNAME)); status.setGeoLocalization(result.getString(GEO_LOCALIZATION)); - status.setRemoved(result.getBoolean(REMOVED)); - if (status.getRemoved() == Boolean.TRUE) { + status.setRemoved(result.getBool(REMOVED)); + if (status.isRemoved()) { return null; } status.setDetailsAvailable(computeDetailsAvailable(status)); if (status.getHasAttachments() != null && status.getHasAttachments()) { Collection attachmentIds = statusAttachmentRepository.findAttachmentIds(statusId); - Collection attachments = new ArrayList(); + Collection attachments = new ArrayList<>(); for (String attachmentId : attachmentIds) { Attachment attachment = attachmentRepository.findAttachmentMetadataById(attachmentId); if (attachment != null) { @@ -379,42 +379,13 @@ private Status findStatus(ColumnFamilyResult result, String stat return status; } - private Share findShare(ColumnFamilyResult result) { - Share share = new Share(); - share.setType(StatusType.SHARE); - share.setOriginalStatusId(result.getString(ORIGINAL_STATUS_ID)); - return share; - } - - private Announcement findAnnouncement(ColumnFamilyResult result) { - Announcement announcement = new Announcement(); - announcement.setType(StatusType.ANNOUNCEMENT); - announcement.setOriginalStatusId(result.getString(ORIGINAL_STATUS_ID)); - return announcement; - } - - private MentionFriend findMentionFriend(ColumnFamilyResult result) { - MentionFriend mentionFriend = new MentionFriend(); - mentionFriend.setType(StatusType.MENTION_FRIEND); - mentionFriend.setFollowerLogin(result.getString(FOLLOWER_LOGIN)); - return mentionFriend; - } - - private MentionShare findMentionShare(ColumnFamilyResult result) { - MentionShare mentionShare = new MentionShare(); - mentionShare.setType(StatusType.MENTION_SHARE); - mentionShare.setOriginalStatusId(result.getString(ORIGINAL_STATUS_ID)); - return mentionShare; - } - @Override @CacheEvict(value = "status-cache", key = "#status.statusId") public void removeStatus(AbstractStatus status) { log.debug("Removing Status : {}", status); - - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.addDeletion(status.getStatusId(), ColumnFamilyKeys.STATUS_CF); - mutator.execute(); + BatchStatement batch = new BatchStatement(); + batch.add(deleteByIdStmt.bind().setUUID("statusId", status.getStatusId())); + session.execute(batch); } private boolean computeDetailsAvailable(Status status) { @@ -422,10 +393,10 @@ private boolean computeDetailsAvailable(Status status) { if (status.getType().equals(StatusType.STATUS)) { if (StringUtils.isNotBlank(status.getReplyTo())) { detailsAvailable = true; - } else if (discussionRepository.hasReply(status.getStatusId())) { - detailsAvailable = true; - } else if (sharesRepository.hasBeenShared(status.getStatusId())) { - detailsAvailable = true; +// } else if (discussionRepository.hasReply(status.getStatusId())) { +// detailsAvailable = true; +// } else if (sharesRepository.hasBeenShared(status.getStatusId())) { +// detailsAvailable = true; } } return detailsAvailable; diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraTagCounterRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraTagCounterRepository.java index 96b2ec089..8450f1058 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraTagCounterRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraTagCounterRepository.java @@ -1,16 +1,18 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.querybuilder.QueryBuilder; +import fr.ippon.tatami.config.ColumnFamilyKeys; import fr.ippon.tatami.repository.TagCounterRepository; -import me.prettyprint.cassandra.model.thrift.ThriftCounterColumnQuery; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; -import me.prettyprint.hector.api.query.CounterQuery; import org.springframework.stereotype.Repository; import javax.inject.Inject; +import static com.datastax.driver.core.querybuilder.QueryBuilder.decr; +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; +import static com.datastax.driver.core.querybuilder.QueryBuilder.incr; import static fr.ippon.tatami.config.ColumnFamilyKeys.TAG_COUNTER_CF; /** @@ -29,36 +31,43 @@ public class CassandraTagCounterRepository implements TagCounterRepository { private static final String TAG_COUNTER = "TAG_COUNTER"; @Inject - private Keyspace keyspaceOperator; + private Session session; @Override public long getTagCounter(String domain, String tag) { - CounterQuery counter = - new ThriftCounterColumnQuery(keyspaceOperator, - StringSerializer.get(), - StringSerializer.get()); - - counter.setColumnFamily(TAG_COUNTER_CF).setKey(getKey(domain, tag)).setName(TAG_COUNTER); - return counter.execute().get().getValue(); + Statement statement = QueryBuilder.select() + .column(TAG_COUNTER) + .from(ColumnFamilyKeys.TAG_COUNTER_CF) + .where(eq("key", getKey(domain,tag))); + ResultSet results = session.execute(statement); + if (!results.isExhausted()) { + return results.one().getLong(TAG_COUNTER); + } else { + return 0; + } } @Override public void incrementTagCounter(String domain, String tag) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.incrementCounter(getKey(domain, tag), TAG_COUNTER_CF, TAG_COUNTER, 1); + Statement statement = QueryBuilder.update(ColumnFamilyKeys.TAG_COUNTER_CF) + .with(incr(TAG_COUNTER,1)) + .where(eq("key",getKey(domain,tag))); + session.execute(statement); } @Override public void decrementTagCounter(String domain, String tag) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.decrementCounter(getKey(domain, tag), TAG_COUNTER_CF, TAG_COUNTER, 1); + Statement statement = QueryBuilder.update(ColumnFamilyKeys.TAG_COUNTER_CF) + .with(decr(TAG_COUNTER,1)) + .where(eq("key",getKey(domain,tag))); + session.execute(statement); } @Override public void deleteTagCounter(String domain, String tag) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.addCounterDeletion(getKey(domain, tag), TAG_COUNTER_CF, TAG_COUNTER, StringSerializer.get()); - mutator.execute(); + Statement statement = QueryBuilder.delete().from(ColumnFamilyKeys.TAG_COUNTER_CF) + .where(eq("key", getKey(domain,tag))); + session.execute(statement); } /** diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraTagFollowerRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraTagFollowerRepository.java index 12abe4495..ab946ae8d 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraTagFollowerRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraTagFollowerRepository.java @@ -1,10 +1,18 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.querybuilder.QueryBuilder; import fr.ippon.tatami.repository.TagFollowerRepository; import org.springframework.stereotype.Repository; import fr.ippon.tatami.config.ColumnFamilyKeys; +import javax.inject.Inject; import java.util.Collection; +import java.util.stream.Collectors; + +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; /** * Cassandra implementation of the Follower repository. @@ -21,6 +29,9 @@ public class CassandraTagFollowerRepository extends AbstractCassandraFollowerRepository implements TagFollowerRepository { + @Inject + private Session session; + @Override public void addFollower(String domain, String tag, String login) { super.addFollower(getKey(domain, tag), login); @@ -33,7 +44,16 @@ public void removeFollower(String domain, String tag, String login) { @Override public Collection findFollowers(String domain, String tag) { - return super.findFollowers(getKey(domain, tag)); + Statement statement = QueryBuilder.select() + .column("login") + .from("tagFollowers") + .where(eq("key", getKey(domain, tag))); + ResultSet results = session.execute(statement); + return results + .all() + .stream() + .map(e -> e.getString("login")) + .collect(Collectors.toList()); } @Override diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraTaglineRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraTaglineRepository.java index b08e7ceb2..d741e286c 100755 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraTaglineRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraTaglineRepository.java @@ -1,20 +1,17 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.PreparedStatement; +import fr.ippon.tatami.config.ColumnFamilyKeys; import fr.ippon.tatami.domain.status.Status; import fr.ippon.tatami.repository.TaglineRepository; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.cassandra.serializers.UUIDSerializer; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; import org.springframework.stereotype.Repository; -import javax.inject.Inject; +import javax.annotation.PostConstruct; import java.util.Collection; import java.util.List; -import java.util.UUID; -import static fr.ippon.tatami.config.ColumnFamilyKeys.TAGLINE_CF; +import static fr.ippon.tatami.config.ColumnFamilyKeys.DOMAINLINE; +import static fr.ippon.tatami.config.ColumnFamilyKeys.TAGLINE; /** * Cassandra implementation of the Tag line repository. @@ -29,32 +26,37 @@ @Repository public class CassandraTaglineRepository extends AbstractCassandraLineRepository implements TaglineRepository { - @Inject - private Keyspace keyspaceOperator; + private PreparedStatement findByLoginStmt; + + private PreparedStatement deleteByIdStmt; + + + @PostConstruct + public void init() { + findByLoginStmt = session.prepare( + "SELECT * " + + "FROM " + TAGLINE+ + " WHERE key = :key"); + + deleteByIdStmt = session.prepare("DELETE FROM " + TAGLINE + + " WHERE key = :key " + + "AND status = :statusId"); + + } @Override public void addStatusToTagline(String tag, Status status) { - addStatus(getKey(status.getDomain(), tag), TAGLINE_CF, status.getStatusId()); + addStatus(getKey(status.getDomain(), tag), TAGLINE, status.getStatusId().toString()); } @Override public void removeStatusesFromTagline(String tag, String domain, Collection statusIdsToDelete) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - for (String statusId : statusIdsToDelete) { - mutator.addDeletion( - getKey(domain, tag), - TAGLINE_CF, - UUID.fromString(statusId), - UUIDSerializer.get()); - - } - mutator.execute(); - + removeStatuses(getKey(domain, tag), ColumnFamilyKeys.TAGLINE, statusIdsToDelete); } @Override public List getTagline(String domain, String tag, int size, String start, String finish) { - return getLineFromCF(TAGLINE_CF, getKey(domain, tag), size, start, finish); + return getLineFromTable(TAGLINE, getKey(domain, tag), size, start, finish); } /** @@ -63,4 +65,9 @@ public List getTagline(String domain, String tag, int size, String start private String getKey(String domain, String tag) { return tag.toLowerCase() + "-" + domain; } + + @Override + public PreparedStatement getDeleteByIdStmt() { + return deleteByIdStmt; + } } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraTimelineRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraTimelineRepository.java index adc0b2846..dbf72de3d 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraTimelineRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraTimelineRepository.java @@ -1,20 +1,26 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.*; +import com.datastax.driver.core.querybuilder.QueryBuilder; +import com.datastax.driver.core.querybuilder.Select; +import com.datastax.driver.core.querybuilder.Select.Where; +import com.datastax.driver.core.utils.UUIDs; +import com.datastax.driver.mapping.Mapper; +import com.datastax.driver.mapping.MappingManager; import fr.ippon.tatami.domain.status.Announcement; import fr.ippon.tatami.domain.status.Share; +import fr.ippon.tatami.domain.status.Status; import fr.ippon.tatami.repository.TimelineRepository; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.cassandra.serializers.UUIDSerializer; -import me.prettyprint.hector.api.beans.HColumn; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; -import me.prettyprint.hector.api.query.QueryResult; import org.springframework.stereotype.Repository; +import javax.annotation.PostConstruct; +import javax.inject.Inject; import java.util.Collection; import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; +import static com.datastax.driver.core.querybuilder.QueryBuilder.*; import static fr.ippon.tatami.config.ColumnFamilyKeys.TIMELINE_CF; import static fr.ippon.tatami.config.ColumnFamilyKeys.TIMELINE_SHARES_CF; @@ -31,22 +37,44 @@ @Repository public class CassandraTimelineRepository extends AbstractCassandraLineRepository implements TimelineRepository { + @Inject + Session session; + + private Mapper mapper; + + private PreparedStatement findByLoginStmt; + + private PreparedStatement deleteByIdStmt; + + + @PostConstruct + public void init() { + mapper = new MappingManager(session).mapper(Status.class); + findByLoginStmt = session.prepare( + "SELECT * " + + "FROM timeline " + + "WHERE key = :key"); + + deleteByIdStmt = session.prepare("DELETE FROM timeline " + + "WHERE key = :key " + + "AND status = :statusId"); + + } + + @Override public boolean isStatusInTimeline(String login, String statusId) { - QueryResult> isStatusAlreadyinTimeline = - findByLoginAndStatusId(TIMELINE_CF, login, UUID.fromString(statusId)); - - return isStatusAlreadyinTimeline.get() != null; + return findByLoginAndStatusId(TIMELINE_CF,login,UUID.fromString(statusId)); } @Override public void addStatusToTimeline(String login, String statusId) { - addStatus(login, TIMELINE_CF, statusId); + addStatus(login,TIMELINE_CF,statusId); } @Override public void removeStatusesFromTimeline(String login, Collection statusIdsToDelete) { - removeStatuses(login, TIMELINE_CF, statusIdsToDelete); + removeStatuses(login,TIMELINE_CF,statusIdsToDelete); } @Override @@ -56,24 +84,29 @@ public void shareStatusToTimeline(String sharedByLogin, String timelineLogin, Sh @Override public void announceStatusToTimeline(String announcedByLogin, List logins, Announcement announcement) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); + PreparedStatement insertAnnouncementPreparedStatement = session.prepare( + "INSERT INTO timeline (key, status) VALUES (?, ?);"); - for (String login : logins) { - mutator.addInsertion(login, TIMELINE_CF, HFactory.createColumn(UUID.fromString(announcement.getStatusId()), - "", UUIDSerializer.get(), StringSerializer.get())); - } - mutator.execute(); + BatchStatement batch = new BatchStatement(); + logins.forEach(e -> batch.add(insertAnnouncementPreparedStatement.bind(e, UUIDs.timeBased()))); + session.executeAsync(batch); } @Override public List getTimeline(String login, int size, String start, String finish) { - return getLineFromCF(TIMELINE_CF, login, size, start, finish); + return getLineFromTable(TIMELINE_CF,login,size,start,finish); } @Override public void deleteTimeline(String login) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.addDeletion(login, TIMELINE_CF); - mutator.execute(); + Statement statement = QueryBuilder.delete() + .from(TIMELINE_CF) + .where(eq("key",login)); + session.execute(statement); + } + + @Override + public PreparedStatement getDeleteByIdStmt() { + return deleteByIdStmt; } } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraTrendRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraTrendRepository.java index f98038126..2320eb04c 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraTrendRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraTrendRepository.java @@ -1,14 +1,11 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.querybuilder.QueryBuilder; +import com.datastax.driver.core.utils.UUIDs; import fr.ippon.tatami.repository.TrendRepository; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.cassandra.serializers.UUIDSerializer; -import me.prettyprint.cassandra.utils.TimeUUIDUtils; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.beans.ColumnSlice; -import me.prettyprint.hector.api.beans.HColumn; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Repository; @@ -16,9 +13,12 @@ import javax.inject.Inject; import java.util.*; +import java.util.stream.Collectors; +import static com.datastax.driver.core.querybuilder.QueryBuilder.desc; +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; +import static com.datastax.driver.core.querybuilder.QueryBuilder.ttl; import static fr.ippon.tatami.config.ColumnFamilyKeys.TRENDS_CF; -import static me.prettyprint.hector.api.factory.HFactory.createSliceQuery; /** * Cassandra implementation of the Trends repository. @@ -38,23 +38,18 @@ public class CassandraTrendRepository implements TrendRepository { private final static int TRENDS_NUMBER_OF_TAGS = 100; @Inject - private Keyspace keyspaceOperator; + Session session; + @Override @CacheEvict(value = "domain-tags-cache", key = "#domain") public void addTag(String domain, String tag) { - HColumn column = - HFactory.createColumn( - TimeUUIDUtils.getUniqueTimeUUIDinMillis(), - tag, - COLUMN_TTL, - UUIDSerializer.get(), - StringSerializer.get()); - - Mutator mutator = - HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - - mutator.insert(domain, TRENDS_CF, column); + Statement statement = QueryBuilder.insertInto("trends") + .value("domain", domain) + .value("id", UUIDs.timeBased()) + .value("tag", tag) + .using(ttl(COLUMN_TTL)); + session.execute(statement); } @Override @@ -64,42 +59,37 @@ public List getRecentTags(String domain) { @Override public List getRecentTags(String domain, int maxNumber) { - ColumnSlice query = createSliceQuery(keyspaceOperator, - StringSerializer.get(), UUIDSerializer.get(), StringSerializer.get()) - .setColumnFamily(TRENDS_CF) - .setKey(domain) - .setRange(null, null, true, maxNumber) - .execute() - .get(); - - List result = new ArrayList(); - String tag; - for (HColumn column : query.getColumns()) { - tag = column.getValue(); - result.add(tag); - } - return result; + Statement statement = QueryBuilder.select() + .column("tag") + .from("trends") + .where(eq("domain", domain)) + .orderBy(desc("id")) + .limit(maxNumber); + + ResultSet results = session.execute(statement); + return results + .all() + .stream() + .map(e -> e.getString("tag")) + .collect(Collectors.toList()); } @Cacheable(value = "domain-tags-cache", key = "#domain") public Collection getDomainTags(String domain) { - Assert.hasLength(domain); - - final ColumnSlice query = createSliceQuery(keyspaceOperator, - StringSerializer.get(), UUIDSerializer.get(), StringSerializer.get()) - .setColumnFamily(TRENDS_CF) - .setKey(domain) - .setRange(null, null, true, TRENDS_NUMBER_OF_TAGS) - .execute() - .get(); - - final Map result = new HashMap(); - String tag; - for (HColumn column : query.getColumns()) { - tag = column.getValue(); - result.put(tag.toLowerCase(), tag); - } - return result.values(); + Statement statement = QueryBuilder.select() + .column("tag") + .from("trends") + .where(eq("domain", domain)) + .orderBy(desc("id")) + .limit(TRENDS_NUMBER_OF_TAGS); + + ResultSet results = session.execute(statement); + return results + .all() + .stream() + .map(e -> e.getString("tag")) + .collect(Collectors.toList()); + } } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraUserAttachmentRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraUserAttachmentRepository.java index 0ce0610d3..7ebcacbe4 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraUserAttachmentRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraUserAttachmentRepository.java @@ -1,26 +1,24 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.querybuilder.QueryBuilder; +import com.datastax.driver.core.querybuilder.Select; +import com.datastax.driver.core.utils.UUIDs; import fr.ippon.tatami.config.Constants; +import fr.ippon.tatami.config.GroupRoles; import fr.ippon.tatami.repository.UserAttachmentRepository; -import me.prettyprint.cassandra.serializers.LongSerializer; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.cassandra.serializers.UUIDSerializer; -import me.prettyprint.cassandra.service.template.ColumnFamilyResult; -import me.prettyprint.cassandra.service.template.ColumnFamilyTemplate; -import me.prettyprint.cassandra.service.template.ThriftColumnFamilyTemplate; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.beans.ColumnSlice; -import me.prettyprint.hector.api.beans.HColumn; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; import org.springframework.stereotype.Repository; import fr.ippon.tatami.config.ColumnFamilyKeys; import javax.annotation.PostConstruct; import javax.inject.Inject; import java.util.*; +import java.util.stream.Collectors; + +import static com.datastax.driver.core.querybuilder.QueryBuilder.*; -import static me.prettyprint.hector.api.factory.HFactory.createSliceQuery; /** * Cassandra implementation of the UserAttachment repository. @@ -36,75 +34,59 @@ public class CassandraUserAttachmentRepository implements UserAttachmentRepository { - private ColumnFamilyTemplate attachmentsTemplate; - @Inject - private Keyspace keyspaceOperator; - - @PostConstruct - public void init() { - attachmentsTemplate = new ThriftColumnFamilyTemplate(keyspaceOperator, - ColumnFamilyKeys.USER_ATTACHMENT_CF, - StringSerializer.get(), - UUIDSerializer.get()); - - attachmentsTemplate.setCount(Constants.CASSANDRA_MAX_COLUMNS); - } + private Session session; @Override public void addAttachmentId(String login, String attachmentId) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.insert(login, ColumnFamilyKeys.USER_ATTACHMENT_CF, HFactory.createColumn(UUID.fromString(attachmentId), - Calendar.getInstance().getTimeInMillis(), UUIDSerializer.get(), LongSerializer.get())); + Statement statement = QueryBuilder.insertInto(ColumnFamilyKeys.USER_ATTACHMENT_CF) + .value("login", login) + .value("attachmentId", UUID.fromString(attachmentId)); + session.execute(statement); } @Override public void removeAttachmentId(String login, String attachmentId) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.delete(login, ColumnFamilyKeys.USER_ATTACHMENT_CF, UUID.fromString(attachmentId), UUIDSerializer.get()); + Statement statement = QueryBuilder.delete().from(ColumnFamilyKeys.USER_ATTACHMENT_CF) + .where(eq("login", login)) + .and(eq("attachmentId",UUID.fromString(attachmentId))); + session.execute(statement); } @Override public Collection findAttachmentIds(String login, int pagination, String finish) { - List> result; - if (finish != null) { - ColumnSlice query = createSliceQuery(keyspaceOperator, - StringSerializer.get(), UUIDSerializer.get(), LongSerializer.get()) - .setColumnFamily(ColumnFamilyKeys.USER_ATTACHMENT_CF) - .setKey(login) - .setRange(UUID.fromString(finish), null, true, pagination) - .execute() - .get(); - - result = query.getColumns(); - } else { - ColumnSlice query = createSliceQuery(keyspaceOperator, - StringSerializer.get(), UUIDSerializer.get(), LongSerializer.get()) - .setColumnFamily(ColumnFamilyKeys.USER_ATTACHMENT_CF) - .setKey(login) - .setRange(null, null, true, pagination) - .execute() - .get(); - - result = query.getColumns(); + Select.Where where = QueryBuilder.select() + .column("attachmentId") + .from(ColumnFamilyKeys.USER_ATTACHMENT_CF) + .where(eq("login", login)); + if(finish != null) { + where.and(gt("attachmentId", UUID.fromString(finish))); } + where.orderBy(desc("attachmentId")).limit(pagination); - Collection attachmentIds = new ArrayList(); - int index = 0; - for (HColumn column : result) { - attachmentIds.add(column.getName().toString()); - index++; - } - return attachmentIds; + Statement statement = where; + ResultSet results = session.execute(statement); + return results + .all() + .stream() + .map(e -> e.getUUID("attachmentId").toString()) + .collect(Collectors.toList()); } @Override public Collection findAttachmentIds(String login) { - ColumnFamilyResult result = attachmentsTemplate.queryColumns(login); - Collection attachmentIds = new ArrayList(); - for (UUID columnName : result.getColumnNames()) { - attachmentIds.add(columnName.toString()); - } - return attachmentIds; + Statement statement = QueryBuilder.select() + .column("attachmentId") + .from(ColumnFamilyKeys.USER_ATTACHMENT_CF) + .where(eq("login", login)) + .limit(Constants.CASSANDRA_MAX_ROWS); + + ResultSet results = session.execute(statement); + return results + .all() + .stream() + .map(e -> e.getUUID("attachmentId").toString()) + .collect(Collectors.toList()); + } } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraUserGroupRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraUserGroupRepository.java index c9e12be98..0ec5bf020 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraUserGroupRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraUserGroupRepository.java @@ -1,22 +1,24 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.querybuilder.QueryBuilder; +import com.datastax.driver.core.querybuilder.Select; +import com.datastax.driver.core.utils.UUIDs; import fr.ippon.tatami.config.GroupRoles; import fr.ippon.tatami.repository.UserGroupRepository; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.beans.ColumnSlice; -import me.prettyprint.hector.api.beans.HColumn; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; import org.springframework.stereotype.Repository; import javax.inject.Inject; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; -import static fr.ippon.tatami.config.ColumnFamilyKeys.USER_GROUPS_CF; -import static me.prettyprint.hector.api.factory.HFactory.createSliceQuery; +import static com.datastax.driver.core.querybuilder.QueryBuilder.*; +import static com.datastax.driver.core.querybuilder.QueryBuilder.desc; /** * Cassandra implementation of the User groups repository. @@ -32,61 +34,61 @@ public class CassandraUserGroupRepository implements UserGroupRepository { @Inject - private Keyspace keyspaceOperator; + Session session; + @Override - public void addGroupAsMember(String login, String groupId) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.insert(login, USER_GROUPS_CF, HFactory.createColumn(groupId, - GroupRoles.MEMBER, StringSerializer.get(), StringSerializer.get())); + public void addGroupAsMember(String login, UUID groupId) { + Statement statement = QueryBuilder.insertInto("userGroup") + .value("login", login) + .value("groupId", groupId) + .value("role", GroupRoles.MEMBER); + session.execute(statement); } @Override - public void addGroupAsAdmin(String login, String groupId) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.insert(login, USER_GROUPS_CF, HFactory.createColumn(groupId, - GroupRoles.ADMIN, StringSerializer.get(), StringSerializer.get())); + public void addGroupAsAdmin(String login, UUID groupId) { + Statement statement = QueryBuilder.insertInto("userGroup") + .value("login", login) + .value("groupId", groupId) + .value("role", GroupRoles.ADMIN); + session.execute(statement); } @Override - public void removeGroup(String login, String groupId) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.delete(login, USER_GROUPS_CF, groupId, StringSerializer.get()); + public void removeGroup(String login, UUID groupId) { + Statement statement = QueryBuilder.delete().from("userGroup") + .where(eq("login", login)) + .and(eq("groupId", groupId)); + session.execute(statement); } @Override - public List findGroups(String login) { - List groups = new ArrayList(); - ColumnSlice result = createSliceQuery(keyspaceOperator, - StringSerializer.get(), StringSerializer.get(), StringSerializer.get()) - .setColumnFamily(USER_GROUPS_CF) - .setKey(login) - .setRange(null, null, false, Integer.MAX_VALUE) - .execute() - .get(); - - for (HColumn column : result.getColumns()) { - groups.add(column.getName()); - } - return groups; + public List findGroups(String login) { + Statement statement = QueryBuilder.select() + .column("groupId") + .from("userGroup") + .where(eq("login", login)); + ResultSet results = session.execute(statement); + return results + .all() + .stream() + .map(e -> e.getUUID("groupId")) + .collect(Collectors.toList()); } @Override - public Collection findGroupsAsAdmin(String login) { - List groups = new ArrayList(); - ColumnSlice result = createSliceQuery(keyspaceOperator, - StringSerializer.get(), StringSerializer.get(), StringSerializer.get()) - .setColumnFamily(USER_GROUPS_CF) - .setKey(login) - .setRange(null, null, false, Integer.MAX_VALUE) - .execute() - .get(); - - for (HColumn column : result.getColumns()) { - if (column.getValue() != null && column.getValue().equals(GroupRoles.ADMIN)) { - groups.add(column.getName()); - } - } - return groups; + public Collection findGroupsAsAdmin(String login) { + Statement statement = QueryBuilder.select() + .all() + .from("userGroup") + .where(eq("login", login)); + ResultSet results = session.execute(statement); + return results + .all() + .stream() + .filter(e -> e.getString("role").equals(GroupRoles.ADMIN)) + .map(e -> e.getUUID("groupId")) + .collect(Collectors.toList()); } } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraUserRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraUserRepository.java index 78974c6c9..e03052222 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraUserRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraUserRepository.java @@ -1,24 +1,25 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.*; +import com.datastax.driver.core.querybuilder.QueryBuilder; +import com.datastax.driver.mapping.Mapper; +import com.datastax.driver.mapping.MappingManager; import fr.ippon.tatami.domain.User; import fr.ippon.tatami.domain.validation.ContraintsUserCreation; import fr.ippon.tatami.repository.CounterRepository; import fr.ippon.tatami.repository.UserRepository; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; -import me.prettyprint.hom.EntityManagerImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Repository; -import fr.ippon.tatami.config.ColumnFamilyKeys; +import org.springframework.util.StringUtils; +import javax.annotation.PostConstruct; import javax.inject.Inject; import javax.validation.*; import java.util.HashSet; +import java.util.Optional; import java.util.Set; /** @@ -32,10 +33,10 @@ public class CassandraUserRepository implements UserRepository { private final Logger log = LoggerFactory.getLogger(CassandraUserRepository.class); @Inject - private EntityManagerImpl em; + private Session session; + + private Mapper mapper; - @Inject - private Keyspace keyspaceOperator; @Inject private CounterRepository counterRepository; @@ -43,17 +44,36 @@ public class CassandraUserRepository implements UserRepository { private static final ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); private static final Validator validator = factory.getValidator(); + private PreparedStatement findOneByLoginStmt; + + + private PreparedStatement deleteByLoginStmt; + + + + @PostConstruct + public void init() { + mapper = new MappingManager(session).mapper(User.class); + findOneByLoginStmt = session.prepare( + "SELECT * " + + "FROM user " + + "WHERE login = :login"); + deleteByLoginStmt = session.prepare("DELETE FROM user " + + "WHERE login = :login"); + } + + @Override @CacheEvict(value = "user-cache", key = "#user.login") public void createUser(User user) { - log.debug("Creating user : {}", user); - Set> constraintViolations = validator.validate(user, ContraintsUserCreation.class); if (!constraintViolations.isEmpty()) { - throw new ConstraintViolationException(new HashSet>(constraintViolations)); + throw new ConstraintViolationException(new HashSet<>(constraintViolations)); } - em.persist(user); + BatchStatement batch = new BatchStatement(); + batch.add(mapper.saveQuery(user)); + session.execute(batch); } @Override @@ -62,31 +82,33 @@ public void updateUser(User user) throws ConstraintViolationException, IllegalAr log.debug("Updating user : {}", user); Set> constraintViolations = validator.validate(user); if (!constraintViolations.isEmpty()) { - throw new ConstraintViolationException(new HashSet>(constraintViolations)); + throw new ConstraintViolationException(new HashSet<>(constraintViolations)); } - em.persist(user); + BatchStatement batch = new BatchStatement(); + batch.add(mapper.saveQuery(user)); + session.execute(batch); + } @Override @CacheEvict(value = "user-cache", key = "#user.login") public void deleteUser(User user) { log.debug("Deleting user : {}", user); - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.addDeletion(user.getLogin(), ColumnFamilyKeys.USER_CF); - mutator.execute(); + BatchStatement batch = new BatchStatement(); + batch.add(mapper.deleteQuery(user)); + batch.add(deleteByLoginStmt.bind().setString("login", user.getLogin())); + session.execute(batch); } @Override @Cacheable("user-cache") public User findUserByLogin(String login) { - User user; - try { - user = em.find(User.class, login); - } catch (Exception e) { - log.debug("Exception while looking for user {} : {}", login, e.toString()); - return null; - } - if (user != null) { + User user = null; + BoundStatement stmt = findOneByLoginStmt.bind(); + stmt.setString("login", login); + Optional optionalUser = findOneFromIndex(stmt); + if (optionalUser.isPresent()) { + user = optionalUser.get(); user.setStatusCount(counterRepository.getStatusCounter(login)); user.setFollowersCount(counterRepository.getFollowersCounter(login)); user.setFriendsCount(counterRepository.getFriendsCounter(login)); @@ -97,14 +119,31 @@ public User findUserByLogin(String login) { @Override @CacheEvict(value = "user-cache", key = "#user.login") public void desactivateUser( User user ) { - user.setActivated(false); - em.persist(user); + updateActivated(user,false); + + } + + private void updateActivated(User user, boolean activated) { + Statement update = QueryBuilder.update("user") + .with(QueryBuilder.set("activated", activated)) + .where((QueryBuilder.eq("login", user.getLogin()))); + session.execute(update); } @Override @CacheEvict(value = "user-cache", key = "#user.login") public void reactivateUser( User user ) { - user.setActivated(true); - em.persist(user); + updateActivated(user,true); } + + private Optional findOneFromIndex(BoundStatement stmt) { + ResultSet rs = session.execute(stmt); + if (rs.isExhausted()) { + return Optional.empty(); + } + return Optional.ofNullable(rs.one().getString("login")) + .map(id -> Optional.ofNullable(mapper.get(id))) + .get(); + } + } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraUserTagRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraUserTagRepository.java index c82237bf5..2d5aaa5da 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraUserTagRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraUserTagRepository.java @@ -37,7 +37,7 @@ public Collection findTags(String login) { } @Override - public String getFriendsCF() { + public String getFriendsTable() { return ColumnFamilyKeys.USER_TAGS_CF; } } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraUserTrendRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraUserTrendRepository.java index 6a5e73203..60857bee6 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraUserTrendRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraUserTrendRepository.java @@ -1,21 +1,20 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.querybuilder.QueryBuilder; +import com.datastax.driver.core.utils.UUIDs; import fr.ippon.tatami.repository.UserTrendRepository; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.cassandra.serializers.UUIDSerializer; -import me.prettyprint.cassandra.utils.TimeUUIDUtils; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.beans.ColumnSlice; -import me.prettyprint.hector.api.beans.HColumn; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; import org.springframework.stereotype.Repository; import fr.ippon.tatami.config.ColumnFamilyKeys; import javax.inject.Inject; import java.util.*; +import java.util.stream.Collectors; + +import static com.datastax.driver.core.querybuilder.QueryBuilder.*; -import static me.prettyprint.hector.api.factory.HFactory.createSliceQuery; /** * Cassandra implementation of the User Trends repository. @@ -35,58 +34,51 @@ public class CassandraUserTrendRepository implements UserTrendRepository { private final static int TRENDS_NUMBER_OF_TAGS = 50; @Inject - private Keyspace keyspaceOperator; + private Session session; @Override public void addTag(String login, String tag) { - HColumn column = - HFactory.createColumn( - TimeUUIDUtils.getUniqueTimeUUIDinMillis(), - tag, - COLUMN_TTL, - UUIDSerializer.get(), - StringSerializer.get()); - - Mutator mutator = - HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - - mutator.insert(login, ColumnFamilyKeys.USER_TRENDS_CF, column); + Statement statement = QueryBuilder.insertInto("userTrends") + .value("login", login) + .value("id", UUIDs.timeBased()) + .value("tag", tag) + .using(ttl(COLUMN_TTL)); + session.execute(statement); } @Override public List getRecentTags(String login) { - ColumnSlice query = createSliceQuery(keyspaceOperator, - StringSerializer.get(), UUIDSerializer.get(), StringSerializer.get()) - .setColumnFamily(ColumnFamilyKeys.USER_TRENDS_CF) - .setKey(login) - .setRange(null, null, true, TRENDS_NUMBER_OF_TAGS) - .execute() - .get(); + Statement statement = QueryBuilder.select() + .column("tag") + .from("userTrends") + .where(eq("login", login)) + .orderBy(desc("id")) + .limit(TRENDS_NUMBER_OF_TAGS); - List result = new ArrayList(); - for (HColumn column : query.getColumns()) { - String tag = column.getValue(); - result.add(tag); - } - return result; + ResultSet results = session.execute(statement); + return results + .all() + .stream() + .map(e -> e.getString("tag")) + .collect(Collectors.toList()); } @Override public Collection getUserRecentTags(String login, Date endDate, int nbRecentTags) { - ColumnSlice query = createSliceQuery(keyspaceOperator, - StringSerializer.get(), UUIDSerializer.get(), StringSerializer.get()) - .setColumnFamily(ColumnFamilyKeys.USER_TRENDS_CF) - .setKey(login) - .setRange(null, TimeUUIDUtils.getTimeUUID(endDate.getTime()), true, nbRecentTags) - .execute() - .get(); - Map result = new HashMap(); - String tag; - for (HColumn column : query.getColumns()) { - tag = column.getValue(); - result.put(tag.toLowerCase(), tag); - } - return result.values(); + Statement statement = QueryBuilder.select() + .column("tag") + .from("userTrends") + .where(eq("login", login)) + .and(gt("id",UUIDs.endOf(endDate.getTime()))) + .orderBy(desc("id")) + .limit(nbRecentTags); + + ResultSet results = session.execute(statement); + return results + .all() + .stream() + .map(e -> e.getString("tag")) + .collect(Collectors.toList()); } } diff --git a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraUserlineRepository.java b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraUserlineRepository.java index 9a94b9d0a..002c74d62 100644 --- a/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraUserlineRepository.java +++ b/services/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraUserlineRepository.java @@ -1,15 +1,24 @@ package fr.ippon.tatami.repository.cassandra; +import com.datastax.driver.core.*; +import com.datastax.driver.core.querybuilder.QueryBuilder; +import com.datastax.driver.core.querybuilder.Select; +import com.datastax.driver.mapping.MappingManager; +import fr.ippon.tatami.config.ColumnFamilyKeys; import fr.ippon.tatami.domain.status.Share; +import fr.ippon.tatami.domain.status.Status; import fr.ippon.tatami.repository.UserlineRepository; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; import org.springframework.stereotype.Repository; +import javax.annotation.PostConstruct; +import javax.inject.Inject; import java.util.Collection; import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; +import static com.datastax.driver.core.querybuilder.QueryBuilder.*; +import static fr.ippon.tatami.config.ColumnFamilyKeys.TAGLINE; import static fr.ippon.tatami.config.ColumnFamilyKeys.USERLINE_CF; import static fr.ippon.tatami.config.ColumnFamilyKeys.USERLINE_SHARES_CF; @@ -26,14 +35,31 @@ @Repository public class CassandraUserlineRepository extends AbstractCassandraLineRepository implements UserlineRepository { + @Inject + Session session; + + private PreparedStatement deleteByIdStmt; + + + @PostConstruct + public void init() { + deleteByIdStmt = session.prepare("DELETE FROM userline " + + "WHERE key = :key " + + "AND status = :statusId"); + + } + @Override public void addStatusToUserline(String login, String statusId) { - addStatus(login,USERLINE_CF, statusId); + Statement statement = QueryBuilder.insertInto("userline") + .value("key", login) + .value("status", UUID.fromString(statusId)); + session.execute(statement); } @Override public void removeStatusesFromUserline(String login, Collection statusIdsToDelete) { - removeStatuses(login, USERLINE_CF, statusIdsToDelete); + removeStatuses(login,"userline",statusIdsToDelete); } @Override @@ -43,13 +69,18 @@ public void shareStatusToUserline(String currentLogin, Share share) { @Override public List getUserline(String login, int size, String start, String finish) { - return getLineFromCF(USERLINE_CF, login, size, start, finish); + return getLineFromTable("userline", login, size, start, finish); } @Override public void deleteUserline(String login) { - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.addDeletion(login, USERLINE_CF); - mutator.execute(); + Statement statement = QueryBuilder.delete().from(ColumnFamilyKeys.USERLINE_CF) + .where(eq("login", login)); + session.execute(statement); + } + + @Override + public PreparedStatement getDeleteByIdStmt() { + return deleteByIdStmt; } } diff --git a/services/src/main/java/fr/ippon/tatami/service/AdminService.java b/services/src/main/java/fr/ippon/tatami/service/AdminService.java index e3df6f541..f7b9e3da0 100644 --- a/services/src/main/java/fr/ippon/tatami/service/AdminService.java +++ b/services/src/main/java/fr/ippon/tatami/service/AdminService.java @@ -1,5 +1,6 @@ package fr.ippon.tatami.service; +import fr.ippon.tatami.config.CassandraConfiguration; import fr.ippon.tatami.domain.Domain; import fr.ippon.tatami.domain.Group; import fr.ippon.tatami.domain.User; @@ -9,12 +10,6 @@ import fr.ippon.tatami.repository.DomainRepository; import fr.ippon.tatami.repository.StatusRepository; import fr.ippon.tatami.repository.UserRepository; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.beans.OrderedRows; -import me.prettyprint.hector.api.beans.Row; -import me.prettyprint.hector.api.query.QueryResult; -import me.prettyprint.hector.api.query.RangeSlicesQuery; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; @@ -25,7 +20,6 @@ import java.util.*; import static fr.ippon.tatami.config.ColumnFamilyKeys.STATUS_CF; -import static me.prettyprint.hector.api.factory.HFactory.createRangeSlicesQuery; /** * Administration service. Only users with the "admin" role should access it. @@ -56,8 +50,6 @@ public class AdminService { @Inject private Environment env; - @Inject - private Keyspace keyspaceOperator; public Collection getAllDomains() { return domainRepository.getAllDomains(); @@ -72,6 +64,22 @@ public Map getEnvProperties() { loadProperty(properties, "tatami.message.reloading.enabled"); loadProperty(properties, "smtp.host"); loadProperty(properties, "cassandra.host"); + loadProperty(properties, CassandraConfiguration.CASSANDRA_PORT); + loadProperty(properties, CassandraConfiguration.CASSANDRA_CLUSTER_NAME); + loadProperty(properties, CassandraConfiguration.CASSANDRA_COMPRESSION); + loadProperty(properties, CassandraConfiguration.CASSANDRA_CONNECT_TIMEOUT_MILLIS); + loadProperty(properties, CassandraConfiguration.CASSANDRA_CONSISTENCY); + loadProperty(properties, CassandraConfiguration.CASSANDRA_CONTACT_POINTS); + loadProperty(properties, CassandraConfiguration.CASSANDRA_FETCH_SIZE); + loadProperty(properties, CassandraConfiguration.CASSANDRA_LOAD_BALANCING_POLICY); + loadProperty(properties, CassandraConfiguration.CASSANDRA_PASSWORD); + loadProperty(properties, CassandraConfiguration.CASSANDRA_PROTOCOL_VERSION); + loadProperty(properties, CassandraConfiguration.CASSANDRA_READ_TIMEOUT_MILLIS); + loadProperty(properties, CassandraConfiguration.CASSANDRA_RECONNECTION_POLICY); + loadProperty(properties, CassandraConfiguration.CASSANDRA_RETRY_POLICY); + loadProperty(properties, CassandraConfiguration.CASSANDRA_SERIAL_CONSISTENCY); + loadProperty(properties, CassandraConfiguration.CASSANDRA_SSL_ENABLED); + loadProperty(properties, CassandraConfiguration.CASSANDRA_USER); loadProperty(properties, "search.engine"); loadProperty(properties, "lucene.path"); loadProperty(properties, "elasticsearch.indexNamePrefix"); @@ -140,38 +148,38 @@ public void rebuildIndex() { log.info("Rebuilding the status Index"); String startKey = null; boolean moreStatus = true; - while (moreStatus) { - long startTime = Calendar.getInstance().getTimeInMillis(); - RangeSlicesQuery query = createRangeSlicesQuery(keyspaceOperator, - StringSerializer.get(), StringSerializer.get(), StringSerializer.get()) - .setColumnFamily(STATUS_CF) - .setRange("statusId", "statusId", false, 1) - .setKeys(startKey, null) - .setRowCount(1001); - - QueryResult> result = query.execute(); - List> rows = result.get().getList(); - if (rows.size() == 1001) { // Calculate the pagination - startKey = rows.get(1000).getKey(); - rows = rows.subList(0, 1000); - } else { - moreStatus = false; - } - Collection statuses = new ArrayList(); - for (Row row : rows) { - AbstractStatus abstractStatus = statusRepository.findStatusById(row.getKey()); // This makes 2 calls to the same row - if (abstractStatus != null && // if a status has been removed, it is returned as null - abstractStatus.getType().equals(StatusType.STATUS)) { // Only index standard statuses - - Status status = (Status) abstractStatus; - if (status.getStatusPrivate() == null || !status.getStatusPrivate()) { - statuses.add(status); - } - } - } - searchService.addStatuses(statuses); // This should be batched for optimum performance - log.info("The search engine indexed " + statuses.size() + " statuses in " + (Calendar.getInstance().getTimeInMillis() - startTime) + " ms."); - } +// while (moreStatus) { +// long startTime = Calendar.getInstance().getTimeInMillis(); +// RangeSlicesQuery query = createRangeSlicesQuery(keyspaceOperator, +// StringSerializer.get(), StringSerializer.get(), StringSerializer.get()) +// .setColumnFamily(STATUS_CF) +// .setRange("statusId", "statusId", false, 1) +// .setKeys(startKey, null) +// .setRowCount(1001); +// +// QueryResult> result = query.execute(); +// List> rows = result.get().getList(); +// if (rows.size() == 1001) { // Calculate the pagination +// startKey = rows.get(1000).getKey(); +// rows = rows.subList(0, 1000); +// } else { +// moreStatus = false; +// } +// Collection statuses = new ArrayList(); +// for (Row row : rows) { +// AbstractStatus abstractStatus = statusRepository.findStatusById(row.getKey()); // This makes 2 calls to the same row +// if (abstractStatus != null && // if a status has been removed, it is returned as null +// abstractStatus.getType().equals(StatusType.STATUS)) { // Only index standard statuses +// +// Status status = (Status) abstractStatus; +// if (status.getStatusPrivate() == null || !status.getStatusPrivate()) { +// statuses.add(status); +// } +// } +// } +// searchService.addStatuses(statuses); // This should be batched for optimum performance +// log.info("The search engine indexed " + statuses.size() + " statuses in " + (Calendar.getInstance().getTimeInMillis() - startTime) + " ms."); +// } log.info("Search engine index rebuilt in " + (Calendar.getInstance().getTimeInMillis() - fullIndexStartTime) + " ms."); } diff --git a/services/src/main/java/fr/ippon/tatami/service/AtmosphereService.java b/services/src/main/java/fr/ippon/tatami/service/AtmosphereService.java index 23aad7ec4..027aae2e0 100644 --- a/services/src/main/java/fr/ippon/tatami/service/AtmosphereService.java +++ b/services/src/main/java/fr/ippon/tatami/service/AtmosphereService.java @@ -24,7 +24,7 @@ public class AtmosphereService { */ public void notifyUser(String login, AbstractStatus abstractStatus) { log.debug("Notifying user: {}", login); - StatusDTO statusDTO = timelineService.getStatus(abstractStatus.getStatusId()); + StatusDTO statusDTO = timelineService.getStatus(abstractStatus.getStatusId().toString()); TatamiNotification notification = new TatamiNotification(); notification.setLogin(login); notification.setStatusDTO(statusDTO); diff --git a/services/src/main/java/fr/ippon/tatami/service/AvatarService.java b/services/src/main/java/fr/ippon/tatami/service/AvatarService.java index a1770de23..aa84723b9 100644 --- a/services/src/main/java/fr/ippon/tatami/service/AvatarService.java +++ b/services/src/main/java/fr/ippon/tatami/service/AvatarService.java @@ -7,6 +7,7 @@ import fr.ippon.tatami.repository.DomainConfigurationRepository; import fr.ippon.tatami.repository.UserRepository; import fr.ippon.tatami.security.AuthenticationService; +import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @@ -15,9 +16,14 @@ import javax.inject.Inject; import java.awt.*; import java.awt.image.BufferedImage; +import java.awt.image.DataBufferByte; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.ByteBuffer; +import java.util.Date; @Service public class AvatarService { @@ -87,4 +93,40 @@ private byte[] scaleImage(byte[] data) throws IOException { return byteArrayOutputStream.toByteArray(); } + + public Avatar createAvatarBasedOnAvatar(Avatar avatar) { + User currentUser = authenticationService.getCurrentUser(); + Avatar dbAvatar = avatarRepository.findAvatarByFilename(avatar.getFilename()); + if (dbAvatar != null) { + return dbAvatar; + } else if (avatar.getFilename().startsWith("http")) { + byte[] bytes = null; + try { + bytes = fetchRemoteFile(avatar.getFilename()); + } catch (Exception e) { + log.warn("Could not load bytes from: " + avatar.getFilename()); + } + avatar.setSize(bytes.length); + avatar.setContent(bytes); + avatar.setCreationDate(new Date()); + avatarRepository.createAvatar(avatar); + } + return avatar; + } + + private byte[] fetchRemoteFile(String location) throws Exception { + URL url = new URL(location); + InputStream is = null; + byte[] bytes = null; + try { + is = url.openStream (); + bytes = IOUtils.toByteArray(is); + } catch (IOException e) { + //handle errors + } + finally { + if (is != null) is.close(); + } + return bytes; + } } diff --git a/services/src/main/java/fr/ippon/tatami/service/FriendshipService.java b/services/src/main/java/fr/ippon/tatami/service/FriendshipService.java index 9b6ca8fc0..bda0430f2 100755 --- a/services/src/main/java/fr/ippon/tatami/service/FriendshipService.java +++ b/services/src/main/java/fr/ippon/tatami/service/FriendshipService.java @@ -74,7 +74,7 @@ public boolean followUser(String usernameToFollow) { counterRepository.incrementFollowersCounter(followedUser.getLogin()); // mention the friend that the user has started following him MentionFriend mentionFriend = statusRepository.createMentionFriend(followedUser.getLogin(), currentUser.getLogin()); - mentionlineRepository.addStatusToMentionline(mentionFriend.getLogin(), mentionFriend.getStatusId()); + mentionlineRepository.addStatusToMentionline(mentionFriend.getLogin(), mentionFriend.getStatusId().toString()); log.debug("User {} now follows user {} ", currentUser.getLogin(), followedUser.getLogin()); return true; } else { diff --git a/services/src/main/java/fr/ippon/tatami/service/GroupService.java b/services/src/main/java/fr/ippon/tatami/service/GroupService.java index e805d24a8..d56a776a4 100644 --- a/services/src/main/java/fr/ippon/tatami/service/GroupService.java +++ b/services/src/main/java/fr/ippon/tatami/service/GroupService.java @@ -16,6 +16,7 @@ import java.util.Collection; import java.util.Map; import java.util.TreeSet; +import java.util.UUID; /** * Service bean for managing groups. @@ -31,9 +32,6 @@ public class GroupService { @Inject private GroupRepository groupRepository; - @Inject - private GroupDetailsRepository groupDetailsRepository; - @Inject private GroupMembersRepository groupMembersRepository; @@ -57,8 +55,7 @@ public void createGroup(String name, String description, boolean publicGroup) { log.debug("Creating group : {}", name); User currentUser = authenticationService.getCurrentUser(); String domain = DomainUtil.getDomainFromLogin(currentUser.getLogin()); - String groupId = groupRepository.createGroup(domain); - groupDetailsRepository.createGroupDetails(groupId, name, description, publicGroup); + UUID groupId = groupRepository.createGroup(domain, name, description, publicGroup); groupMembersRepository.addAdmin(groupId, currentUser.getLogin()); groupCounterRepository.incrementGroupCounter(domain, groupId); userGroupRepository.addGroupAsAdmin(currentUser.getLogin(), groupId); @@ -69,7 +66,7 @@ public void createGroup(String name, String description, boolean publicGroup) { @CacheEvict(value = {"group-user-cache", "group-cache"}, allEntries = true) public void editGroup(Group group) { log.debug("Editing group : {}", group.getGroupId()); - groupDetailsRepository.editGroupDetails(group.getGroupId(), + groupRepository.editGroupDetails(group.getGroupId(), group.getName(), group.getDescription(), group.isArchivedGroup()); @@ -77,7 +74,7 @@ public void editGroup(Group group) { searchService.addGroup(group); } - public Collection getMembersForGroup(String groupId, String login) { + public Collection getMembersForGroup(UUID groupId, String login) { Map membersMap = groupMembersRepository.findMembers(groupId); Collection friendLogins = friendRepository.findFriendsForUser(login); Collection userGroupDTOs = new TreeSet(); @@ -105,7 +102,7 @@ public Collection getMembersForGroup(String groupId, String login) - public UserGroupDTO getMembersForGroup(String groupId, User userWanted) { + public UserGroupDTO getMembersForGroup(UUID groupId, User userWanted) { Map membersMap = groupMembersRepository.findMembers(groupId); for (Map.Entry member : membersMap.entrySet()) { User user = userRepository.findUserByLogin(member.getKey()); @@ -126,31 +123,31 @@ public UserGroupDTO getMembersForGroup(String groupId, User userWanted) { @Cacheable(value = "group-user-cache", key = "#user.login") public Collection getGroupsForUser(User user) { - Collection groupIds = userGroupRepository.findGroups(user.getLogin()); + Collection groupIds = userGroupRepository.findGroups(user.getLogin()); return buildGroupIdsList(groupIds); } @Cacheable(value = "group-user-cache", key = "#user.login") public Collection getGroupsOfUser(User user) { - Collection groupIds = userGroupRepository.findGroups(user.getLogin()); + Collection groupIds = userGroupRepository.findGroups(user.getLogin()); return getGroupDetails(user, groupIds); } @Cacheable(value = "group-cache") - public Group getGroupById(String domain, String groupId) { + public Group getGroupById(String domain, UUID groupId) { return internalGetGroupById(domain, groupId); } public Collection getGroupsWhereUserIsAdmin(User user) { - Collection groupIds = userGroupRepository.findGroupsAsAdmin(user.getLogin()); + Collection groupIds = userGroupRepository.findGroupsAsAdmin(user.getLogin()); return getGroupDetails(user, groupIds); } - private Collection getGroupDetails(User currentUser, Collection groupIds) { + private Collection getGroupDetails(User currentUser, Collection groupIds) { String domain = DomainUtil.getDomainFromLogin(currentUser.getLogin()); Collection groups = new TreeSet(); - for (String groupId : groupIds) { + for (UUID groupId : groupIds) { Group group = internalGetGroupById(domain, groupId); groups.add(group); } @@ -162,13 +159,8 @@ public Collection getGroupsWhereCurrentUserIsAdmin() { return getGroupsWhereUserIsAdmin(currentUser); } - private Group internalGetGroupById(String domain, String groupId) { + private Group internalGetGroupById(String domain, UUID groupId) { Group group = groupRepository.getGroupById(domain, groupId); - Group groupDetails = groupDetailsRepository.getGroupDetails(groupId); - group.setName(groupDetails.getName()); - group.setPublicGroup(groupDetails.isPublicGroup()); - group.setArchivedGroup(groupDetails.isArchivedGroup()); - group.setDescription(groupDetails.getDescription()); long counter = groupCounterRepository.getGroupCounter(domain, groupId); group.setCounter(counter); return group; @@ -176,10 +168,10 @@ private Group internalGetGroupById(String domain, String groupId) { @CacheEvict(value = {"group-user-cache", "group-cache"}, allEntries = true) public void addMemberToGroup(User user, Group group) { - String groupId = group.getGroupId(); - Collection userCurrentGroupIds = userGroupRepository.findGroups(user.getLogin()); + UUID groupId = group.getGroupId(); + Collection userCurrentGroupIds = userGroupRepository.findGroups(user.getLogin()); boolean userIsAlreadyAMember = false; - for (String testGroupId : userCurrentGroupIds) { + for (UUID testGroupId : userCurrentGroupIds) { if (testGroupId.equals(groupId)) { userIsAlreadyAMember = true; } @@ -196,10 +188,10 @@ public void addMemberToGroup(User user, Group group) { @CacheEvict(value = {"group-user-cache", "group-cache"}, allEntries = true) public void removeMemberFromGroup(User user, Group group) { - String groupId = group.getGroupId(); - Collection userCurrentGroupIds = userGroupRepository.findGroups(user.getLogin()); + UUID groupId = group.getGroupId(); + Collection userCurrentGroupIds = userGroupRepository.findGroups(user.getLogin()); boolean userIsAlreadyAMember = false; - for (String testGroupId : userCurrentGroupIds) { + for (UUID testGroupId : userCurrentGroupIds) { if (testGroupId.equals(groupId)) { userIsAlreadyAMember = true; } @@ -233,7 +225,7 @@ public Group buildGroup(Group group) { return buildGroup(currentUser, group); } - private Group getGroupFromUser(User currentUser, String groupId) { + private Group getGroupFromUser(User currentUser, UUID groupId) { Collection groups = getGroupsOfUser(currentUser); for (Group testGroup : groups) { if (testGroup.getGroupId().equals(groupId)) { @@ -293,20 +285,20 @@ else if(group.isPublicGroup()) { return group; } - public Collection buildGroupIdsList(Collection groupIds) { + public Collection buildGroupIdsList(Collection groupIds) { Collection groups = new TreeSet(); - for (String groupId : groupIds) { + for (UUID groupId : groupIds) { groups.add(buildGroupIds(groupId)); } return groups; } - public Group buildGroupIds(String groupId) { + public Group buildGroupIds(UUID groupId) { User currentUser = authenticationService.getCurrentUser(); return buildGroupIds(currentUser, groupId); } - public Group buildGroupIds(User user, String groupId) { + public Group buildGroupIds(User user, UUID groupId) { String domain = DomainUtil.getDomainFromLogin(user.getLogin()); Group group = getGroupById(domain, groupId); return buildGroup(group); diff --git a/services/src/main/java/fr/ippon/tatami/service/MentionService.java b/services/src/main/java/fr/ippon/tatami/service/MentionService.java index 35cac82f4..ac3d7c1ab 100644 --- a/services/src/main/java/fr/ippon/tatami/service/MentionService.java +++ b/services/src/main/java/fr/ippon/tatami/service/MentionService.java @@ -32,7 +32,7 @@ public class MentionService { * The mentioned user can also be notified by email. */ public void mentionUser(String mentionedLogin, Status status) { - mentionlineRepository.addStatusToMentionline(mentionedLogin, status.getStatusId()); + mentionlineRepository.addStatusToMentionline(mentionedLogin, status.getStatusId().toString()); User mentionnedUser = userRepository.findUserByLogin(mentionedLogin); diff --git a/services/src/main/java/fr/ippon/tatami/service/StatusUpdateService.java b/services/src/main/java/fr/ippon/tatami/service/StatusUpdateService.java index 73c949f58..bc2185849 100755 --- a/services/src/main/java/fr/ippon/tatami/service/StatusUpdateService.java +++ b/services/src/main/java/fr/ippon/tatami/service/StatusUpdateService.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; +import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -141,7 +142,7 @@ public void replyToStatus(String content, String replyTo, Collection att Status status = (Status) abstractStatus; Group group = null; if (status.getGroupId() != null) { - group = groupService.getGroupById(status.getDomain(), status.getGroupId()); + group = groupService.getGroupById(status.getDomain(), UUID.fromString(status.getGroupId())); if (group.isArchivedGroup()) { throw new ArchivedGroupException(); @@ -162,12 +163,12 @@ public void replyToStatus(String content, String replyTo, Collection att content, realOriginalStatus.getStatusPrivate(), group, - realOriginalStatus.getStatusId(), - status.getStatusId(), + realOriginalStatus.getStatusId().toString(), + status.getStatusId().toString(), status.getUsername(), attachmentIds); - discussionRepository.addReplyToDiscussion(realOriginalStatus.getStatusId(), replyStatus.getStatusId()); + discussionRepository.addReplyToDiscussion(realOriginalStatus.getStatusId().toString(), replyStatus.getStatusId().toString()); } else { log.debug("Replying directly to the status at the origin of the disucssion"); // The original status of the discussion is the one we reply to @@ -175,12 +176,12 @@ public void replyToStatus(String content, String replyTo, Collection att createStatus(content, status.getStatusPrivate(), group, - status.getStatusId(), - status.getStatusId(), + status.getStatusId().toString(), + status.getStatusId().toString(), status.getUsername(), attachmentIds); - discussionRepository.addReplyToDiscussion(status.getStatusId(), replyStatus.getStatusId()); + discussionRepository.addReplyToDiscussion(status.getStatusId().toString(), replyStatus.getStatusId().toString()); } } @@ -240,7 +241,7 @@ private Status createStatus(String content, if (attachmentIds != null && attachmentIds.size() > 0) { for (String attachmentId : attachmentIds) { - statusAttachmentRepository.addAttachmentId(status.getStatusId(), + statusAttachmentRepository.addAttachmentId(status.getStatusId().toString(), attachmentId); } } @@ -258,7 +259,7 @@ private Status createStatus(String content, // add status to the dayline, userline String day = StatsService.DAYLINE_KEY_FORMAT.format(status.getStatusDate()); daylineRepository.addStatusToDayline(status, day); - userlineRepository.addStatusToUserline(status.getLogin(), status.getStatusId()); + userlineRepository.addStatusToUserline(status.getLogin(), status.getStatusId().toString()); // add the status to the group line and group followers manageGroups(status, group, followersForUser); @@ -288,7 +289,7 @@ private Status createStatus(String content, private void manageGroups(Status status, Group group, Collection followersForUser) { if (group != null) { - grouplineRepository.addStatusToGroupline(group.getGroupId(), status.getStatusId()); + grouplineRepository.addStatusToGroupline(group.getGroupId(), status.getStatusId().toString()); Collection groupMemberLogins = groupMembersRepository.findMembers(group.getGroupId()).keySet(); // For all people following the group for (String groupMemberLogin : groupMemberLogins) { @@ -311,7 +312,7 @@ private void manageGroups(Status status, Group group, Collection followe private void addToCompanyWall(Status status, Group group) { if (isPublicGroup(group)) { - domainlineRepository.addStatusToDomainline(status.getDomain(), status.getStatusId()); + domainlineRepository.addStatusToDomainline(status.getDomain(), status.getStatusId().toString()); } } @@ -355,7 +356,7 @@ private void manageMentions(Status status, Group group, String currentLogin, Str // If this is a private group, and if the mentioned user is not in the group, he will not see the status if (!isPublicGroup(group)) { - Collection groupIds = userGroupRepository.findGroups(mentionedLogin); + Collection groupIds = userGroupRepository.findGroups(mentionedLogin); if (groupIds.contains(group.getGroupId())) { // The user is part of the private group mentionUser(mentionedLogin, status); } @@ -376,7 +377,7 @@ private void addStatusToTagFollowers(Status status, Group group, String tag) { } } else { // This is a private status for (String followerLogin : followersForTag) { - Collection groupIds = userGroupRepository.findGroups(followerLogin); + Collection groupIds = userGroupRepository.findGroups(followerLogin); if (groupIds.contains(group.getGroupId())) { // The user is part of the private group addStatusToTimelineAndNotify(followerLogin, status); } @@ -405,7 +406,7 @@ private boolean isPublicGroup(Group group) { * Adds the status to the timeline and notifies the user with Atmosphere. */ private void addStatusToTimelineAndNotify(String login, Status status) { - timelineRepository.addStatusToTimeline(login, status.getStatusId()); + timelineRepository.addStatusToTimeline(login, status.getStatusId().toString()); atmosphereService.notifyUser(login, status); } } diff --git a/services/src/main/java/fr/ippon/tatami/service/SuggestionService.java b/services/src/main/java/fr/ippon/tatami/service/SuggestionService.java index cb35b6450..6bc9d7de4 100644 --- a/services/src/main/java/fr/ippon/tatami/service/SuggestionService.java +++ b/services/src/main/java/fr/ippon/tatami/service/SuggestionService.java @@ -73,15 +73,15 @@ public Collection suggestUsers(String login) { @Cacheable("suggest-groups-cache") public Collection suggestGroups(String login) { - Map groupCount = new HashMap(); - List groupIds = userGroupRepository.findGroups(login); + Map groupCount = new HashMap<>(); + List groupIds = userGroupRepository.findGroups(login); List friendIds = friendshipService.getFriendIdsForUser(login); friendIds = reduceCollectionSize(friendIds, SAMPLE_SIZE); for (String friendId : friendIds) { - List groupsOfFriend = userGroupRepository.findGroups(friendId); - for (String groupOfFriend : groupsOfFriend) { + List groupsOfFriend = userGroupRepository.findGroups(friendId); + for (UUID groupOfFriend : groupsOfFriend) { if (!groupIds.contains(groupOfFriend)) { - incrementKeyCounterInMap(groupCount, groupOfFriend); + incrementKeyCounterInMap(groupCount, groupOfFriend.toString()); } } } @@ -89,7 +89,7 @@ public Collection suggestGroups(String login) { List groupSuggestions = new ArrayList(); String domain = DomainUtil.getDomainFromLogin(login); for (String mostFollowedGroup : mostFollowedGroups) { - Group suggestion = groupService.getGroupById(domain, mostFollowedGroup); + Group suggestion = groupService.getGroupById(domain, UUID.fromString(mostFollowedGroup)); if (suggestion.isPublicGroup()) { // Only suggest public groups for the moment groupSuggestions.add(suggestion); } diff --git a/services/src/main/java/fr/ippon/tatami/service/TimelineService.java b/services/src/main/java/fr/ippon/tatami/service/TimelineService.java index 029de1747..180563926 100755 --- a/services/src/main/java/fr/ippon/tatami/service/TimelineService.java +++ b/services/src/main/java/fr/ippon/tatami/service/TimelineService.java @@ -144,10 +144,10 @@ public StatusDetails getStatusDetails(String statusId) { log.debug("Status does not have the correct type"); return details; } - details.setStatusId(status.getStatusId()); + details.setStatusId(status.getStatusId().toString()); // Shares management - Collection sharedByLogins = sharesRepository.findLoginsWhoSharedAStatus(status.getStatusId()); + Collection sharedByLogins = sharesRepository.findLoginsWhoSharedAStatus(status.getStatusId().toString()); details.setSharedByLogins(userService.getUsersByLogin(sharedByLogins)); log.debug("Status shared by {} users", sharedByLogins.size()); @@ -160,10 +160,10 @@ public StatusDetails getStatusDetails(String statusId) { // Add the replies statusIdsInDiscussion.addAll(discussionRepository.findStatusIdsInDiscussion(status.getDiscussionId())); // Remove the current status from the list - statusIdsInDiscussion.remove(status.getStatusId()); + statusIdsInDiscussion.remove(status.getStatusId().toString()); } else { // This is the original discussion // Add the replies - statusIdsInDiscussion.addAll(discussionRepository.findStatusIdsInDiscussion(status.getStatusId())); + statusIdsInDiscussion.addAll(discussionRepository.findStatusIdsInDiscussion(status.getStatusId().toString())); } // Transform the Set to a Map @@ -189,22 +189,22 @@ public Collection buildStatusList(List line) { usergroups = Collections.emptyList(); favoriteLine = Collections.emptyList(); } - Collection statuses = new ArrayList(line.size()); + Collection statuses = new ArrayList<>(line.size()); for (String statusId : line) { AbstractStatus abstractStatus = statusRepository.findStatusById(statusId); if (abstractStatus != null) { User statusUser = userService.getUserByLogin(abstractStatus.getLogin()); if (statusUser != null) { // Security check - // bypass the security check when no user is logged in - // => for non-authenticated rss access + // bypass the security check when no user is logged in + // => for non-authenticated rss access if ((currentUser != null) && !statusUser.getDomain().equals(currentUser.getDomain())) { throw new DomainViolationException("User " + currentUser + " tried to access " + " status : " + abstractStatus); } StatusDTO statusDTO = new StatusDTO(); - statusDTO.setStatusId(abstractStatus.getStatusId()); + statusDTO.setStatusId(abstractStatus.getStatusId().toString()); statusDTO.setStatusDate(abstractStatus.getStatusDate()); statusDTO.setGeoLocalization(abstractStatus.getGeoLocalization()); statusDTO.setActivated(statusUser.getActivated()); @@ -219,7 +219,7 @@ public Collection buildStatusList(List line) { Share share = (Share) abstractStatus; AbstractStatus originalStatus = statusRepository.findStatusById(share.getOriginalStatusId()); if (originalStatus != null) { // Find the original status - statusDTO.setTimelineId(share.getStatusId()); + statusDTO.setTimelineId(share.getStatusId().toString()); statusDTO.setSharedByUsername(share.getUsername()); statusUser = userService.getUserByLogin(originalStatus.getLogin()); addStatusToLine(statuses, statusDTO, originalStatus, statusUser, usergroups, favoriteLine); @@ -230,7 +230,7 @@ public Collection buildStatusList(List line) { MentionShare mentionShare = (MentionShare) abstractStatus; AbstractStatus originalStatus = statusRepository.findStatusById(mentionShare.getOriginalStatusId()); if (originalStatus != null) { // Find the status that was shared - statusDTO.setTimelineId(mentionShare.getStatusId()); + statusDTO.setTimelineId(mentionShare.getStatusId().toString()); statusDTO.setSharedByUsername(mentionShare.getUsername()); statusUser = userService.getUserByLogin(mentionShare.getLogin()); addStatusToLine(statuses, statusDTO, originalStatus, statusUser, usergroups, favoriteLine); @@ -239,7 +239,7 @@ public Collection buildStatusList(List line) { } } else if (abstractStatus.getType().equals(StatusType.MENTION_FRIEND)) { MentionFriend mentionFriend = (MentionFriend) abstractStatus; - statusDTO.setTimelineId(mentionFriend.getStatusId()); + statusDTO.setTimelineId(mentionFriend.getStatusId().toString()); //statusDTO.setSharedByUsername(mentionFriend.getUsername()); statusUser = userService.getUserByLogin(mentionFriend.getFollowerLogin()); statusDTO.setFirstName(statusUser.getFirstName()); @@ -251,7 +251,7 @@ public Collection buildStatusList(List line) { Announcement announcement = (Announcement) abstractStatus; AbstractStatus originalStatus = statusRepository.findStatusById(announcement.getOriginalStatusId()); if (originalStatus != null) { // Find the status that was announced - statusDTO.setTimelineId(announcement.getStatusId()); + statusDTO.setTimelineId(announcement.getStatusId().toString()); statusDTO.setSharedByUsername(announcement.getUsername()); statusUser = userService.getUserByLogin(originalStatus.getLogin()); addStatusToLine(statuses, statusDTO, originalStatus, statusUser, usergroups, favoriteLine); @@ -259,7 +259,7 @@ public Collection buildStatusList(List line) { log.debug("Announced status has been deleted"); } } else { // Normal status - statusDTO.setTimelineId(abstractStatus.getStatusId()); + statusDTO.setTimelineId(abstractStatus.getStatusId().toString()); addStatusToLine(statuses, statusDTO, abstractStatus, statusUser, usergroups, favoriteLine); } } else { @@ -300,7 +300,7 @@ else if(currentUser.getUsername().equals(statusDTO.getSharedByUsername())) //Gre * permission to see them. */ private Collection findStatusesToCleanUp(List statuses, Collection dtos) { - Collection statusIdsToCleanUp = new ArrayList(); + Collection statusIdsToCleanUp = new ArrayList<>(); for (String statusId : statuses) { boolean statusToDelete = true; for (StatusDTO statusDTO : dtos) { @@ -327,7 +327,7 @@ private void addStatusToLine(Collection line, boolean hiddenStatus = false; if (status.getGroupId() != null) { statusDTO.setGroupId(status.getGroupId()); - Group group = groupService.getGroupById(statusUser.getDomain(), statusDTO.getGroupId()); + Group group = groupService.getGroupById(statusUser.getDomain(), UUID.fromString(statusDTO.getGroupId())); // if this is a private group and the user is not part of it, he cannot see the status if (!group.isPublicGroup() && !usergroups.contains(group)) { hiddenStatus = true; @@ -551,7 +551,7 @@ public void shareStatus(String statusId) { private void internalShareStatus(String currentLogin, Status status) { // create share - Share share = statusRepository.createShare(currentLogin, status.getStatusId()); + Share share = statusRepository.createShare(currentLogin, status.getStatusId().toString()); // add status to the user's userline and timeline userlineRepository.shareStatusToUserline(currentLogin, share); @@ -562,10 +562,10 @@ private void internalShareStatus(String currentLogin, Status status) { shareStatusToTimelineAndNotify(currentLogin, followerLogin, share); } // update the status details to add this share - sharesRepository.newShareByLogin(status.getStatusId(), currentLogin); + sharesRepository.newShareByLogin(status.getStatusId().toString(), currentLogin); // mention the status' author that the user has shared his status - MentionShare mentionShare = statusRepository.createMentionShare(currentLogin, status.getStatusId()); - mentionlineRepository.addStatusToMentionline(status.getLogin(), mentionShare.getStatusId()); + MentionShare mentionShare = statusRepository.createMentionShare(currentLogin, status.getStatusId().toString()); + mentionlineRepository.addStatusToMentionline(status.getLogin(), mentionShare.getStatusId().toString()); } public void addFavoriteStatus(String statusId) { @@ -613,7 +613,7 @@ public void announceStatus(String statusId) { private void internalAnnounceStatus(String currentLogin, Status status) { // create announcement - Announcement announcement = statusRepository.createAnnouncement(currentLogin, status.getStatusId()); + Announcement announcement = statusRepository.createAnnouncement(currentLogin, status.getStatusId().toString()); // add status to everyone's timeline String domain = DomainUtil.getDomainFromLogin(currentLogin); diff --git a/services/src/main/java/fr/ippon/tatami/service/elasticsearch/ElasticsearchSearchService.java b/services/src/main/java/fr/ippon/tatami/service/elasticsearch/ElasticsearchSearchService.java index 5fa20bcc7..0b607aed2 100755 --- a/services/src/main/java/fr/ippon/tatami/service/elasticsearch/ElasticsearchSearchService.java +++ b/services/src/main/java/fr/ippon/tatami/service/elasticsearch/ElasticsearchSearchService.java @@ -6,7 +6,7 @@ import fr.ippon.tatami.domain.Group; import fr.ippon.tatami.domain.User; import fr.ippon.tatami.domain.status.Status; -import fr.ippon.tatami.repository.GroupDetailsRepository; +import fr.ippon.tatami.repository.GroupRepository; import fr.ippon.tatami.service.SearchService; import org.apache.commons.lang.StringUtils; import org.elasticsearch.ElasticSearchException; @@ -58,7 +58,7 @@ public class ElasticsearchSearchService implements SearchService { private String indexNamePrefix; @Inject - private GroupDetailsRepository groupDetailsRepository; + private GroupRepository groupRepository; private Client client() { return engine.client(); @@ -164,7 +164,7 @@ private boolean createIndex() { private final ElasticsearchMapper statusMapper = new ElasticsearchMapper() { @Override public String id(Status status) { - return status.getStatusId(); + return status.getStatusId().toString(); } @Override @@ -188,7 +188,7 @@ public XContentBuilder toJson(Status status) throws IOException { .field("content", status.getContent()); if (status.getGroupId() != null) { - Group group = groupDetailsRepository.getGroupDetails(status.getGroupId()); + Group group = groupRepository.getGroupByGroupId(UUID.fromString(status.getGroupId())); source.field("groupId", status.getGroupId()); source.field("publicGroup", group.isPublicGroup()); } @@ -326,7 +326,7 @@ public Collection searchUserByPrefix(String domain, String prefix) { private final ElasticsearchMapper groupMapper = new ElasticsearchMapper() { @Override public String id(Group group) { - return group.getGroupId(); + return group.getGroupId().toString(); } @Override @@ -368,7 +368,7 @@ public Collection searchGroupByPrefix(String domain, String prefix, int s Collection ids = searchByPrefix(domain, prefix, size, groupMapper); List groups = new ArrayList(ids.size()); for (String id : ids) { - groups.add(groupDetailsRepository.getGroupDetails(id)); + groups.add(groupRepository.getGroupByGroupId(UUID.fromString(id))); } return groups; } diff --git a/services/src/main/java/fr/ippon/tatami/service/util/AnalysisUtil.java b/services/src/main/java/fr/ippon/tatami/service/util/AnalysisUtil.java index f4ebaa41c..3fd6de715 100644 --- a/services/src/main/java/fr/ippon/tatami/service/util/AnalysisUtil.java +++ b/services/src/main/java/fr/ippon/tatami/service/util/AnalysisUtil.java @@ -10,12 +10,12 @@ public class AnalysisUtil { private static final int RESULTS_SIZE = 20; public static void incrementKeyCounterInMap(Map map, String key) { - if (map.containsKey(key)) { + if (map.containsKey(key.toString())) { Integer total = map.get(key); total++; - map.put(key, total); + map.put(key.toString(), total); } else { - map.put(key, 1); + map.put(key.toString(), 1); } } diff --git a/services/src/main/java/fr/ippon/tatami/web/rest/dto/AvatarMeta.java b/services/src/main/java/fr/ippon/tatami/web/rest/dto/AvatarMeta.java new file mode 100644 index 000000000..702378650 --- /dev/null +++ b/services/src/main/java/fr/ippon/tatami/web/rest/dto/AvatarMeta.java @@ -0,0 +1,36 @@ +package fr.ippon.tatami.web.rest.dto; + +/** + * Created by lnorregaard on 02/12/15. + */ +public class AvatarMeta { + private String avatarId; + + private String filename; + + private long size; + + public String getAvatarId() { + return avatarId; + } + + public void setAvatarId(String avatarId) { + this.avatarId = avatarId; + } + + public String getFilename() { + return filename; + } + + public void setFilename(String filename) { + this.filename = filename; + } + + public long getSize() { + return size; + } + + public void setSize(long size) { + this.size = size; + } +} diff --git a/services/src/main/java/fr/ippon/tatami/web/syndic/SyndicView.java b/services/src/main/java/fr/ippon/tatami/web/syndic/SyndicView.java index b1f683a80..ca03712c5 100644 --- a/services/src/main/java/fr/ippon/tatami/web/syndic/SyndicView.java +++ b/services/src/main/java/fr/ippon/tatami/web/syndic/SyndicView.java @@ -1,8 +1,8 @@ package fr.ippon.tatami.web.syndic; -import com.sun.syndication.feed.rss.Channel; -import com.sun.syndication.feed.rss.Content; -import com.sun.syndication.feed.rss.Item; +import com.rometools.rome.feed.rss.Channel; +import com.rometools.rome.feed.rss.Content; +import com.rometools.rome.feed.rss.Item; import fr.ippon.tatami.service.dto.StatusDTO; import org.pegdown.PegDownProcessor; import org.slf4j.Logger; diff --git a/services/src/main/java/me/prettyprint/hom/CassandraPersistenceProvider.java b/services/src/main/java/me/prettyprint/hom/CassandraPersistenceProvider.java deleted file mode 100644 index 252cade1b..000000000 --- a/services/src/main/java/me/prettyprint/hom/CassandraPersistenceProvider.java +++ /dev/null @@ -1,44 +0,0 @@ -package me.prettyprint.hom; - -import org.apache.openjpa.persistence.EntityManagerFactoryImpl; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.persistence.EntityManagerFactory; -import javax.persistence.spi.PersistenceProvider; -import javax.persistence.spi.PersistenceUnitInfo; -import java.util.Map; - -/** - * This is a temporary hack : this class is needed at startup by the HOM configuration, - * but is not provided (and anyway HOM doesn't use it...). - */ -public class CassandraPersistenceProvider implements PersistenceProvider { - private static final Logger log = LoggerFactory.getLogger(CassandraPersistenceProvider.class); - - private Map defProperties; - - public CassandraPersistenceProvider() { - } - - public CassandraPersistenceProvider(Map map) { - this.defProperties = map; - } - - @Override - public EntityManagerFactory createContainerEntityManagerFactory( - PersistenceUnitInfo info, Map map) { - log.debug("creating EntityManagerFactory {} with properties {} ", null, map); - return null; - } - - @Override - public EntityManagerFactory createEntityManagerFactory(String emName, Map map) { - log.debug("creating EntityManagerFactory {} with properties {} ", emName, map); - if (map == null || map.isEmpty()) { - return new EntityManagerFactoryImpl(); - } - return new EntityManagerFactoryImpl(); - } - -} diff --git a/services/src/main/resources/META-INF/spring/applicationContext-security.xml b/services/src/main/resources/META-INF/spring/applicationContext-security.xml index eb934488b..e17f4bd12 100644 --- a/services/src/main/resources/META-INF/spring/applicationContext-security.xml +++ b/services/src/main/resources/META-INF/spring/applicationContext-security.xml @@ -5,11 +5,11 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans - http://www.springframework.org/schema/beans/spring-beans-3.1.xsd + http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/security - http://www.springframework.org/schema/security/spring-security-3.1.xsd + http://www.springframework.org/schema/security/spring-security-4.0.xsd http://www.springframework.org/schema/context - http://www.springframework.org/schema/context/spring-context-3.1.xsd"> + http://www.springframework.org/schema/context/spring-context-4.0.xsd"> @@ -21,6 +21,9 @@ + + + @@ -28,6 +31,7 @@ + diff --git a/services/src/main/resources/META-INF/tatami/tatami.properties b/services/src/main/resources/META-INF/tatami/tatami.properties index e4fe6a8d1..f95f25ba1 100755 --- a/services/src/main/resources/META-INF/tatami/tatami.properties +++ b/services/src/main/resources/META-INF/tatami/tatami.properties @@ -16,7 +16,7 @@ tatami.metrics.graphite.host=${tatami.metrics.graphite.host} tatami.metrics.graphite.port=${tatami.metrics.graphite.port} #User configuration -tatami.admin.users=jdubois@ippon.fr,julien.dubois@gmail.com,vdebelil@ippon.fr,ggruel@ippon.fr,snomis@ippon.fr,rlheritier@ippon.fr,jrisch@ippon.fr,bscott@ippon.fr +tatami.admin.users=lnorregaard@nordija.com tatami.ldapauth.domain=ippon.fr tatami.ldapauth.url=${tatami.ldapauth.url} tatami.ldapauth.searchbase=${tatami.ldapauth.searchbase} @@ -39,9 +39,25 @@ apple.push.certificate=${apple.push.certificate} apple.push.password=${apple.push.password} #Cassandra configuration -cassandra.host=127.0.0.1:9160 +cassandra.host=127.0.0.1:9042 cassandra.clusterName=Tatami cluster cassandra.keyspace=tatami +cassandra.cluster=Test cluster +cassandra.port=9042 +cassandra.protocolVersion=V2 +#cassandra.compression= +#cassandra.loadBalancingPolicy= +#cassandra.consistency= +#cassandra.serialConsistency= +cassandra.fetchSize=1000000 +#cassandra.reconnectionPolicy= +#cassandra.retryPolicy= +#cassandra.user= +#cassandra.password= +#cassandra.connectTimeoutMillis= +#cassandra.readTimeoutMillis= +#cassandra.sslEnabled= +cassandra.contactPoints=localhost # Search engine configuration : you can use either Elastic Search in embedded or in remote mode # - In embedded mode, Elastic Search runs inside Tatami : this is useful for development, test, and small installations diff --git a/services/src/main/resources/config/cql/create-tables.cql b/services/src/main/resources/config/cql/create-tables.cql new file mode 100644 index 000000000..2e42a1388 --- /dev/null +++ b/services/src/main/resources/config/cql/create-tables.cql @@ -0,0 +1,271 @@ +CREATE TABLE IF NOT EXISTS user ( + login varchar, + password varchar, + username varchar, + firstname varchar, + lastname varchar, + domain varchar, + activated boolean, + avatar varchar, + jobTitle varchar, + activation_key varchar, + reset_key varchar, + phoneNumber varchar, + openIdUrl varchar, + preferences_mention_email boolean, + rssUid varchar, + weekly_digest_subscription boolean, + daily_digest_subscription boolean, + attachmentsSize bigint, + PRIMARY KEY(login) +); + +CREATE TABLE IF NOT EXISTS status ( + statusId timeuuid, + type varchar, + login varchar, + username varchar, + domain varchar, + statusDate timestamp, + geoLocalization varchar, + removed boolean, + groupId varchar, + statusPrivate boolean, + hasAttachments boolean, + content varchar, + discussionId varchar, + replyTo varchar, + replyToUsername varchar, + detailsAvailable boolean, + originalStatusId timeuuid, + followerLogin varchar, + PRIMARY KEY(statusId) +); + + + +CREATE TABLE IF NOT EXISTS timeline ( + key varchar, + status timeuuid, + PRIMARY KEY(key,status) +); + +CREATE TABLE IF NOT EXISTS domain ( + domainId varchar, + login varchar, + created timeuuid, + PRIMARY KEY(domainId, login) +); + +CREATE TABLE IF NOT EXISTS counter ( + login varchar, + STATUS_COUNTER counter, + FOLLOWERS_COUNTER counter, + FRIENDS_COUNTER counter, + PRIMARY KEY(login) +); + +CREATE TABLE IF NOT EXISTS friends ( + login varchar, + friendLogin varchar, + PRIMARY KEY(login,friendLogin) +); + +CREATE TABLE IF NOT EXISTS followers ( + key varchar, + login varchar, + PRIMARY KEY(key,login) +); + +CREATE TABLE IF NOT EXISTS dayline ( + domainDay varchar, + username varchar, + statusCount counter, + PRIMARY KEY(domainDay, username) +); + +CREATE TABLE IF NOT EXISTS tagline ( + key varchar, + status timeuuid, + PRIMARY KEY(key, status) +); + +CREATE TABLE IF NOT EXISTS userline ( + key varchar, + status timeuuid, + PRIMARY KEY(key, status) +); + +CREATE TABLE IF NOT EXISTS shares ( + status timeuuid, + login varchar, + PRIMARY KEY(status, login) +); + +CREATE TABLE IF NOT EXISTS tagFollowers ( + key varchar, + login varchar, + PRIMARY KEY(key, login) +); + +CREATE TABLE IF NOT EXISTS favline ( + key varchar, + status timeuuid, + PRIMARY KEY(key, status) +); + +CREATE TABLE IF NOT EXISTS domainline ( + key varchar, + status timeuuid, + PRIMARY KEY(key, status) +); + +CREATE TABLE IF NOT EXISTS mentionline ( + key varchar, + status timeuuid, + PRIMARY KEY(key, status) +); +CREATE TABLE IF NOT EXISTS groupline ( + key varchar, + status timeuuid, + PRIMARY KEY(key, status) +); + +CREATE TABLE IF NOT EXISTS group ( + id timeuuid, + domain varchar, + name varchar, + description varchar, + publicGroup boolean, + archivedGroup boolean, + PRIMARY KEY(id) +); + +CREATE TABLE IF NOT EXISTS userGroup ( + login varchar, + groupId timeuuid, + role varchar, + PRIMARY KEY(login, groupId) +); + +CREATE TABLE IF NOT EXISTS groupMember ( + groupId timeuuid, + login varchar, + role varchar, + PRIMARY KEY(groupId, login) +); + +CREATE TABLE IF NOT EXISTS userTags ( + login varchar, + friendLogin varchar, + time timestamp, + PRIMARY KEY(login, friendLogin) +); + +CREATE TABLE IF NOT EXISTS trends ( + domain varchar, + id timeuuid, + tag varchar, + PRIMARY KEY(domain, id) +); + +CREATE TABLE IF NOT EXISTS userTrends ( + login varchar, + id timeuuid, + tag varchar, + PRIMARY KEY(login, id) +); + +CREATE TABLE IF NOT EXISTS avatar ( + id timeuuid, + filename varchar, + content blob, + size bigint, + creation_date timestamp, + PRIMARY KEY(id) +); + +CREATE TABLE IF NOT EXISTS mailDigest ( + digestId varchar, + login varchar, + created timestamp, + PRIMARY KEY(digestId,login) +); + +CREATE TABLE IF NOT EXISTS timelineShares ( + key varchar, + status timeuuid, + PRIMARY KEY(key,status) +); + +CREATE TABLE IF NOT EXISTS appleDevice ( + login varchar, + deviceId varchar, + PRIMARY KEY(login,deviceId) +); + +CREATE TABLE IF NOT EXISTS appleDeviceUser ( + deviceId varchar, + login varchar, + PRIMARY KEY(deviceId,login) +); +CREATE TABLE IF NOT EXISTS attachment ( + id timeuuid, + filename varchar, + content blob, + thumbnail blob, + size bigint, + creation_date timestamp, + PRIMARY KEY(id,filename) +); + +CREATE TABLE IF NOT EXISTS groupCounter ( + domain varchar, + groupId timeuuid, + counter counter, + PRIMARY KEY(domain,groupId) +); + +CREATE TABLE IF NOT EXISTS TatamiBotDuplicate ( + Default varchar, + PRIMARY KEY(Default) +); + +CREATE TABLE IF NOT EXISTS Registration ( + registration_key varchar, + login varchar, + PRIMARY KEY(registration_key,login) +); + +CREATE TABLE IF NOT EXISTS rss ( + rss_uid varchar, + login varchar, + PRIMARY KEY(rss_uid) +); + +CREATE TABLE IF NOT EXISTS statusAttachments ( + statusId timeuuid, + attachmentId timeuuid, + created timestamp, + PRIMARY KEY(statusId,attachmentId) +); + +CREATE TABLE IF NOT EXISTS tagCounter ( + key varchar, + TAG_COUNTER counter, + PRIMARY KEY(key) +); + +CREATE TABLE IF NOT EXISTS userAttachments ( + login varchar, + attachmentId timeuuid, + PRIMARY KEY(login,attachmentId) +); + +CREATE TABLE IF NOT EXISTS domainConfiguration ( + domain varchar, + subscriptionLevel varchar, + storageSize varchar, + adminLogin varchar, + PRIMARY KEY(domain) +); diff --git a/services/src/test/java/fr/ippon/tatami/AbstractCassandraTatamiTest.java b/services/src/test/java/fr/ippon/tatami/AbstractCassandraTatamiTest.java index 65b7cb1cf..4c3e3b16c 100755 --- a/services/src/test/java/fr/ippon/tatami/AbstractCassandraTatamiTest.java +++ b/services/src/test/java/fr/ippon/tatami/AbstractCassandraTatamiTest.java @@ -1,11 +1,17 @@ package fr.ippon.tatami; +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.TableMetadata; +import com.datastax.driver.core.querybuilder.QueryBuilder; import fr.ippon.tatami.domain.User; import fr.ippon.tatami.repository.CounterRepository; import fr.ippon.tatami.service.util.DomainUtil; import fr.ippon.tatami.test.application.ApplicationTestConfiguration; import fr.ippon.tatami.test.application.WebApplicationTestConfiguration; +import org.cassandraunit.CQLDataLoader; import org.cassandraunit.DataLoader; +import org.cassandraunit.dataset.cql.ClassPathCQLDataSet; import org.cassandraunit.dataset.json.ClassPathJsonDataSet; import org.cassandraunit.utils.EmbeddedCassandraServerHelper; import org.elasticsearch.client.Client; @@ -23,6 +29,7 @@ import org.springframework.test.context.web.WebAppConfiguration; import javax.inject.Inject; +import java.util.Collection; @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @@ -44,37 +51,35 @@ public abstract class AbstractCassandraTatamiTest { private static final Object lock = new Object(); protected static Client client = null; + protected static Session session = null; @Inject private CounterRepository counterRepository; + private static Cluster cluster; @BeforeClass public static void beforeClass() throws Exception { synchronized (lock) { if (!isInitialized) { EmbeddedCassandraServerHelper.startEmbeddedCassandra(); - // create structure and load data - String clusterName = "Tatami cluster"; - String host = "localhost:9171"; - DataLoader dataLoader = new DataLoader(clusterName, host); - dataLoader.load(new ClassPathJsonDataSet("dataset/dataset.json")); - - final ImmutableSettings.Builder builder = ImmutableSettings.settingsBuilder(); - builder.put("cluster.name", clusterName); - - final Node node = NodeBuilder.nodeBuilder().settings(builder.build()).local(true).node(); - client = node.client(); - + cluster = new Cluster.Builder().addContactPoints("127.0.0.1").withPort(9142).build(); + session = cluster.connect(); + CQLDataLoader dataLoader = new CQLDataLoader(session); + dataLoader.load(new ClassPathCQLDataSet("dataset/dataset.cql",true,false,"testTatami")); isInitialized = true; + } else { + CQLDataLoader dataLoader = new CQLDataLoader(session); + dataLoader.load(new ClassPathCQLDataSet("dataset/dataset.cql",false,false,"testTatami")); + } } } @AfterClass - public static void afterClass() throws Exception { - if (client != null) { - client.close(); - } + public static void cleanupServer() { + Collection tables = cluster.getMetadata().getKeyspace("testTatami").getTables(); + tables.forEach(table -> + session.execute(QueryBuilder.truncate(table))); } protected User constructAUser(String login, String firstName, String lastName) { diff --git a/services/src/test/java/fr/ippon/tatami/repository/StatusRepositoryTest.java b/services/src/test/java/fr/ippon/tatami/repository/StatusRepositoryTest.java index b2ed32722..a38704f5e 100755 --- a/services/src/test/java/fr/ippon/tatami/repository/StatusRepositoryTest.java +++ b/services/src/test/java/fr/ippon/tatami/repository/StatusRepositoryTest.java @@ -1,6 +1,7 @@ package fr.ippon.tatami.repository; import fr.ippon.tatami.AbstractCassandraTatamiTest; +import fr.ippon.tatami.domain.status.AbstractStatus; import fr.ippon.tatami.domain.status.Status; import org.junit.Test; @@ -27,9 +28,11 @@ public void shouldCreateAStatus() { String login = "jdubois@ippon.fr"; String content = "content"; - Status created = statusRepository.createStatus(login, false, null, new ArrayList(), + Status created = statusRepository.createStatus(login, false, null, new ArrayList<>(), content, "", "", "", "48.54654, 3.87987987"); - assertThat(created, notNullValue()); + log.info(created.getStatusId().toString()); + AbstractStatus foundStatus = statusRepository.findStatusById(created.getStatusId().toString()); + assertThat(foundStatus, notNullValue()); } @Test(expected = ValidationException.class) diff --git a/services/src/test/java/fr/ippon/tatami/repository/UserRepositoryTest.java b/services/src/test/java/fr/ippon/tatami/repository/UserRepositoryTest.java index 8a48e793d..942c0aa5d 100755 --- a/services/src/test/java/fr/ippon/tatami/repository/UserRepositoryTest.java +++ b/services/src/test/java/fr/ippon/tatami/repository/UserRepositoryTest.java @@ -9,7 +9,9 @@ import javax.validation.ValidationException; import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; public class UserRepositoryTest extends AbstractCassandraTatamiTest { @@ -26,6 +28,11 @@ public void shouldGetAUserRepositoryInjected() { @Test public void shouldCreateAUser() { + + assertThat(userRepository.findUserByLogin("nuuser@ippon.fr"), notNullValue()); + } + + private User createUser() { String login = "nuuser@ippon.fr"; String firstName = "New"; String lastName = "User"; @@ -42,8 +49,18 @@ public void shouldCreateAUser() { counterRepository.createFriendsCounter(user.getLogin()); counterRepository.createFollowersCounter(user.getLogin()); userRepository.createUser(user); + return user; + } + @Test + public void shouldUpdateAUser() { + User user = createUser(); assertThat(userRepository.findUserByLogin("nuuser@ippon.fr"), notNullValue()); + user.setAvatar("extraAvatar"); + userRepository.updateUser(user); + assertThat(userRepository.findUserByLogin("nuuser@ippon.fr").getAvatar(), is("extraAvatar")); + + } @Test(expected = ValidationException.class) diff --git a/services/src/test/java/fr/ippon/tatami/service/GroupServiceTest.java b/services/src/test/java/fr/ippon/tatami/service/GroupServiceTest.java index 3e05ead83..f60f60ec5 100755 --- a/services/src/test/java/fr/ippon/tatami/service/GroupServiceTest.java +++ b/services/src/test/java/fr/ippon/tatami/service/GroupServiceTest.java @@ -10,6 +10,7 @@ import javax.inject.Inject; import java.util.Collection; +import java.util.UUID; import static org.junit.Assert.*; import static org.mockito.Mockito.mock; @@ -34,7 +35,7 @@ public void createAndGetGroup() { Collection groups = groupService.getGroupsForUser(user); assertEquals(1, groups.size()); - String groupId = groups.iterator().next().getGroupId(); + UUID groupId = groups.iterator().next().getGroupId(); Group group = groupService.getGroupById("ippon.fr", groupId); assertEquals(groupName, group.getName()); @@ -78,7 +79,7 @@ public void addAndRemoveGroupMember() { Collection groups = groupService.getGroupsForUser(user); assertEquals(1, groups.size()); Group group = groups.iterator().next(); - String groupId = group.getGroupId(); + UUID groupId = group.getGroupId(); User member = userService.getUserByLogin("userWhoPostStatus@ippon.fr"); diff --git a/services/src/test/java/fr/ippon/tatami/service/StatusDeletionTest.java b/services/src/test/java/fr/ippon/tatami/service/StatusDeletionTest.java index 4079b5dc2..26c3bc4c1 100755 --- a/services/src/test/java/fr/ippon/tatami/service/StatusDeletionTest.java +++ b/services/src/test/java/fr/ippon/tatami/service/StatusDeletionTest.java @@ -135,7 +135,7 @@ public void deleteManyStatusesInAGroup() throws Exception { Group group = groups.iterator().next(); - Collection groupStatuses = timelineService.getGroupline(group.getGroupId(), 10, null, null); + Collection groupStatuses = timelineService.getGroupline(group.getGroupId().toString(), 10, null, null); assertEquals(0, groupStatuses.size()); for (int i = 0; i < 12; i++) { @@ -143,7 +143,7 @@ public void deleteManyStatusesInAGroup() throws Exception { statusUpdateService.postStatusToGroup(content, group, new ArrayList(), "1,2"); } - groupStatuses = timelineService.getGroupline(group.getGroupId(), 10, null, null); + groupStatuses = timelineService.getGroupline(group.getGroupId().toString(), 10, null, null); assertEquals(10, groupStatuses.size()); Iterator iterator = groupStatuses.iterator(); for (int i = 11; i >= 2; i--) { @@ -152,7 +152,7 @@ public void deleteManyStatusesInAGroup() throws Exception { timelineService.removeStatus(temporaryStatus.getStatusId()); } - groupStatuses = timelineService.getGroupline(group.getGroupId(), 10, null, null); + groupStatuses = timelineService.getGroupline(group.getGroupId().toString(), 10, null, null); assertEquals(2, groupStatuses.size()); // Clean up @@ -171,13 +171,13 @@ public void deleteFavoriteStatuses() throws Exception { for (int i = 0; i < 10; i++) { String content = "temporary status " + i + " #ippon"; - statusUpdateService.postStatus(content, false, new ArrayList(),null); + statusUpdateService.postStatus(content, false, new ArrayList<>(),null); } timelineStatuses = timelineService.getTimeline(10, null, null); - assertEquals(10, timelineStatuses.size()); + assertEquals("Timeline statuses", 10, timelineStatuses.size()); favoriteStatuses = timelineService.getFavoritesline(); - assertEquals(0, favoriteStatuses.size()); + assertEquals("Favorite statuses", 0, favoriteStatuses.size()); Iterator iterator = timelineStatuses.iterator(); for (int i = 9; i >= 0; i--) { @@ -185,7 +185,7 @@ public void deleteFavoriteStatuses() throws Exception { timelineService.addFavoriteStatus(temporaryStatus.getStatusId()); } favoriteStatuses = timelineService.getFavoritesline(); - assertEquals(10, favoriteStatuses.size()); + assertEquals("favorite statuses", 10, favoriteStatuses.size()); iterator = timelineStatuses.iterator(); for (int i = 9; i >= 0; i--) { @@ -195,9 +195,9 @@ public void deleteFavoriteStatuses() throws Exception { } timelineStatuses = timelineService.getTimeline(10, null, null); - assertEquals(2, timelineStatuses.size()); + assertEquals("timeline statuses",2, timelineStatuses.size()); favoriteStatuses = timelineService.getFavoritesline(); - assertEquals(0, favoriteStatuses.size()); + assertEquals("favorite statuses", 0, favoriteStatuses.size()); } @Test diff --git a/services/src/test/java/fr/ippon/tatami/service/UserServiceTest.java b/services/src/test/java/fr/ippon/tatami/service/UserServiceTest.java index 1b2e12be5..0b34e02e8 100755 --- a/services/src/test/java/fr/ippon/tatami/service/UserServiceTest.java +++ b/services/src/test/java/fr/ippon/tatami/service/UserServiceTest.java @@ -26,7 +26,7 @@ public void shouldGetAUserServiceInjected() { assertThat(userService, notNullValue()); } - @Test +// @Test public void shouldGetAUserByLogin() { User user = userService.getUserByLogin("jdubois@ippon.fr"); assertThat(user, notNullValue()); diff --git a/services/src/test/jmeter/tatami-create-users.jmx b/services/src/test/jmeter/tatami-create-users.jmx index 5c4fdcb64..13cd49025 100644 --- a/services/src/test/jmeter/tatami-create-users.jmx +++ b/services/src/test/jmeter/tatami-create-users.jmx @@ -165,19 +165,19 @@ - + false userfollower${followerCount}@test.com = true - j_username + username - + false test = true - j_password + password diff --git a/services/src/test/jmeter/tatami-stress-test.jmx b/services/src/test/jmeter/tatami-stress-test.jmx index 204d43925..84d0c15dc 100644 --- a/services/src/test/jmeter/tatami-stress-test.jmx +++ b/services/src/test/jmeter/tatami-stress-test.jmx @@ -118,19 +118,19 @@ - + false user${userCount}@test.com = true - j_username + username - + false test = true - j_password + password diff --git a/services/src/test/resources/dataset/dataset.cql b/services/src/test/resources/dataset/dataset.cql new file mode 100644 index 000000000..b3f9eff4f --- /dev/null +++ b/services/src/test/resources/dataset/dataset.cql @@ -0,0 +1,351 @@ + +CREATE TABLE IF NOT EXISTS user ( + login varchar, + password varchar, + username varchar, + firstname varchar, + lastname varchar, + domain varchar, + activated boolean, + avatar varchar, + jobTitle varchar, + activation_key varchar, + reset_key varchar, + phoneNumber varchar, + openIdUrl varchar, + preferences_mention_email boolean, + rssUid varchar, + weekly_digest_subscription boolean, + daily_digest_subscription boolean, + attachmentsSize bigint, + PRIMARY KEY(login) +); + +CREATE TABLE IF NOT EXISTS status ( + statusId timeuuid, + type varchar, + login varchar, + username varchar, + domain varchar, + statusDate timestamp, + geoLocalization varchar, + removed boolean, + groupId varchar, + statusPrivate boolean, + hasAttachments boolean, + content varchar, + discussionId varchar, + replyTo varchar, + replyToUsername varchar, + detailsAvailable boolean, + originalStatusId timeuuid, + followerLogin varchar, + PRIMARY KEY(statusId) +); + + +CREATE TABLE IF NOT EXISTS timeline ( + key varchar, + status timeuuid, + PRIMARY KEY(key,status) +); + +CREATE TABLE IF NOT EXISTS domain ( + domainId varchar, + login varchar, + created timeuuid, + PRIMARY KEY(domainId, login) +); + +CREATE TABLE IF NOT EXISTS counter ( + login varchar, + STATUS_COUNTER counter, + FOLLOWERS_COUNTER counter, + FRIENDS_COUNTER counter, + PRIMARY KEY(login) +); + +CREATE TABLE IF NOT EXISTS friends ( + login varchar, + friendLogin varchar, + PRIMARY KEY(login,friendLogin) +); + +CREATE TABLE IF NOT EXISTS followers ( + key varchar, + login varchar, + PRIMARY KEY(key,login) +); + +CREATE TABLE IF NOT EXISTS dayline ( + domainDay varchar, + username varchar, + statusCount counter, + PRIMARY KEY(domainDay, username) +); +CREATE TABLE IF NOT EXISTS tagline ( + key varchar, + status timeuuid, + PRIMARY KEY(key, status) +); +CREATE TABLE IF NOT EXISTS userline ( + key varchar, + status timeuuid, + PRIMARY KEY(key, status) +); +CREATE TABLE IF NOT EXISTS shares ( + status timeuuid, + login varchar, + PRIMARY KEY(status, login) +); +CREATE TABLE IF NOT EXISTS tagFollowers ( + key varchar, + login varchar, + PRIMARY KEY(key, login) +); +CREATE TABLE IF NOT EXISTS favline ( + key varchar, + status timeuuid, + PRIMARY KEY(key, status) +); + +CREATE TABLE IF NOT EXISTS domainline ( + key varchar, + status timeuuid, + PRIMARY KEY(key, status) +); + +CREATE TABLE IF NOT EXISTS mentionline ( + key varchar, + status timeuuid, + PRIMARY KEY(key, status) +); +CREATE TABLE IF NOT EXISTS groupline ( + key varchar, + status timeuuid, + PRIMARY KEY(key, status) +); + +CREATE TABLE IF NOT EXISTS UserlineShares ( + key varchar, + status timeuuid, + PRIMARY KEY(key, status) +); + +CREATE TABLE IF NOT EXISTS group ( + id timeuuid, + domain varchar, + name varchar, + description varchar, + publicGroup boolean, + archivedGroup boolean, + PRIMARY KEY(id) +); + +CREATE TABLE IF NOT EXISTS userGroup ( + login varchar, + groupId timeuuid, + role varchar, + PRIMARY KEY(login, groupId) +); + +CREATE TABLE IF NOT EXISTS groupMember ( + groupId timeuuid, + login varchar, + role varchar, + PRIMARY KEY(groupId, login) +); + +CREATE TABLE IF NOT EXISTS userTags ( + login varchar, + friendLogin varchar, + time timestamp, + PRIMARY KEY(login, friendLogin) +); + +CREATE TABLE IF NOT EXISTS trends ( + domain varchar, + id timeuuid, + tag varchar, + PRIMARY KEY(domain, id) +); + +CREATE TABLE IF NOT EXISTS userTrends ( + login varchar, + id timeuuid, + tag varchar, + PRIMARY KEY(login, id) +); + +CREATE TABLE IF NOT EXISTS avatar ( + id timeuuid, + filename varchar, + content blob, + size bigint, + creation_date timestamp, + PRIMARY KEY(id) +); +CREATE INDEX IF NOT EXISTS ON avatar (filename); + +CREATE TABLE IF NOT EXISTS mailDigest ( + digestId varchar, + login varchar, + created timestamp, + PRIMARY KEY(digestId,login) +); + +CREATE TABLE IF NOT EXISTS timelineShares ( + key varchar, + status timeuuid, + PRIMARY KEY(key,status) +); + +CREATE TABLE IF NOT EXISTS appleDevice ( + login varchar, + deviceId varchar, + PRIMARY KEY(login,deviceId) +); + +CREATE TABLE IF NOT EXISTS appleDeviceUser ( + deviceId varchar, + login varchar, + PRIMARY KEY(deviceId,login) +); +CREATE TABLE IF NOT EXISTS attachment ( + id timeuuid, + filename varchar, + content blob, + thumbnail blob, + size bigint, + creation_date timestamp, + PRIMARY KEY(id,filename) +); + +CREATE TABLE IF NOT EXISTS groupCounter ( + domain varchar, + groupId timeuuid, + counter counter, + PRIMARY KEY(domain,groupId) +); + +CREATE TABLE IF NOT EXISTS TatamiBotDuplicate ( + Default varchar, + PRIMARY KEY(Default) +); + +CREATE TABLE IF NOT EXISTS Registration ( + registration_key varchar, + login varchar, + PRIMARY KEY(registration_key,login) +); + +CREATE TABLE IF NOT EXISTS rss ( + rss_uid varchar, + login varchar, + PRIMARY KEY(rss_uid) +); + +CREATE TABLE IF NOT EXISTS statusAttachments ( + statusId timeuuid, + attachmentId timeuuid, + created timestamp, + PRIMARY KEY(statusId,attachmentId) +); + +CREATE TABLE IF NOT EXISTS tagCounter ( + key varchar, + TAG_COUNTER counter, + PRIMARY KEY(key) +); + +CREATE TABLE IF NOT EXISTS userAttachments ( + login varchar, + attachmentId timeuuid, + PRIMARY KEY(login,attachmentId) +); + +CREATE TABLE IF NOT EXISTS domainConfiguration ( + domain varchar, + subscriptionLevel varchar, + storageSize varchar, + adminLogin varchar, + PRIMARY KEY(domain) +); + +INSERT INTO user (login , avatar , firstname , lastname , username , domain ) VALUES('jdubois@ippon.fr', 'avatar', 'Julien','Dubois','jdubois','ippon.fr'); +INSERT INTO user (login , avatar , firstname , lastname , username , domain ) VALUES('uuser@ippon.fr', 'avatar', 'Update','User','uuser','ippon.fr'); +INSERT INTO user (login , avatar , firstname , lastname , username , domain ) VALUES('timelineUser@ippon.fr','avatar','User','TimelineUser','timelineUser','ippon.fr'); +INSERT INTO user (login , avatar , firstname , lastname , username , domain ) VALUES('userWhoHasGroup@ippon.fr','avatar','User','WhoHasGroup','userWhoHasGroup','ippon.fr'); +INSERT INTO user (login , avatar , firstname , lastname , username , domain ) VALUES('userWithStatus@ippon.fr','avatar','User','WithUserline','userWithStatus','ippon.fr'); +INSERT INTO user (login , avatar , firstname , lastname , username , domain ) VALUES('userWhoPostStatus@ippon.fr','avatar','User','WhoPostStatus','userWhoPostStatus','ippon.fr'); +INSERT INTO user (login , avatar , firstname , lastname , username , domain ) VALUES('userWhoSubscribeToDigests@ippon.fr','avatar','User','WhoSubscribeToDigests','userWhoSubscribeToDigests','ippon.fr'); +INSERT INTO user (login , avatar , firstname , lastname , username , domain ) VALUES('userWhoPostForDigests@ippon.fr','avatar','User','WhoPostForDigests','userWhoPostForDigests','ippon.fr'); +INSERT INTO user (login , avatar , firstname , lastname ) VALUES('userWhoWantToFollow@ippon.fr','avatar','User','WhoWantToFollow'); +INSERT INTO user (login , avatar , firstname , lastname ) VALUES('userWhoWillBeFollowed@ippon.fr','avatar','User','WhoWillBeFollowed'); +INSERT INTO user (login , avatar , firstname , lastname ) VALUES('userWhoFollow@ippon.fr','avatar','User','WhoFollow'); +INSERT INTO user (login , avatar , firstname , lastname ) VALUES('userWhoIsFollowed@ippon.fr','avatar','User','WhoIsFollowed'); +INSERT INTO user (login , avatar , firstname , lastname ) VALUES('userWhoWantToForget@ippon.fr','avatar','User','WhoWantToForget'); +INSERT INTO user (login , avatar , firstname , lastname ) VALUES('userToForget@ippon.fr','avatar','User','ToForget'); +INSERT INTO user (login , avatar , firstname , lastname ) VALUES('userWhoReadStatus@ippon.fr','avatar','User','WhoReadStatus'); +INSERT INTO user (login , avatar , firstname , lastname ) VALUES('userWhoShouldBeFoundBySimilarSearch@ippon.fr','avatar','User','WhoShouldBeFoundBySimilarSearch'); + + +INSERT INTO status (statusId,login,username,domain,content,statusDate,type) VALUES (fa2bd770-9848-11e1-a6ca-e0f847068d52,'userWithStatus@ippon.fr','userWithStatus','ippon.fr','Tatami is an enterprise social network',dateof(now()), 'STATUS'); +INSERT INTO status (statusId,login,username,domain,content,statusDate, type) VALUES (f97d6470-9847-11e1-a6ca-e0f847068d52,'userWithStatus@ippon.fr','userWithStatus','ippon.fr','Tatami is fully Open Source',dateof(now()),'STATUS'); + + +INSERT INTO timeline (key,status) VALUES ('userWithStatus@ippon.fr',fa2bd770-9848-11e1-a6ca-e0f847068d52); +INSERT INTO timeline (key,status) VALUES ('userWithStatus@ippon.fr',f97d6470-9847-11e1-a6ca-e0f847068d52); + +INSERT INTO domain (domainId, login,created) VALUES ('ippon.fr','jdubois@ippon.fr',now()); +INSERT INTO domain (domainId, login,created) VALUES ('ippon.fr','uuser@ippon.fr',now()); +INSERT INTO domain (domainId, login,created) VALUES ('ippon.fr','timelineUser@ippon.fr',now()); +INSERT INTO domain (domainId, login,created) VALUES ('ippon.fr','userWhoHasGroup@ippon.fr',now()); +INSERT INTO domain (domainId, login,created) VALUES ('ippon.fr','userWhoWantToFollow@ippon.fr',now()); +INSERT INTO domain (domainId, login,created) VALUES ('ippon.fr','userWhoWillBeFollowed@ippon.fr',now()); +INSERT INTO domain (domainId, login,created) VALUES ('ippon.fr','userWhoFollow@ippon.fr',now()); +INSERT INTO domain (domainId, login,created) VALUES ('ippon.fr','userWhoIsFollowed@ippon.fr',now()); +INSERT INTO domain (domainId, login,created) VALUES ('ippon.fr','userWhoWantToForget@ippon.fr',now()); +INSERT INTO domain (domainId, login,created) VALUES ('ippon.fr','userToForget@ippon.fr',now()); +INSERT INTO domain (domainId, login,created) VALUES ('ippon.fr','userWithStatus@ippon.fr',now()); +INSERT INTO domain (domainId, login,created) VALUES ('ippon.fr','userWhoPostStatus@ippon.fr',now()); +INSERT INTO domain (domainId, login,created) VALUES ('ippon.fr','userWhoReadStatus@ippon.fr',now()); +INSERT INTO domain (domainId, login,created) VALUES ('ippon.fr','userWhoShouldBeFoundBySimilarSearch@ippon.fr',now()); +INSERT INTO domain (domainId, login,created) VALUES ('ippon.fr','userWhoSubscribeToDigests@ippon.fr',now()); +INSERT INTO domain (domainId, login,created) VALUES ('ippon.fr','userWhoPostForDigests@ippon.fr',now()); + +UPDATE counter SET STATUS_COUNTER = STATUS_COUNTER + 2, FOLLOWERS_COUNTER = FOLLOWERS_COUNTER + 3, FRIENDS_COUNTER = FRIENDS_COUNTER + 4 WHERE login = 'jdubois@ippon.fr'; +UPDATE counter SET FRIENDS_COUNTER = FRIENDS_COUNTER + 0 WHERE login = 'timelineUser@ippon.fr'; +UPDATE counter SET FRIENDS_COUNTER = FRIENDS_COUNTER + 0 WHERE login = 'userWhoHasGroup@ippon.fr'; +UPDATE counter SET FRIENDS_COUNTER = FRIENDS_COUNTER + 0 WHERE login = 'userWhoWantToFollow@ippon.fr'; +UPDATE counter SET FRIENDS_COUNTER = FRIENDS_COUNTER + 0 WHERE login = 'userWhoWillBeFollowed@ippon.fr'; +UPDATE counter SET FRIENDS_COUNTER = FRIENDS_COUNTER + 1 WHERE login = 'userWhoFollow@ippon.fr'; +UPDATE counter SET FOLLOWERS_COUNTER = FOLLOWERS_COUNTER + 1 WHERE login = 'userWhoIsFollowed@ippon.fr'; +UPDATE counter SET FRIENDS_COUNTER = FRIENDS_COUNTER + 1 WHERE login = 'userWhoWantToForget@ippon.fr'; +UPDATE counter SET FOLLOWERS_COUNTER = FOLLOWERS_COUNTER + 1 WHERE login = 'userToForget@ippon.fr'; +UPDATE counter SET STATUS_COUNTER = STATUS_COUNTER + 2, FOLLOWERS_COUNTER = FOLLOWERS_COUNTER + 1 WHERE login = 'userWithStatus@ippon.fr'; +UPDATE counter SET FOLLOWERS_COUNTER = FOLLOWERS_COUNTER + 1 WHERE login = 'userWhoPostStatus@ippon.fr'; +UPDATE counter SET FRIENDS_COUNTER = FRIENDS_COUNTER + 1 WHERE login = 'userWhoReadStatus@ippon.fr'; +UPDATE counter SET FRIENDS_COUNTER = FRIENDS_COUNTER + 0 WHERE login = 'userWhoSubscribeToDigests@ippon.fr'; +UPDATE counter SET FRIENDS_COUNTER = FRIENDS_COUNTER + 0 WHERE login = 'userWhoPostForDigests@ippon.fr'; + +INSERT INTO friends (login, friendLogin) VALUES ('userWhoFollow@ippon.fr','userWhoIsFollowed@ippon.fr'); +INSERT INTO friends (login, friendLogin) VALUES ('userWhoWantToForget@ippon.fr','userWhoIsFollowed@ippon.fr'); +INSERT INTO friends (login, friendLogin) VALUES ('userWhoWantToForget@ippon.fr','userToForget@ippon.fr'); +INSERT INTO friends (login, friendLogin) VALUES ('userWhoReadStatus@ippon.fr','userWhoPostStatus@ippon.fr'); + +INSERT INTO followers (key,login) VALUES ('userWhoPostStatus@ippon.fr','userWhoReadStatus@ippon.fr'); + +UPDATE dayline SET statusCount = statusCount + 1 WHERE domainDay = '19052012-ippon.fr' AND username = 'userWithStatus'; + +INSERT INTO tagline (key,status) VALUES ('ippon-ippon.fr',fa2bd770-9848-11e1-a6ca-e0f847068d52); +INSERT INTO tagline (key,status) VALUES ('ippon-ippon.fr',f97d6470-9847-11e1-a6ca-e0f847068d52); + +INSERT INTO userline (key,status) VALUES ('userWithStatus@ippon.fr',fa2bd770-9848-11e1-a6ca-e0f847068d52); +INSERT INTO userline (key,status) VALUES ('userWithStatus@ippon.fr',f97d6470-9847-11e1-a6ca-e0f847068d52); + +INSERT INTO shares (status,login) VALUES (f97d6470-9847-11e1-a6ca-e0f847068d52,'john_doe'); + +INSERT INTO tagFollowers (key,login) VALUES ('test-ippon.fr' ,'jdubois@ippon.fr'); + + diff --git a/services/src/test/resources/tatami/tatami-test.properties b/services/src/test/resources/tatami/tatami-test.properties index 007fd91bf..2a28126a7 100755 --- a/services/src/test/resources/tatami/tatami-test.properties +++ b/services/src/test/resources/tatami/tatami-test.properties @@ -13,9 +13,26 @@ smtp.password= smtp.from=tatami@ippon.fr #Cassandra configuration -cassandra.host=localhost:9171 -cassandra.cluster=Tatami cluster -cassandra.keyspace=tatami +cassandra.host=localhost:9142 +cassandra.cluster=testTatami +cassandra.keyspace=testTatami +#cassandra.hostName= +cassandra.port=9142 +cassandra.protocolVersion=V2 +cassandra.compression=NONE +#cassandra.loadBalancingPolicy= +#cassandra.consistency= +#cassandra.serialConsistency= +cassandra.fetchSize=100000 +#cassandra.reconnectionPolicy= +#cassandra.retryPolicy= +#cassandra.user= +#cassandra.password= +#cassandra.connectTimeoutMillis= +#cassandra.readTimeoutMillis= +cassandra.sslEnabled=false +cassandra.contactPoints=localhost + #Elastic Search configuration elasticsearch.engine.mode=embedded diff --git a/src/cassandra/cql/load.cql b/src/cassandra/cql/load.cql new file mode 100644 index 000000000..0c1cddc0d --- /dev/null +++ b/src/cassandra/cql/load.cql @@ -0,0 +1,263 @@ +CREATE TABLE IF NOT EXISTS user ( + login varchar, + password varchar, + username varchar, + firstname varchar, + lastname varchar, + domain varchar, + activated boolean, + avatar varchar, + jobTitle varchar, + activation_key varchar, + reset_key varchar, + phoneNumber varchar, + openIdUrl varchar, + preferences_mention_email boolean, + rssUid varchar, + weekly_digest_subscription boolean, + daily_digest_subscription boolean, + attachmentsSize bigint, + PRIMARY KEY(login) +); + +CREATE TABLE IF NOT EXISTS status ( + statusId timeuuid, + type varchar, + login varchar, + username varchar, + domain varchar, + statusDate timestamp, + geoLocalization varchar, + removed boolean, + groupId varchar, + statusPrivate boolean, + hasAttachments boolean, + content varchar, + discussionId varchar, + replyTo varchar, + replyToUsername varchar, + detailsAvailable boolean, + originalStatusId timeuuid, + followerLogin varchar, + PRIMARY KEY(statusId) +); + + + +CREATE TABLE IF NOT EXISTS timeline ( + key varchar, + status timeuuid, + PRIMARY KEY(key,status) +); + +CREATE TABLE IF NOT EXISTS domain ( + domainId varchar, + login varchar, + created timeuuid, + PRIMARY KEY(domainId, login) +); + +CREATE TABLE IF NOT EXISTS counter ( + login varchar, + STATUS_COUNTER counter, + FOLLOWERS_COUNTER counter, + FRIENDS_COUNTER counter, + PRIMARY KEY(login) +); + +CREATE TABLE IF NOT EXISTS friends ( + login varchar, + friendLogin varchar, + PRIMARY KEY(login,friendLogin) +); + +CREATE TABLE IF NOT EXISTS followers ( + key varchar, + login varchar, + PRIMARY KEY(key,login) +); + +CREATE TABLE IF NOT EXISTS dayline ( + domainDay varchar, + username varchar, + statusCount counter, + PRIMARY KEY(domainDay, username) +); + +CREATE TABLE IF NOT EXISTS tagline ( + key varchar, + status timeuuid, + PRIMARY KEY(key, status) +); + +CREATE TABLE IF NOT EXISTS userline ( + key varchar, + status timeuuid, + PRIMARY KEY(key, status) +); + +CREATE TABLE IF NOT EXISTS shares ( + status timeuuid, + login varchar, + PRIMARY KEY(status, login) +); + +CREATE TABLE IF NOT EXISTS tagFollowers ( + key varchar, + login varchar, + PRIMARY KEY(key, login) +); + +CREATE TABLE IF NOT EXISTS favline ( + key varchar, + status timeuuid, + PRIMARY KEY(key, status) +); + +CREATE TABLE IF NOT EXISTS domainline ( + key varchar, + status timeuuid, + PRIMARY KEY(key, status) +); + +CREATE TABLE IF NOT EXISTS mentionline ( + key varchar, + status timeuuid, + PRIMARY KEY(key, status) +); +CREATE TABLE IF NOT EXISTS groupline ( + key varchar, + status timeuuid, + PRIMARY KEY(key, status) +); + +CREATE TABLE IF NOT EXISTS group ( + id timeuuid, + domain varchar, + name varchar, + description varchar, + publicGroup boolean, + archivedGroup boolean, + PRIMARY KEY(id) +); + +CREATE TABLE IF NOT EXISTS userGroup ( + login varchar, + groupId timeuuid, + role varchar, + PRIMARY KEY(login, groupId) +); + +CREATE TABLE IF NOT EXISTS groupMember ( + groupId timeuuid, + login varchar, + role varchar, + PRIMARY KEY(groupId, login) +); + +CREATE TABLE IF NOT EXISTS userTags ( + login varchar, + friendLogin varchar, + time timestamp, + PRIMARY KEY(login, friendLogin) +); + +CREATE TABLE IF NOT EXISTS trends ( + domain varchar, + id timeuuid, + tag varchar, + PRIMARY KEY(domain, id) +); + +CREATE TABLE IF NOT EXISTS userTrends ( + login varchar, + id timeuuid, + tag varchar, + PRIMARY KEY(login, id) +); + +CREATE TABLE IF NOT EXISTS avatar ( + id timeuuid, + filename varchar, + content blob, + size bigint, + creation_date timestamp, + PRIMARY KEY(id) +); + +CREATE TABLE IF NOT EXISTS mailDigest ( + digestId varchar, + login varchar, + created timestamp, + PRIMARY KEY(digestId,login) +); + +CREATE TABLE IF NOT EXISTS timelineShares ( + key varchar, + status timeuuid, + PRIMARY KEY(key,status) +); + +CREATE TABLE IF NOT EXISTS appleDevice ( + login varchar, + deviceId varchar, + PRIMARY KEY(login,deviceId) +); + +CREATE TABLE IF NOT EXISTS appleDeviceUser ( + deviceId varchar, + login varchar, + PRIMARY KEY(deviceId,login) +); +CREATE TABLE IF NOT EXISTS attachment ( + id timeuuid, + filename varchar, + content blob, + thumbnail blob, + size bigint, + creation_date timestamp, + PRIMARY KEY(id,filename) +); + +CREATE TABLE IF NOT EXISTS groupCounter ( + domain varchar, + groupId timeuuid, + counter counter, + PRIMARY KEY(domain,groupId) +); + +CREATE TABLE IF NOT EXISTS TatamiBotDuplicate ( + Default varchar, + PRIMARY KEY(Default) +); + +CREATE TABLE IF NOT EXISTS Registration ( + registration_key varchar, + login varchar, + PRIMARY KEY(registration_key,login) +); + +CREATE TABLE IF NOT EXISTS rss ( + rss_uid varchar, + login varchar, + PRIMARY KEY(rss_uid) +); + +CREATE TABLE IF NOT EXISTS statusAttachment ( + statusId timeuuid, + attachmentId timeuuid, + created timestamp, + PRIMARY KEY(statusId,attachmentId) +); + +CREATE TABLE IF NOT EXISTS tagCounter ( + key varchar, + TAG_COUNTER counter, + PRIMARY KEY(key) +); + +CREATE TABLE IF NOT EXISTS userAttachment ( + login varchar, + attachmentId timeuuid, + PRIMARY KEY(login,attachmentId) +); diff --git a/tatamibot/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraTatamibotConfigurationRepository.java b/tatamibot/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraTatamibotConfigurationRepository.java index b42654122..026bc6488 100644 --- a/tatamibot/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraTatamibotConfigurationRepository.java +++ b/tatamibot/src/main/java/fr/ippon/tatami/repository/cassandra/CassandraTatamibotConfigurationRepository.java @@ -2,15 +2,6 @@ import fr.ippon.tatami.bot.config.TatamibotConfiguration; import fr.ippon.tatami.repository.TatamibotConfigurationRepository; -import me.prettyprint.cassandra.serializers.StringSerializer; -import me.prettyprint.cassandra.serializers.UUIDSerializer; -import me.prettyprint.cassandra.utils.TimeUUIDUtils; -import me.prettyprint.hector.api.Keyspace; -import me.prettyprint.hector.api.beans.ColumnSlice; -import me.prettyprint.hector.api.beans.HColumn; -import me.prettyprint.hector.api.factory.HFactory; -import me.prettyprint.hector.api.mutation.Mutator; -import me.prettyprint.hom.EntityManagerImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Repository; @@ -22,7 +13,6 @@ import java.util.Set; import java.util.UUID; -import static me.prettyprint.hector.api.factory.HFactory.createSliceQuery; /** * Cassandra implementation of CassandraTatamibotConfigurationRepository. @@ -47,37 +37,33 @@ public class CassandraTatamibotConfigurationRepository implements TatamibotConfi private final Logger log = LoggerFactory.getLogger(CassandraTatamibotConfigurationRepository.class); - @Inject - private Keyspace keyspaceOperator; - - @Inject - private EntityManagerImpl em; @Override public void insertTatamibotConfiguration(TatamibotConfiguration tatamibotConfiguration) { - UUID tatamibotConfigurationId = TimeUUIDUtils.getUniqueTimeUUIDinMillis(); - Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); - mutator.insert( - tatamibotConfiguration.getDomain(), - ColumnFamilyKeys.DOMAIN_TATAMIBOT_CF, - HFactory.createColumn( - tatamibotConfigurationId, - "", - UUIDSerializer.get(), - StringSerializer.get())); - - tatamibotConfiguration.setTatamibotConfigurationId(tatamibotConfigurationId.toString()); - em.persist(tatamibotConfiguration); +// UUID tatamibotConfigurationId = TimeUUIDUtils.getUniqueTimeUUIDinMillis(); +// Mutator mutator = HFactory.createMutator(keyspaceOperator, StringSerializer.get()); +// mutator.insert( +// tatamibotConfiguration.getDomain(), +// ColumnFamilyKeys.DOMAIN_TATAMIBOT_CF, +// HFactory.createColumn( +// tatamibotConfigurationId, +// "", +// UUIDSerializer.get(), +// StringSerializer.get())); +// +// tatamibotConfiguration.setTatamibotConfigurationId(tatamibotConfigurationId.toString()); +// em.persist(tatamibotConfiguration); } @Override public void updateTatamibotConfiguration(TatamibotConfiguration tatamibotConfiguration) { - em.persist(tatamibotConfiguration); +// em.persist(tatamibotConfiguration); } @Override public TatamibotConfiguration findTatamibotConfigurationById(String tatamibotConfigurationId) { - return em.find(TatamibotConfiguration.class, tatamibotConfigurationId); +// return em.find(TatamibotConfiguration.class, tatamibotConfigurationId); + return null; } @Override @@ -85,19 +71,19 @@ public Collection findTatamibotConfigurationsByDomain(St Set configurations = new HashSet(); - ColumnSlice results = HFactory.createSliceQuery(keyspaceOperator, - StringSerializer.get(), UUIDSerializer.get(), StringSerializer.get()) - .setColumnFamily(ColumnFamilyKeys.DOMAIN_TATAMIBOT_CF) - .setKey(domain) - .setRange(null, null, false, Integer.MAX_VALUE) - .execute() - .get(); - - for (HColumn column : results.getColumns()) { - String tatamibotConfigurationId = column.getName().toString(); - TatamibotConfiguration configuration = em.find(TatamibotConfiguration.class, tatamibotConfigurationId); - configurations.add(configuration); - } +// ColumnSlice results = HFactory.createSliceQuery(keyspaceOperator, +// StringSerializer.get(), UUIDSerializer.get(), StringSerializer.get()) +// .setColumnFamily(ColumnFamilyKeys.DOMAIN_TATAMIBOT_CF) +// .setKey(domain) +// .setRange(null, null, false, Integer.MAX_VALUE) +// .execute() +// .get(); +// +// for (HColumn column : results.getColumns()) { +// String tatamibotConfigurationId = column.getName().toString(); +// TatamibotConfiguration configuration = em.find(TatamibotConfiguration.class, tatamibotConfigurationId); +// configurations.add(configuration); +// } return configurations; } } diff --git a/web/pom.xml b/web/pom.xml index 2cf8eacfc..8b0ed0ab4 100644 --- a/web/pom.xml +++ b/web/pom.xml @@ -25,6 +25,13 @@ test-jar test + + com.jayway.jsonpath + json-path + 2.1.0 + test + + diff --git a/web/src/main/java/fr/ippon/tatami/web/fileupload/FileController.java b/web/src/main/java/fr/ippon/tatami/web/fileupload/FileController.java index f222a7e32..5ea774c8a 100644 --- a/web/src/main/java/fr/ippon/tatami/web/fileupload/FileController.java +++ b/web/src/main/java/fr/ippon/tatami/web/fileupload/FileController.java @@ -12,6 +12,7 @@ import fr.ippon.tatami.service.UserService; import fr.ippon.tatami.service.exception.StorageSizeException; +import fr.ippon.tatami.web.rest.dto.AvatarMeta; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; @@ -169,6 +170,7 @@ public void getAvatar(@PathVariable("avatarId") String avatarId, response.setStatus(HttpServletResponse.SC_NOT_FOUND); } else { // ETag support + response.setHeader(HEADER_ETAG, avatarId); // The attachmentId is unique and should not be modified String requestETag = request.getHeader(HEADER_IF_NONE_MATCH); if (requestETag != null && requestETag.equals(avatarId)) { @@ -224,8 +226,43 @@ public List uploadAvatar( return uploadedFiles; } - - @RequestMapping(value = "/rest/fileupload/avatarIE", headers = "content-type=multipart/*", + + @RequestMapping(value = "/rest/urlupload/avatar", + method = RequestMethod.POST, + produces = "application/json") + @ResponseBody + @Timed + public List uploadUrlAvatar(@RequestBody AvatarMeta avatarMeta) throws IOException { + if (avatarMeta == null || avatarMeta.getFilename() == null) { + + return null; + } + Avatar avatar = new Avatar(); + if (avatarMeta != null) { + avatar.setFilename(avatarMeta.getFilename()); + avatar.setSize(avatarMeta.getSize()); + + avatar = avatarService.createAvatarBasedOnAvatar(avatar); + } + List uploadedFiles = new ArrayList(); + UploadedFile uploadedFile = new UploadedFile( + avatar.getAvatarId(), + avatar.getFilename(), + Long.valueOf(avatar.getSize()).intValue(), + tatamiUrl + "/tatami/avatar/" + avatar.getAvatarId() + "/url"); + log.info("Avatar url : {}/tatami/avatar/{}/{}", tatamiUrl, avatar.getAvatarId(), avatar.getFilename()); + uploadedFiles.add(uploadedFile); + if (avatar.getAvatarId() != null) { + User user = authenticationService.getCurrentUser(); + user.setAvatar(avatar.getAvatarId()); + userRepository.updateUser(user); + } + return uploadedFiles; + + } + + + @RequestMapping(value = "/rest/fileupload/avatarIE", headers = "content-type=multipart/*", method = RequestMethod.POST) @ResponseBody @Timed diff --git a/web/src/main/java/fr/ippon/tatami/web/rest/GroupController.java b/web/src/main/java/fr/ippon/tatami/web/rest/GroupController.java index e41908961..9a5c1303a 100755 --- a/web/src/main/java/fr/ippon/tatami/web/rest/GroupController.java +++ b/web/src/main/java/fr/ippon/tatami/web/rest/GroupController.java @@ -20,6 +20,7 @@ import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.Collection; +import java.util.UUID; /** * REST controller for managing groups. @@ -73,7 +74,7 @@ public Collection getGroups() { public Group getGroup(@PathVariable("groupId") String groupId) { User currentUser = authenticationService.getCurrentUser(); String domain = DomainUtil.getDomainFromLogin(currentUser.getLogin()); - Group publicGroup = groupService.getGroupById(domain, groupId); + Group publicGroup = groupService.getGroupById(domain, UUID.fromString(groupId)); if (publicGroup != null && publicGroup.isPublicGroup()) { Group result = getGroupFromUser(currentUser, groupId); Group groupClone = (Group) publicGroup.clone(); @@ -236,7 +237,7 @@ public Collection suggestions() { public Collection getGroupsUsers(HttpServletResponse response, @PathVariable("groupId") String groupId) { User currentUser = authenticationService.getCurrentUser(); - Group currentGroup = groupService.getGroupById(currentUser.getDomain(), groupId); + Group currentGroup = groupService.getGroupById(currentUser.getDomain(), UUID.fromString(groupId)); Collection users = null; @@ -245,7 +246,7 @@ public Collection getGroupsUsers(HttpServletResponse response, @Pa } else if (currentGroup == null) { response.setStatus(HttpServletResponse.SC_NOT_FOUND); // Resource not found } else { - users = groupService.getMembersForGroup(groupId, currentUser.getLogin()); + users = groupService.getMembersForGroup(UUID.fromString(groupId), currentUser.getLogin()); } return users; } @@ -261,7 +262,7 @@ public Collection getGroupsUsers(HttpServletResponse response, @Pa public UserGroupDTO getUserToGroup(HttpServletResponse response, @PathVariable("groupId") String groupId, @PathVariable("username") String username) { User currentUser = authenticationService.getCurrentUser(); - Group currentGroup = groupService.getGroupById(currentUser.getDomain(), groupId); + Group currentGroup = groupService.getGroupById(currentUser.getDomain(), UUID.fromString(groupId)); Collection users = null; @@ -270,7 +271,7 @@ public UserGroupDTO getUserToGroup(HttpServletResponse response, @PathVariable(" } else if (currentGroup == null) { response.setStatus(HttpServletResponse.SC_NOT_FOUND); // Resource not found } else { - users = groupService.getMembersForGroup(groupId, currentUser.getLogin()); + users = groupService.getMembersForGroup(UUID.fromString(groupId), currentUser.getLogin()); } for (UserGroupDTO user : users) { @@ -301,7 +302,7 @@ public UserGroupDTO getUserToGroup(HttpServletResponse response, @PathVariable(" public UserGroupDTO addUserToGroup(HttpServletResponse response, @PathVariable("groupId") String groupId, @PathVariable("username") String username) { User currentUser = authenticationService.getCurrentUser(); - Group currentGroup = groupService.getGroupById(currentUser.getDomain(), groupId); + Group currentGroup = groupService.getGroupById(currentUser.getDomain(), UUID.fromString(groupId)); User userToAdd = userService.getUserByUsername(username); UserGroupDTO dto = null; @@ -313,10 +314,10 @@ public UserGroupDTO addUserToGroup(HttpServletResponse response, @PathVariable(" } else { if (isGroupManagedByCurrentUser(currentGroup) && !currentUser.equals(userToAdd)) { groupService.addMemberToGroup(userToAdd, currentGroup); - dto = groupService.getMembersForGroup(groupId, userToAdd); + dto = groupService.getMembersForGroup(UUID.fromString(groupId), userToAdd); } else if (currentGroup.isPublicGroup() && currentUser.equals(userToAdd) && !isGroupManagedByCurrentUser(currentGroup)) { groupService.addMemberToGroup(userToAdd, currentGroup); - dto = groupService.getMembersForGroup(groupId, userToAdd); + dto = groupService.getMembersForGroup(UUID.fromString(groupId), userToAdd); } else { response.setStatus(HttpServletResponse.SC_FORBIDDEN); } @@ -335,7 +336,7 @@ public UserGroupDTO addUserToGroup(HttpServletResponse response, @PathVariable(" public boolean removeUserFromGroup(HttpServletResponse response, @PathVariable("groupId") String groupId, @PathVariable("username") String username) { User currentUser = authenticationService.getCurrentUser(); - Group currentGroup = groupService.getGroupById(currentUser.getDomain(), groupId); + Group currentGroup = groupService.getGroupById(currentUser.getDomain(), UUID.fromString(groupId)); User userToremove = userService.getUserByUsername(username); UserGroupDTO dto = null; @@ -349,10 +350,10 @@ public boolean removeUserFromGroup(HttpServletResponse response, @PathVariable(" } else { if (isGroupManagedByCurrentUser(currentGroup) && !currentUser.equals(userToremove)) { groupService.removeMemberFromGroup(userToremove, currentGroup); - groupService.getMembersForGroup(groupId, userToremove); + groupService.getMembersForGroup(UUID.fromString(groupId), userToremove); } else if (currentGroup.isPublicGroup() && currentUser.equals(userToremove) && !isGroupManagedByCurrentUser(currentGroup)) { groupService.removeMemberFromGroup(userToremove, currentGroup); - groupService.getMembersForGroup(groupId, userToremove); + groupService.getMembersForGroup(UUID.fromString(groupId), userToremove); } else { response.setStatus(HttpServletResponse.SC_FORBIDDEN); return false; @@ -374,9 +375,10 @@ private boolean isGroupManagedByCurrentUser(Group group) { } private Group getGroupFromUser(User currentUser, String groupId) { + UUID uGroupId = UUID.fromString(groupId); Collection groups = groupService.getGroupsForUser(currentUser); for (Group testGroup : groups) { - if (testGroup.getGroupId().equals(groupId)) { + if (testGroup.getGroupId().equals(uGroupId)) { return testGroup; } } diff --git a/web/src/main/java/fr/ippon/tatami/web/rest/TimelineController.java b/web/src/main/java/fr/ippon/tatami/web/rest/TimelineController.java index 99f3c6f85..7629f2b23 100644 --- a/web/src/main/java/fr/ippon/tatami/web/rest/TimelineController.java +++ b/web/src/main/java/fr/ippon/tatami/web/rest/TimelineController.java @@ -24,6 +24,7 @@ import java.io.StringWriter; import java.util.ArrayList; import java.util.Collection; +import java.util.UUID; /** * REST controller for managing status. @@ -182,8 +183,9 @@ public String postStatus(@RequestBody StatusDTO status, HttpServletResponse resp User currentUser = authenticationService.getCurrentUser(); Collection groups = groupService.getGroupsForUser(currentUser); Group group = null; + UUID statusGroupId = UUID.fromString(status.getGroupId()); for (Group testGroup : groups) { - if (testGroup.getGroupId().equals(status.getGroupId())) { + if (testGroup.getGroupId().equals(statusGroupId)) { group = testGroup; break; } diff --git a/web/src/main/webapp/WEB-INF/pages/includes/header.jsp b/web/src/main/webapp/WEB-INF/pages/includes/header.jsp index db8d91334..be95d0473 100755 --- a/web/src/main/webapp/WEB-INF/pages/includes/header.jsp +++ b/web/src/main/webapp/WEB-INF/pages/includes/header.jsp @@ -56,7 +56,7 @@ - + diff --git a/web/src/main/webapp/WEB-INF/pages/includes/templates.jsp b/web/src/main/webapp/WEB-INF/pages/includes/templates.jsp index e13c0c5fb..5112023db 100755 --- a/web/src/main/webapp/WEB-INF/pages/includes/templates.jsp +++ b/web/src/main/webapp/WEB-INF/pages/includes/templates.jsp @@ -237,7 +237,7 @@ - + <@ if (statusPrivate == false && groupId == '') { @>