diff --git a/src/main/java/biz/nynja/account/codecs/AuthenticationProviderCodec.java b/src/main/java/biz/nynja/account/codecs/AuthenticationProviderCodec.java new file mode 100644 index 0000000000000000000000000000000000000000..c1260f786a8d5285ab5adffa3df7e95c6ec8f171 --- /dev/null +++ b/src/main/java/biz/nynja/account/codecs/AuthenticationProviderCodec.java @@ -0,0 +1,67 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.codecs; + +import java.nio.ByteBuffer; + +import com.datastax.driver.core.ProtocolVersion; +import com.datastax.driver.core.TypeCodec; +import com.datastax.driver.core.UDTValue; +import com.datastax.driver.core.UserType; +import com.datastax.driver.core.exceptions.InvalidTypeException; + +import biz.nynja.account.models.AuthenticationProvider; + + +public class AuthenticationProviderCodec extends TypeCodec { + + private final TypeCodec innerCodec; + + private final UserType userType; + + public AuthenticationProviderCodec(TypeCodec innerCodec, Class javaType) { + super(innerCodec.getCqlType(), javaType); + this.innerCodec = innerCodec; + this.userType = (UserType) innerCodec.getCqlType(); + } + + @Override + public ByteBuffer serialize(AuthenticationProvider value, ProtocolVersion protocolVersion) + throws InvalidTypeException { + return innerCodec.serialize(toUDTValue(value), protocolVersion); + } + + @Override + public AuthenticationProvider deserialize(ByteBuffer bytes, ProtocolVersion protocolVersion) + throws InvalidTypeException { + return toAuthenticationProvider(innerCodec.deserialize(bytes, protocolVersion)); + } + + @Override + public AuthenticationProvider parse(String value) throws InvalidTypeException { + return value == null || value.isEmpty() || value.equals("NULL") ? null + : toAuthenticationProvider(innerCodec.parse(value)); + } + + @Override + public String format(AuthenticationProvider value) throws InvalidTypeException { + return value == null ? null : innerCodec.format(toUDTValue(value)); + } + + protected AuthenticationProvider toAuthenticationProvider(UDTValue value) { + if (value == null) { + return null; + } else { + AuthenticationProvider authProvider = new AuthenticationProvider(); + authProvider.setType(value.getString("type")); + authProvider.setValue(value.getString("value")); + return authProvider; + } + } + + protected UDTValue toUDTValue(AuthenticationProvider value) { + return value == null ? null + : userType.newValue().setString("type", value.getType()).setString("value", value.getValue()); + } +} diff --git a/src/main/java/biz/nynja/account/components/PreparedStatementsCache.java b/src/main/java/biz/nynja/account/components/PreparedStatementsCache.java new file mode 100644 index 0000000000000000000000000000000000000000..91b9b32575dc5804591380f851ee8366510e02e4 --- /dev/null +++ b/src/main/java/biz/nynja/account/components/PreparedStatementsCache.java @@ -0,0 +1,69 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.components; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import javax.annotation.PostConstruct; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; + +import com.datastax.driver.core.PreparedStatement; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.TypeCodec; +import com.datastax.driver.core.UDTValue; +import com.datastax.driver.core.UserType; + +import biz.nynja.account.codecs.AuthenticationProviderCodec; +import biz.nynja.account.configuration.CassandraConfig; +import biz.nynja.account.models.AuthenticationProvider; + +@Configuration +public class PreparedStatementsCache { + + private final CassandraConfig cassandraConfig; + + private final Session session; + + private static Map statementsCache; + + @Autowired + public PreparedStatementsCache(Session session, CassandraConfig cassandraConfig) { + this.session = session; + this.cassandraConfig = cassandraConfig; + } + + @PostConstruct + public void init() { + statementsCache = new ConcurrentHashMap<>(); + registerAuthenticationProviderCodec(); + } + + private Map getPreparedStatementsCache() { + return statementsCache; + } + + public PreparedStatement getPreparedStatement(String cql) { + if (getPreparedStatementsCache().containsKey(cql)) { + return getPreparedStatementsCache().get(cql); + } else { + PreparedStatement statement = session.prepare(cql); + getPreparedStatementsCache().put(cql, statement); + return statement; + } + } + + private void registerAuthenticationProviderCodec() { + UserType authenticationProviderType = session.getCluster().getMetadata() + .getKeyspace(cassandraConfig.getConfiguredKeyspaceName()).getUserType("authenticationprovider"); + TypeCodec authenticationProviderTypeCodec = session.getCluster().getConfiguration().getCodecRegistry() + .codecFor(authenticationProviderType); + + AuthenticationProviderCodec addressCodec = new AuthenticationProviderCodec(authenticationProviderTypeCodec, + AuthenticationProvider.class); + session.getCluster().getConfiguration().getCodecRegistry().register(addressCodec); + } +} diff --git a/src/main/java/biz/nynja/account/components/StatementsPool.java b/src/main/java/biz/nynja/account/components/StatementsPool.java new file mode 100644 index 0000000000000000000000000000000000000000..5ee4b816e0ee2aca6ba635ab3718313a9e42db00 --- /dev/null +++ b/src/main/java/biz/nynja/account/components/StatementsPool.java @@ -0,0 +1,69 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.components; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.datastax.driver.core.BoundStatement; + +import biz.nynja.account.models.AuthenticationProvider; + +@Service +public class StatementsPool { + + private final PreparedStatementsCache preparedStatementsCache; + + @Autowired + public StatementsPool(PreparedStatementsCache preparedStatementsCache) { + this.preparedStatementsCache = preparedStatementsCache; + } + + public BoundStatement addAuthenticationProviderToProfile(UUID profileId, AuthenticationProvider authProvider) { + String cql = "UPDATE profile SET authenticationproviders = authenticationproviders + ? WHERE profileid = ? ;"; + Set toBeAdded = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(authProvider))); + BoundStatement bound = preparedStatementsCache.getPreparedStatement(cql).bind(toBeAdded, profileId); + return bound; + } + + public BoundStatement insertProfileByAuthenticationProvider(UUID profileId, String authPovider, + String authProviderType) { + String cql = "INSERT INTO profilebyauthenticationprovider (authenticationprovider, authenticationprovidertype, profileid) VALUES (?, ?, ?) ;"; + BoundStatement bound = preparedStatementsCache.getPreparedStatement(cql).bind(authPovider, authProviderType, + profileId); + return bound; + } + + public BoundStatement deleteAuthenicationProviderFromProfile(UUID profileId, AuthenticationProvider authProvider) { + String cql = "UPDATE profile SET authenticationproviders = authenticationproviders - ? WHERE profileid = ? ;"; + Set toBeDeleted = Collections + .unmodifiableSet(new HashSet<>(Arrays.asList(authProvider))); + BoundStatement bound = preparedStatementsCache.getPreparedStatement(cql).bind(toBeDeleted, profileId); + return bound; + } + + public BoundStatement deleteProfileByAuthenticationProvider(String authProvider, String authProviderType) { + String cql = "DELETE FROM profilebyauthenticationprovider where authenticationprovider = ? and authenticationprovidertype = ? ;"; + BoundStatement bound = preparedStatementsCache.getPreparedStatement(cql).bind(authProvider, authProviderType); + return bound; + } + + public BoundStatement deleteBackupAuthenticationProviderFromProfile(UUID profileId) { + String cql = "DELETE backupauthenticationprovider FROM profile WHERE profileid = ? ; "; + BoundStatement bound = preparedStatementsCache.getPreparedStatement(cql).bind(profileId); + return bound; + } + + public BoundStatement deleteCreationProviderFromAccount(UUID accountId) { + String cql = "DELETE authenticationprovidertype, authenticationprovider FROM account WHERE accountid = ? ;"; + BoundStatement bound = preparedStatementsCache.getPreparedStatement(cql).bind(accountId); + return bound; + } +} diff --git a/src/main/java/biz/nynja/account/configuration/CassandraConfig.java b/src/main/java/biz/nynja/account/configuration/CassandraConfig.java index 470660383e856b62c4a775916e8f95c04dc1c7af..739d5063f39b8523254bf50a494cdee7e90b7e96 100644 --- a/src/main/java/biz/nynja/account/configuration/CassandraConfig.java +++ b/src/main/java/biz/nynja/account/configuration/CassandraConfig.java @@ -70,4 +70,8 @@ public class CassandraConfig extends AbstractCassandraConfiguration { protected List getStartupScripts() { return super.getStartupScripts(); } + + public String getConfiguredKeyspaceName() { + return getKeyspaceName(); + } } diff --git a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java index 76a684eba5394ac5a2ef2af99f950129e28c6adb..e760dab22af7418c416af33ab5dd87c5db13bdbc 100644 --- a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java +++ b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java @@ -19,12 +19,13 @@ import org.springframework.data.cassandra.core.WriteResult; import org.springframework.stereotype.Service; import com.datastax.driver.core.BatchStatement; +import com.datastax.driver.core.BoundStatement; import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.Session; -import com.datastax.driver.core.SimpleStatement; -import com.datastax.driver.core.Statement; import biz.nynja.account.components.AccountServiceHelper; +import biz.nynja.account.components.StatementsPool; + import biz.nynja.account.grpc.AuthProviderDetails; import biz.nynja.account.grpc.CompletePendingAccountCreationRequest; import biz.nynja.account.grpc.UpdateAccountRequest; @@ -51,6 +52,9 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio @Autowired private CassandraTemplate cassandraTemplate; + @Autowired + private StatementsPool statementsPool; + @Autowired private AccountRepository accountRepository; @@ -482,21 +486,18 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio @Override public boolean addAuthenticationProvider(UUID profileId, AuthenticationProvider authProvider) { - BatchStatement batchOperations = new BatchStatement(); + BatchStatement batch = new BatchStatement(); ResultSet wr = null; try { - Statement updateAuthProvidersInProfile = new SimpleStatement( - "UPDATE profile SET authenticationproviders = authenticationproviders + {('" - + authProvider.getType() + "', '" + authProvider.getValue() + "')} WHERE profileid = " - + profileId.toString()); - Statement insertInProfileByAuthProvider = new SimpleStatement(" INSERT INTO profilebyauthenticationprovider " - + "(authenticationprovider, authenticationprovidertype, profileid) VALUES ('" - + authProvider.getValue() + "', '" + authProvider.getType() + "', " + profileId + ");"); - - batchOperations.add(updateAuthProvidersInProfile); - batchOperations.add(insertInProfileByAuthProvider); - wr = session.execute(batchOperations); + BoundStatement updateAuthProvidersInProfile = statementsPool.addAuthenticationProviderToProfile(profileId, + authProvider); + batch.add(updateAuthProvidersInProfile); + BoundStatement insertInProfileByAuthProvider = statementsPool + .insertProfileByAuthenticationProvider(profileId, authProvider.getValue(), authProvider.getType()); + batch.add(insertInProfileByAuthProvider); + + wr = session.execute(batch); } catch (IllegalArgumentException | IllegalStateException e) { logger.info("Exception while adding new authenication provider to profile with id: {}.", profileId); logger.debug("Exception while adding new authenication provider to profile with id: {}, {}", profileId, @@ -514,43 +515,35 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio } @Override - public boolean deleteAuthenticationProvider(Profile profile, - AuthenticationProvider authProvider) { + public boolean deleteAuthenticationProvider(Profile profile, AuthenticationProvider authProvider) { BatchStatement batch = new BatchStatement(); ResultSet rs = null; // Remove authentication provider form list of authentication providers in profile - Statement st1 = new SimpleStatement("UPDATE profile SET authenticationproviders = authenticationproviders - {('" - + authProvider.getType() + "', '" + authProvider.getValue() - + "')} WHERE profileid = " + profile.getProfileId().toString()); + BoundStatement st1 = statementsPool.deleteAuthenicationProviderFromProfile(profile.getProfileId(), + authProvider); batch.add(st1); // Remove record for profile by this authentication provider - Statement st2 = new SimpleStatement( - "DELETE FROM profilebyauthenticationprovider where authenticationprovider = '" - + authProvider.getValue() + "' and authenticationprovidertype = '" - + authProvider.getType() + "';"); + BoundStatement st2 = statementsPool.deleteProfileByAuthenticationProvider(authProvider.getValue(), + authProvider.getType()); batch.add(st2); // The requested authentication provider is set as a backup provider. We have to delete the reference. if (profile.getBackupAuthenticationProvider() != null && profile.getBackupAuthenticationProvider().equals(authProvider)) { - Statement st3 = new SimpleStatement("DELETE backupauthenticationprovider FROM profile WHERE profileid = " - + profile.getProfileId().toString()); + BoundStatement st3 = statementsPool.deleteBackupAuthenticationProviderFromProfile(profile.getProfileId()); batch.add(st3); } // Check if there is an account, created using this authentication provider - Account account = accountServiceHelper.getAccountByAuthenticationProviderHelper( - authProvider.getValue(), authProvider.getType()); - + Account account = accountServiceHelper.getAccountByAuthenticationProviderHelper(authProvider.getValue(), + authProvider.getType()); // Delete authentication provider from account if (account != null) { - Statement st4 = new SimpleStatement( - "DELETE authenticationprovidertype, authenticationprovider FROM account WHERE accountid = " - + account.getAccountId().toString()); + BoundStatement st4 = statementsPool.deleteCreationProviderFromAccount(account.getAccountId()); batch.add(st4); }