CAY-2166 Auto-loading of Cayenne modules * auto-loading CryptoModule * refactoring CryptoModuleBuilder to be a provider of extensions, with defaults coming from CryptoModule
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/374aab3d Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/374aab3d Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/374aab3d Branch: refs/heads/master Commit: 374aab3d9c68f9f5fc5b07dcc485f8a9573bb5ab Parents: 08fc9f4 Author: Andrus Adamchik <and...@objectstyle.com> Authored: Sun Dec 11 17:05:33 2016 +0300 Committer: Andrus Adamchik <and...@objectstyle.com> Committed: Sun Dec 11 20:39:52 2016 +0300 ---------------------------------------------------------------------- .../org/apache/cayenne/crypto/CryptoModule.java | 177 ++++++++++++ .../cayenne/crypto/CryptoModuleBuilder.java | 275 ++++++------------- 2 files changed, 254 insertions(+), 198 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cayenne/blob/374aab3d/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/CryptoModule.java ---------------------------------------------------------------------- diff --git a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/CryptoModule.java b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/CryptoModule.java new file mode 100644 index 0000000..382c9aa --- /dev/null +++ b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/CryptoModule.java @@ -0,0 +1,177 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cayenne.crypto; + +import org.apache.cayenne.access.jdbc.reader.RowReaderFactory; +import org.apache.cayenne.access.translator.batch.BatchTranslatorFactory; +import org.apache.cayenne.crypto.batch.CryptoBatchTranslatorFactoryDecorator; +import org.apache.cayenne.crypto.cipher.CipherFactory; +import org.apache.cayenne.crypto.cipher.DefaultCipherFactory; +import org.apache.cayenne.crypto.key.JceksKeySource; +import org.apache.cayenne.crypto.key.KeySource; +import org.apache.cayenne.crypto.map.ColumnMapper; +import org.apache.cayenne.crypto.map.PatternColumnMapper; +import org.apache.cayenne.crypto.reader.CryptoRowReaderFactoryDecorator; +import org.apache.cayenne.crypto.transformer.DefaultTransformerFactory; +import org.apache.cayenne.crypto.transformer.TransformerFactory; +import org.apache.cayenne.crypto.transformer.bytes.BytesTransformerFactory; +import org.apache.cayenne.crypto.transformer.bytes.DefaultBytesTransformerFactory; +import org.apache.cayenne.crypto.transformer.bytes.LazyBytesTransformerFactory; +import org.apache.cayenne.crypto.transformer.value.Base64StringConverter; +import org.apache.cayenne.crypto.transformer.value.BigDecimalConverter; +import org.apache.cayenne.crypto.transformer.value.BigIntegerConverter; +import org.apache.cayenne.crypto.transformer.value.BooleanConverter; +import org.apache.cayenne.crypto.transformer.value.ByteConverter; +import org.apache.cayenne.crypto.transformer.value.BytesConverter; +import org.apache.cayenne.crypto.transformer.value.BytesToBytesConverter; +import org.apache.cayenne.crypto.transformer.value.DefaultValueTransformerFactory; +import org.apache.cayenne.crypto.transformer.value.DoubleConverter; +import org.apache.cayenne.crypto.transformer.value.FloatConverter; +import org.apache.cayenne.crypto.transformer.value.IntegerConverter; +import org.apache.cayenne.crypto.transformer.value.LazyValueTransformerFactory; +import org.apache.cayenne.crypto.transformer.value.LongConverter; +import org.apache.cayenne.crypto.transformer.value.ShortConverter; +import org.apache.cayenne.crypto.transformer.value.Utf8StringConverter; +import org.apache.cayenne.crypto.transformer.value.UtilDateConverter; +import org.apache.cayenne.crypto.transformer.value.ValueTransformerFactory; +import org.apache.cayenne.di.Binder; +import org.apache.cayenne.di.MapBuilder; +import org.apache.cayenne.di.Module; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.sql.Types; +import java.util.Date; + +/** + * Contains cryptography extensions for Cayenne. + * + * @since 4.0 + */ +public class CryptoModule implements Module { + + private static final String DEFAULT_CIPHER_ALGORITHM = "AES"; + private static final String DEFAULT_CIPHER_MODE = "CBC"; + private static final String DEFAULT_CIPHER_PADDING = "PKCS5Padding"; + // same as default keystore password in java... + private static final char[] DEFAULT_KEY_PASSWORD = "changeit".toCharArray(); + private static final String DEFAULT_COLUMN_MAPPER_PATTERN = "^CRYPTO_"; + + public static CryptoModuleBuilder builder() { + return new CryptoModuleBuilder(); + } + + public static MapBuilder<String> contributeProperties(Binder binder) { + return binder.bindMap(CryptoConstants.PROPERTIES_MAP); + } + + public static MapBuilder<char[]> contributeCredentials(Binder binder) { + return binder.bindMap(CryptoConstants.CREDENTIALS_MAP); + } + + public static MapBuilder<BytesConverter<?>> contributeDbToByteConverters(Binder binder) { + return binder.bindMap(DefaultValueTransformerFactory.DB_TO_BYTE_CONVERTERS_KEY); + } + + public static MapBuilder<BytesConverter<?>> contributeObjectToByteConverters(Binder binder) { + return binder.bindMap(DefaultValueTransformerFactory.OBJECT_TO_BYTE_CONVERTERS_KEY); + } + + @Override + public void configure(Binder binder) { + + MapBuilder<String> props = contributeProperties(binder) + .put(CryptoConstants.CIPHER_ALGORITHM, DEFAULT_CIPHER_ALGORITHM) + .put(CryptoConstants.CIPHER_MODE, DEFAULT_CIPHER_MODE) + .put(CryptoConstants.CIPHER_PADDING, DEFAULT_CIPHER_PADDING); + + // credentials are stored as char[] to potentially allow wiping them clean in memory... + contributeCredentials(binder).put(CryptoConstants.KEY_PASSWORD, DEFAULT_KEY_PASSWORD); + + binder.bind(CipherFactory.class).to(DefaultCipherFactory.class); + binder.bind(TransformerFactory.class).to(DefaultTransformerFactory.class); + binder.bind(ValueTransformerFactory.class).to(DefaultValueTransformerFactory.class); + + MapBuilder<BytesConverter<?>> dbToBytesBinder = contributeDbToByteConverters(binder); + contributeDefaultDbConverters(dbToBytesBinder); + + MapBuilder<BytesConverter<?>> objectToBytesBinder = contributeObjectToByteConverters(binder); + contributeDefaultObjectConverters(objectToBytesBinder); + + binder.bind(BytesTransformerFactory.class).to(DefaultBytesTransformerFactory.class); + binder.bind(KeySource.class).to(JceksKeySource.class); + binder.bind(ColumnMapper.class).toInstance(new PatternColumnMapper(DEFAULT_COLUMN_MAPPER_PATTERN)); + + binder.decorate(BatchTranslatorFactory.class).before(CryptoBatchTranslatorFactoryDecorator.class); + binder.decorate(RowReaderFactory.class).before(CryptoRowReaderFactoryDecorator.class); + + // decorate Crypto's own services to allow Cayenne to operate over plaintext entities even if crypto keys are + // not available. + binder.decorate(ValueTransformerFactory.class).after(LazyValueTransformerFactory.class); + binder.decorate(BytesTransformerFactory.class).after(LazyBytesTransformerFactory.class); + } + + private static void contributeDefaultDbConverters(MapBuilder<BytesConverter<?>> mapBuilder) { + + mapBuilder.put(String.valueOf(Types.BINARY), BytesToBytesConverter.INSTANCE); + mapBuilder.put(String.valueOf(Types.BLOB), BytesToBytesConverter.INSTANCE); + mapBuilder.put(String.valueOf(Types.VARBINARY), BytesToBytesConverter.INSTANCE); + mapBuilder.put(String.valueOf(Types.LONGVARBINARY), BytesToBytesConverter.INSTANCE); + + mapBuilder.put(String.valueOf(Types.CHAR), Base64StringConverter.INSTANCE); + mapBuilder.put(String.valueOf(Types.NCHAR), Base64StringConverter.INSTANCE); + mapBuilder.put(String.valueOf(Types.CLOB), Base64StringConverter.INSTANCE); + mapBuilder.put(String.valueOf(Types.NCLOB), Base64StringConverter.INSTANCE); + mapBuilder.put(String.valueOf(Types.LONGVARCHAR), Base64StringConverter.INSTANCE); + mapBuilder.put(String.valueOf(Types.LONGNVARCHAR), Base64StringConverter.INSTANCE); + mapBuilder.put(String.valueOf(Types.VARCHAR), Base64StringConverter.INSTANCE); + mapBuilder.put(String.valueOf(Types.NVARCHAR), Base64StringConverter.INSTANCE); + } + + private static void contributeDefaultObjectConverters(MapBuilder<BytesConverter<?>> mapBuilder) { + + mapBuilder.put("byte[]", BytesToBytesConverter.INSTANCE); + mapBuilder.put(String.class.getName(), Utf8StringConverter.INSTANCE); + + mapBuilder.put(Double.class.getName(), DoubleConverter.INSTANCE); + mapBuilder.put(Double.TYPE.getName(), DoubleConverter.INSTANCE); + + mapBuilder.put(Float.class.getName(), FloatConverter.INSTANCE); + mapBuilder.put(Float.TYPE.getName(), FloatConverter.INSTANCE); + + mapBuilder.put(Long.class.getName(), LongConverter.INSTANCE); + mapBuilder.put(Long.TYPE.getName(), LongConverter.INSTANCE); + + mapBuilder.put(Integer.class.getName(), IntegerConverter.INSTANCE); + mapBuilder.put(Integer.TYPE.getName(), IntegerConverter.INSTANCE); + + mapBuilder.put(Short.class.getName(), ShortConverter.INSTANCE); + mapBuilder.put(Short.TYPE.getName(), ShortConverter.INSTANCE); + + mapBuilder.put(Byte.class.getName(), ByteConverter.INSTANCE); + mapBuilder.put(Byte.TYPE.getName(), ByteConverter.INSTANCE); + + mapBuilder.put(Boolean.class.getName(), BooleanConverter.INSTANCE); + mapBuilder.put(Boolean.TYPE.getName(), BooleanConverter.INSTANCE); + + mapBuilder.put(Date.class.getName(), UtilDateConverter.INSTANCE); + mapBuilder.put(BigInteger.class.getName(), BigIntegerConverter.INSTANCE); + mapBuilder.put(BigDecimal.class.getName(), BigDecimalConverter.INSTANCE); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/374aab3d/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/CryptoModuleBuilder.java ---------------------------------------------------------------------- diff --git a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/CryptoModuleBuilder.java b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/CryptoModuleBuilder.java index 4162409..63adfe8 100644 --- a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/CryptoModuleBuilder.java +++ b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/CryptoModuleBuilder.java @@ -18,49 +18,20 @@ ****************************************************************/ package org.apache.cayenne.crypto; -import org.apache.cayenne.access.jdbc.reader.RowReaderFactory; -import org.apache.cayenne.access.translator.batch.BatchTranslatorFactory; -import org.apache.cayenne.crypto.batch.CryptoBatchTranslatorFactoryDecorator; import org.apache.cayenne.crypto.cipher.CipherFactory; -import org.apache.cayenne.crypto.cipher.DefaultCipherFactory; -import org.apache.cayenne.crypto.key.JceksKeySource; import org.apache.cayenne.crypto.key.KeySource; import org.apache.cayenne.crypto.map.ColumnMapper; import org.apache.cayenne.crypto.map.PatternColumnMapper; -import org.apache.cayenne.crypto.reader.CryptoRowReaderFactoryDecorator; -import org.apache.cayenne.crypto.transformer.DefaultTransformerFactory; -import org.apache.cayenne.crypto.transformer.TransformerFactory; import org.apache.cayenne.crypto.transformer.bytes.BytesTransformerFactory; -import org.apache.cayenne.crypto.transformer.bytes.DefaultBytesTransformerFactory; -import org.apache.cayenne.crypto.transformer.bytes.LazyBytesTransformerFactory; -import org.apache.cayenne.crypto.transformer.value.Base64StringConverter; -import org.apache.cayenne.crypto.transformer.value.BigDecimalConverter; -import org.apache.cayenne.crypto.transformer.value.BigIntegerConverter; -import org.apache.cayenne.crypto.transformer.value.BooleanConverter; -import org.apache.cayenne.crypto.transformer.value.ByteConverter; import org.apache.cayenne.crypto.transformer.value.BytesConverter; -import org.apache.cayenne.crypto.transformer.value.BytesToBytesConverter; -import org.apache.cayenne.crypto.transformer.value.DefaultValueTransformerFactory; -import org.apache.cayenne.crypto.transformer.value.DoubleConverter; -import org.apache.cayenne.crypto.transformer.value.FloatConverter; -import org.apache.cayenne.crypto.transformer.value.IntegerConverter; -import org.apache.cayenne.crypto.transformer.value.LazyValueTransformerFactory; -import org.apache.cayenne.crypto.transformer.value.LongConverter; -import org.apache.cayenne.crypto.transformer.value.ShortConverter; -import org.apache.cayenne.crypto.transformer.value.Utf8StringConverter; -import org.apache.cayenne.crypto.transformer.value.UtilDateConverter; import org.apache.cayenne.crypto.transformer.value.ValueTransformerFactory; import org.apache.cayenne.di.Binder; import org.apache.cayenne.di.MapBuilder; import org.apache.cayenne.di.Module; import java.io.File; -import java.math.BigDecimal; -import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URL; -import java.sql.Types; -import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -70,15 +41,11 @@ import java.util.Objects; * runtime needed to enable encryption of certain data columns. Builder allows * to specify custom ciphers, as well as a strategy for discovering which * columns are encrypted. - * + * * @since 4.0 */ public class CryptoModuleBuilder { - private static final String DEFAULT_CIPHER_ALGORITHM = "AES"; - private static final String DEFAULT_CIPHER_MODE = "CBC"; - private static final String DEFAULT_CIPHER_PADDING = "PKCS5Padding"; - private Class<? extends ValueTransformerFactory> valueTransformerFactoryType; private Class<? extends BytesTransformerFactory> bytesTransformerFactoryType; @@ -91,7 +58,6 @@ public class CryptoModuleBuilder { private String cipherAlgoritm; private String cipherMode; - private String cipherPadding; private Class<? extends CipherFactory> cipherFactoryType; private URL keyStoreUrl; @@ -105,71 +71,50 @@ public class CryptoModuleBuilder { private boolean compress; - public static CryptoModuleBuilder builder() { - return new CryptoModuleBuilder(); - } - - public CryptoModuleBuilder() { - - // init some sensible defaults that work in JVM without extra - // packages... - this.cipherAlgoritm = DEFAULT_CIPHER_ALGORITHM; - this.cipherMode = DEFAULT_CIPHER_MODE; - this.cipherPadding = DEFAULT_CIPHER_PADDING; - - this.cipherFactoryType = DefaultCipherFactory.class; - this.keySourceType = JceksKeySource.class; - - this.columnMapperPattern = "^CRYPTO_"; - - this.valueTransformerFactoryType = DefaultValueTransformerFactory.class; - this.bytesTransformerFactoryType = DefaultBytesTransformerFactory.class; + // use CryptoModule.builder() to create the builder... + protected CryptoModuleBuilder() { + this.extraDbToBytes = new HashMap<>(); + this.extraObjectToBytes = new HashMap<>(); } public CryptoModuleBuilder cipherAlgorithm(String algorithm) { - this.cipherAlgoritm = algorithm; + this.cipherAlgoritm = Objects.requireNonNull(algorithm); return this; } public CryptoModuleBuilder cipherMode(String mode) { - this.cipherMode = mode; + this.cipherMode = Objects.requireNonNull(mode); return this; } public CryptoModuleBuilder cipherFactory(Class<? extends CipherFactory> factoryType) { - this.cipherFactoryType = factoryType; + this.cipherFactoryType = Objects.requireNonNull(factoryType); return this; } public CryptoModuleBuilder valueTransformer(Class<? extends ValueTransformerFactory> factoryType) { - this.valueTransformerFactoryType = factoryType; + this.valueTransformerFactoryType = Objects.requireNonNull(factoryType); return this; } public <T> CryptoModuleBuilder objectToBytesConverter(Class<T> objectType, BytesConverter<T> converter) { - if (extraObjectToBytes == null) { - extraObjectToBytes = new HashMap<>(); - } extraObjectToBytes.put(objectType.getName(), Objects.requireNonNull(converter)); return this; } public CryptoModuleBuilder dbToBytesConverter(int sqlType, BytesConverter<?> converter) { - if (extraDbToBytes == null) { - extraDbToBytes = new HashMap<>(); - } extraDbToBytes.put(sqlType, Objects.requireNonNull(converter)); return this; } public CryptoModuleBuilder bytesTransformer(Class<? extends BytesTransformerFactory> factoryType) { - this.bytesTransformerFactoryType = factoryType; + this.bytesTransformerFactoryType = Objects.requireNonNull(factoryType); return this; } public CryptoModuleBuilder columnMapper(Class<? extends ColumnMapper> columnMapperType) { this.columnMapperPattern = null; - this.columnMapperType = columnMapperType; + this.columnMapperType = Objects.requireNonNull(columnMapperType); this.columnMapper = null; return this; } @@ -177,24 +122,23 @@ public class CryptoModuleBuilder { public CryptoModuleBuilder columnMapper(ColumnMapper columnMapper) { this.columnMapperPattern = null; this.columnMapperType = null; - this.columnMapper = columnMapper; + this.columnMapper = Objects.requireNonNull(columnMapper); return this; } public CryptoModuleBuilder columnMapper(String pattern) { - this.columnMapperPattern = pattern; + this.columnMapperPattern = Objects.requireNonNull(pattern); this.columnMapperType = null; this.columnMapper = null; return this; } /** - * @param encryptionKeyAlias - * The name of the key in the keystore that should be used for - * encryption by default. + * @param encryptionKeyAlias The name of the key in the keystore that should be used for + * encryption by default. */ public CryptoModuleBuilder encryptionKeyAlias(String encryptionKeyAlias) { - this.encryptionKeyAlias = encryptionKeyAlias; + this.encryptionKeyAlias = Objects.requireNonNull(encryptionKeyAlias); return this; } @@ -202,21 +146,18 @@ public class CryptoModuleBuilder { * Configures keystore parameters. The KeyStore must be of "jceks" type and * contain all needed secret keys for the target database. Currently all * keys must be protected with the same password. - * - * @param file - * A file to load keystore from. - * @param passwordForAllKeys - * A password that unlocks all keys in the keystore. - * @param encryptionKeyAlias - * The name of the key in the keystore that should be used for - * encryption by default. + * + * @param file A file to load keystore from. + * @param passwordForAllKeys A password that unlocks all keys in the keystore. + * @param encryptionKeyAlias The name of the key in the keystore that should be used for + * encryption by default. */ public CryptoModuleBuilder keyStore(File file, char[] passwordForAllKeys, String encryptionKeyAlias) { this.encryptionKeyAlias = encryptionKeyAlias; this.keyPassword = passwordForAllKeys; this.keyStoreUrl = null; this.keyStoreUrlString = null; - this.keyStoreFile = file; + this.keyStoreFile = Objects.requireNonNull(file); return this; } @@ -224,20 +165,17 @@ public class CryptoModuleBuilder { * Configures keystore parameters. The KeyStore must be of "jceks" type and * contain all needed secret keys for the target database. Currently all * keys must be protected with the same password. - * - * @param url - * A URL to load keystore from. - * @param passwordForAllKeys - * A password that unlocks all keys in the keystore. - * @param encryptionKeyAlias - * The name of the key in the keystore that should be used for - * encryption by default. + * + * @param url A URL to load keystore from. + * @param passwordForAllKeys A password that unlocks all keys in the keystore. + * @param encryptionKeyAlias The name of the key in the keystore that should be used for + * encryption by default. */ public CryptoModuleBuilder keyStore(String url, char[] passwordForAllKeys, String encryptionKeyAlias) { this.encryptionKeyAlias = encryptionKeyAlias; this.keyPassword = passwordForAllKeys; this.keyStoreUrl = null; - this.keyStoreUrlString = url; + this.keyStoreUrlString = Objects.requireNonNull(url); this.keyStoreFile = null; return this; } @@ -246,33 +184,30 @@ public class CryptoModuleBuilder { * Configures keystore parameters. The KeyStore must be of "jceks" type and * contain all needed secret keys for the target database. Currently all * keys must be protected with the same password. - * - * @param url - * A URL to load keystore from. - * @param passwordForAllKeys - * A password that unlocks all keys in the keystore. - * @param encryptionKeyAlias - * The name of the key in the keystore that should be used for - * encryption by default. + * + * @param url A URL to load keystore from. + * @param passwordForAllKeys A password that unlocks all keys in the keystore. + * @param encryptionKeyAlias The name of the key in the keystore that should be used for + * encryption by default. */ public CryptoModuleBuilder keyStore(URL url, char[] passwordForAllKeys, String encryptionKeyAlias) { this.encryptionKeyAlias = encryptionKeyAlias; this.keyPassword = passwordForAllKeys; - this.keyStoreUrl = url; + this.keyStoreUrl = Objects.requireNonNull(url); this.keyStoreUrlString = null; this.keyStoreFile = null; return this; } public CryptoModuleBuilder keySource(Class<? extends KeySource> type) { - this.keySourceType = type; + this.keySourceType = Objects.requireNonNull(type); this.keySource = null; return this; } public CryptoModuleBuilder keySource(KeySource keySource) { this.keySourceType = null; - this.keySource = keySource; + this.keySource = Objects.requireNonNull(keySource); return this; } @@ -286,42 +221,22 @@ public class CryptoModuleBuilder { */ public Module build() { - if (valueTransformerFactoryType == null) { - throw new IllegalStateException("'ValueTransformerFactory' is not initialized"); - } - - if (columnMapperType == null && columnMapper == null && columnMapperPattern == null) { - throw new IllegalStateException("'ColumnMapper' is not initialized"); - } - - if (cipherFactoryType == null) { - throw new IllegalStateException("'CipherFactory' is not initialized"); - } - return new Module() { @Override public void configure(Binder binder) { - String keyStoreUrl = null; - if (CryptoModuleBuilder.this.keyStoreUrl != null) { - keyStoreUrl = CryptoModuleBuilder.this.keyStoreUrl.toExternalForm(); - } else if (CryptoModuleBuilder.this.keyStoreUrlString != null) { - keyStoreUrl = CryptoModuleBuilder.this.keyStoreUrlString; - } else if (keyStoreFile != null) { - try { - keyStoreUrl = keyStoreFile.toURI().toURL().toExternalForm(); - } catch (MalformedURLException e) { - throw new IllegalStateException("Invalid keyStore file", e); - } + MapBuilder<String> props = CryptoModule.contributeProperties(binder); + + if (cipherAlgoritm != null) { + props.put(CryptoConstants.CIPHER_ALGORITHM, cipherAlgoritm); } - // String properties - MapBuilder<String> props = binder.<String> bindMap(CryptoConstants.PROPERTIES_MAP) - .put(CryptoConstants.CIPHER_ALGORITHM, cipherAlgoritm) - .put(CryptoConstants.CIPHER_MODE, cipherMode) - .put(CryptoConstants.CIPHER_PADDING, cipherPadding); + if (cipherMode != null) { + props.put(CryptoConstants.CIPHER_MODE, cipherMode); + } + String keyStoreUrl = keyStoreUrl(); if (keyStoreUrl != null) { props.put(CryptoConstants.KEYSTORE_URL, keyStoreUrl); } @@ -334,41 +249,39 @@ public class CryptoModuleBuilder { props.put(CryptoConstants.COMPRESSION, "true"); } - // char[] credentials... stored as char[] to potentially allow - // wiping them clean in memory... - MapBuilder<char[]> creds = binder.<char[]> bindMap(CryptoConstants.CREDENTIALS_MAP); - if (keyPassword != null) { - creds.put(CryptoConstants.KEY_PASSWORD, keyPassword); + CryptoModule.contributeCredentials(binder).put(CryptoConstants.KEY_PASSWORD, keyPassword); } - binder.bind(CipherFactory.class).to(cipherFactoryType); - binder.bind(TransformerFactory.class).to(DefaultTransformerFactory.class); - binder.bind(ValueTransformerFactory.class).to(valueTransformerFactoryType); + if (cipherFactoryType != null) { + binder.bind(CipherFactory.class).to(cipherFactoryType); + } - MapBuilder<BytesConverter<?>> dbToBytesBinder = - binder.bindMap(DefaultValueTransformerFactory.DB_TO_BYTE_CONVERTERS_KEY); - contributeDefaultDbConverters(dbToBytesBinder); - if (extraDbToBytes != null) { + if (valueTransformerFactoryType != null) { + binder.bind(ValueTransformerFactory.class).to(valueTransformerFactoryType); + } + + if (!extraDbToBytes.isEmpty()) { + MapBuilder<BytesConverter<?>> dbToBytesBinder = CryptoModule.contributeDbToByteConverters(binder); for (Map.Entry<Integer, BytesConverter<?>> extraConverter : extraDbToBytes.entrySet()) { dbToBytesBinder.put(extraConverter.getKey().toString(), extraConverter.getValue()); } } - MapBuilder<BytesConverter<?>> objectToBytesBinder = - binder.bindMap(DefaultValueTransformerFactory.OBJECT_TO_BYTE_CONVERTERS_KEY); - contributeDefaultObjectConverters(objectToBytesBinder); - if (extraObjectToBytes != null) { + if (!extraObjectToBytes.isEmpty()) { + MapBuilder<BytesConverter<?>> objectToBytesBinder = CryptoModule.contributeObjectToByteConverters(binder); for (Map.Entry<String, BytesConverter<?>> extraConverter : extraObjectToBytes.entrySet()) { objectToBytesBinder.put(extraConverter.getKey(), extraConverter.getValue()); } } - binder.bind(BytesTransformerFactory.class).to(bytesTransformerFactoryType); + if (bytesTransformerFactoryType != null) { + binder.bind(BytesTransformerFactory.class).to(bytesTransformerFactoryType); + } if (keySource != null) { binder.bind(KeySource.class).toInstance(keySource); - } else { + } else if (keySourceType != null) { binder.bind(KeySource.class).to(keySourceType); } @@ -376,66 +289,32 @@ public class CryptoModuleBuilder { binder.bind(ColumnMapper.class).toInstance(new PatternColumnMapper(columnMapperPattern)); } else if (columnMapperType != null) { binder.bind(ColumnMapper.class).to(columnMapperType); - } else { + } else if (columnMapper != null) { binder.bind(ColumnMapper.class).toInstance(columnMapper); } - - binder.decorate(BatchTranslatorFactory.class).before(CryptoBatchTranslatorFactoryDecorator.class); - binder.decorate(RowReaderFactory.class).before(CryptoRowReaderFactoryDecorator.class); - - // decorate our own services to allow Cayenne to operate over plaintext entities - // even if crypto keys are not available. - binder.decorate(ValueTransformerFactory.class).after(LazyValueTransformerFactory.class); - binder.decorate(BytesTransformerFactory.class).after(LazyBytesTransformerFactory.class); } }; } - private static void contributeDefaultDbConverters(MapBuilder<BytesConverter<?>> mapBuilder) { - - mapBuilder.put(String.valueOf(Types.BINARY), BytesToBytesConverter.INSTANCE); - mapBuilder.put(String.valueOf(Types.BLOB), BytesToBytesConverter.INSTANCE); - mapBuilder.put(String.valueOf(Types.VARBINARY), BytesToBytesConverter.INSTANCE); - mapBuilder.put(String.valueOf(Types.LONGVARBINARY), BytesToBytesConverter.INSTANCE); - - mapBuilder.put(String.valueOf(Types.CHAR), Base64StringConverter.INSTANCE); - mapBuilder.put(String.valueOf(Types.NCHAR), Base64StringConverter.INSTANCE); - mapBuilder.put(String.valueOf(Types.CLOB), Base64StringConverter.INSTANCE); - mapBuilder.put(String.valueOf(Types.NCLOB), Base64StringConverter.INSTANCE); - mapBuilder.put(String.valueOf(Types.LONGVARCHAR), Base64StringConverter.INSTANCE); - mapBuilder.put(String.valueOf(Types.LONGNVARCHAR), Base64StringConverter.INSTANCE); - mapBuilder.put(String.valueOf(Types.VARCHAR), Base64StringConverter.INSTANCE); - mapBuilder.put(String.valueOf(Types.NVARCHAR), Base64StringConverter.INSTANCE); - } - - private static void contributeDefaultObjectConverters(MapBuilder<BytesConverter<?>> mapBuilder) { - - mapBuilder.put("byte[]", BytesToBytesConverter.INSTANCE); - mapBuilder.put(String.class.getName(), Utf8StringConverter.INSTANCE); - - mapBuilder.put(Double.class.getName(), DoubleConverter.INSTANCE); - mapBuilder.put(Double.TYPE.getName(), DoubleConverter.INSTANCE); - - mapBuilder.put(Float.class.getName(), FloatConverter.INSTANCE); - mapBuilder.put(Float.TYPE.getName(), FloatConverter.INSTANCE); - - mapBuilder.put(Long.class.getName(), LongConverter.INSTANCE); - mapBuilder.put(Long.TYPE.getName(), LongConverter.INSTANCE); + protected String keyStoreUrl() { + if (this.keyStoreUrl != null) { + return this.keyStoreUrl.toExternalForm(); + } - mapBuilder.put(Integer.class.getName(), IntegerConverter.INSTANCE); - mapBuilder.put(Integer.TYPE.getName(), IntegerConverter.INSTANCE); + if (this.keyStoreUrlString != null) { + return this.keyStoreUrlString; + } - mapBuilder.put(Short.class.getName(), ShortConverter.INSTANCE); - mapBuilder.put(Short.TYPE.getName(), ShortConverter.INSTANCE); + if (keyStoreFile != null) { + try { + return keyStoreFile.toURI().toURL().toExternalForm(); + } catch (MalformedURLException e) { + throw new IllegalStateException("Invalid keyStore file", e); + } + } - mapBuilder.put(Byte.class.getName(), ByteConverter.INSTANCE); - mapBuilder.put(Byte.TYPE.getName(), ByteConverter.INSTANCE); + return null; + } - mapBuilder.put(Boolean.class.getName(), BooleanConverter.INSTANCE); - mapBuilder.put(Boolean.TYPE.getName(), BooleanConverter.INSTANCE); - mapBuilder.put(Date.class.getName(), UtilDateConverter.INSTANCE); - mapBuilder.put(BigInteger.class.getName(), BigIntegerConverter.INSTANCE); - mapBuilder.put(BigDecimal.class.getName(), BigDecimalConverter.INSTANCE); - } }