001/* 002 * (C) Copyright 2013-2018 Nuxeo (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Florent Guillaume 018 */ 019package org.nuxeo.ecm.core.redis; 020 021import java.io.IOException; 022import java.io.InputStream; 023import java.nio.file.Files; 024import java.nio.file.Paths; 025import java.security.GeneralSecurityException; 026import java.security.KeyStore; 027 028import javax.net.ssl.SSLContext; 029import javax.net.ssl.SSLSocketFactory; 030 031import org.apache.commons.lang3.StringUtils; 032import org.apache.http.ssl.SSLContextBuilder; 033import org.apache.http.ssl.SSLContexts; 034import org.nuxeo.common.xmap.annotation.XNode; 035import org.nuxeo.common.xmap.annotation.XObject; 036 037import redis.clients.jedis.Jedis; 038import redis.clients.jedis.JedisPool; 039import redis.clients.jedis.JedisPoolConfig; 040import redis.clients.jedis.Protocol; 041 042/** 043 * Descriptor for a Redis configuration. 044 * 045 * @since 5.8 046 */ 047@XObject("server") 048public class RedisServerDescriptor extends RedisPoolDescriptor { 049 050 @XNode("host") 051 public String host; 052 053 @XNode("port") 054 public int port = Protocol.DEFAULT_PORT; 055 056 /** @since 10.3 */ 057 @XNode("ssl") 058 public boolean ssl; 059 060 /** @since 10.3 */ 061 @XNode("trustStorePath") 062 public String trustStorePath; 063 064 /** @since 10.3 */ 065 @XNode("trustStorePassword") 066 public String trustStorePassword; 067 068 /** @since 10.3 */ 069 @XNode("trustStoreType") 070 public String trustStoreType; 071 072 /** @since 10.3 */ 073 @XNode("keyStorePath") 074 public String keyStorePath; 075 076 /** @since 10.3 */ 077 @XNode("keyStorePassword") 078 public String keyStorePassword; 079 080 /** @since 10.3 */ 081 @XNode("keyStoreType") 082 public String keyStoreType; 083 084 @XNode("failoverTimeout") 085 public int failoverTimeout = 300; 086 087 @Override 088 public RedisExecutor newExecutor() { 089 SSLContext sslContext = getSSLContext(); 090 boolean useSSL; 091 SSLSocketFactory sslSocketFactory; 092 if (sslContext == null) { 093 useSSL = ssl; 094 sslSocketFactory = null; 095 } else { 096 useSSL = true; 097 sslSocketFactory = sslContext.getSocketFactory(); 098 } 099 try (Jedis jedis = new Jedis(host, port, useSSL, sslSocketFactory, null, null)) { 100 if (StringUtils.isNotBlank(password)) { 101 jedis.auth(password); 102 } 103 String pong = jedis.ping(); 104 if (!"PONG".equals(pong)) { 105 throw new RuntimeException("Cannot connect to Redis host: " + host + ":" + port); 106 } 107 } 108 109 JedisPoolConfig conf = new JedisPoolConfig(); 110 conf.setMaxTotal(maxTotal); 111 conf.setMaxIdle(maxIdle); 112 RedisExecutor base = new RedisPoolExecutor(new JedisPool(conf, host, port, timeout, 113 StringUtils.defaultIfBlank(password, null), database)); 114 return new RedisFailoverExecutor(failoverTimeout, base); 115 } 116 117 protected SSLContext getSSLContext() { 118 try { 119 KeyStore trustStore = loadKeyStore(trustStorePath, trustStorePassword, trustStoreType); 120 KeyStore keyStore = loadKeyStore(keyStorePath, keyStorePassword, keyStoreType); 121 if (trustStore == null && keyStore == null) { 122 return null; 123 } 124 SSLContextBuilder sslContextBuilder = SSLContexts.custom(); 125 if (trustStore != null) { 126 sslContextBuilder.loadTrustMaterial(trustStore, null); 127 } 128 if (keyStore != null) { 129 sslContextBuilder.loadKeyMaterial(keyStore, null); 130 } 131 return sslContextBuilder.build(); 132 } catch (GeneralSecurityException | IOException e) { 133 throw new RuntimeException("Cannot setup SSL context", e); 134 } 135 } 136 137 protected KeyStore loadKeyStore(String path, String password, String type) 138 throws GeneralSecurityException, IOException { 139 if (StringUtils.isBlank(path)) { 140 return null; 141 } 142 String keyStoreType = StringUtils.defaultIfBlank(type, KeyStore.getDefaultType()); 143 KeyStore keyStore = KeyStore.getInstance(keyStoreType); 144 char[] passwordChars = StringUtils.isBlank(password) ? null : password.toCharArray(); 145 try (InputStream is = Files.newInputStream(Paths.get(path))) { 146 keyStore.load(is, passwordChars); 147 } 148 return keyStore; 149 } 150 151}