/* * Copyright 2010 NCHOVY * * Licensed 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.krakenapps.rpc; import java.security.SecureRandom; import java.security.cert.X509Certificate; import java.util.concurrent.Executors; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import org.jboss.netty.bootstrap.ClientBootstrap; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelFuture; import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.channel.ChannelPipelineFactory; import org.jboss.netty.channel.Channels; import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; import org.jboss.netty.handler.ssl.SslHandler; import org.krakenapps.rpc.impl.RpcDecoder; import org.krakenapps.rpc.impl.RpcEncoder; import org.krakenapps.rpc.impl.RpcHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class RpcClient { private final Logger logger = LoggerFactory.getLogger(RpcClient.class.getName()); private final boolean ownHandler; private ClientBootstrap bootstrap; private RpcHandler handler; private RpcConnection conn; public RpcClient(String guid) { this.handler = new RpcHandler(guid, new TrustPeerRegistry()); this.ownHandler = true; } public RpcClient(RpcHandler handler) { this.handler = handler; this.ownHandler = false; } public RpcConnection connect(RpcConnectionProperties props) { if (conn != null) return conn; if (ownHandler) handler.start(); bootstrap = new ClientBootstrap(new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); bootstrap.setOption("tcpNoDelay", true); bootstrap.setOption("keepAlive", true); bootstrap.setPipelineFactory(new ChannelPipelineFactory() { @Override public ChannelPipeline getPipeline() throws Exception { ChannelPipeline pipeline = Channels.pipeline(); pipeline.addLast("decoder", new RpcDecoder()); pipeline.addLast("encoder", new RpcEncoder()); pipeline.addLast("handler", handler); return pipeline; } }); Channel channel = null; try { ChannelFuture channelFuture = bootstrap.connect(props.getRemoteAddress()); channel = channelFuture.awaitUninterruptibly().getChannel(); return doPostConnectSteps(channel, props); } catch (Exception e) { if (channel != null) channel.close(); // shutdown executors bootstrap.releaseExternalResources(); throw new RuntimeException("rpc connection failed", e); } } public RpcConnection connectSsl(RpcConnectionProperties props) { if (conn != null) return conn; if (ownHandler) handler.start(); ClientBootstrap bootstrap = new ClientBootstrap(new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); bootstrap.setOption("tcpNoDelay", true); bootstrap.setOption("keepAlive", true); final KeyManagerFactory kmf = props.getKeyManagerFactory(); final TrustManagerFactory tmf = props.getTrustManagerFactory(); bootstrap.setPipelineFactory(new ChannelPipelineFactory() { @Override public ChannelPipeline getPipeline() throws Exception { ChannelPipeline pipeline = Channels.pipeline(); TrustManager[] trustManagers = null; KeyManager[] keyManagers = null; if (tmf != null) trustManagers = tmf.getTrustManagers(); if (kmf != null) keyManagers = kmf.getKeyManagers(); SSLContext clientContext = SSLContext.getInstance("TLS"); clientContext.init(keyManagers, trustManagers, new SecureRandom()); SSLEngine engine = clientContext.createSSLEngine(); engine.setUseClientMode(true); SslHandler sslHandler = new SslHandler(engine); pipeline.addLast("ssl", sslHandler); pipeline.addLast("decoder", new RpcDecoder()); pipeline.addLast("encoder", new RpcEncoder()); pipeline.addLast("handler", handler); return pipeline; } }); ChannelFuture channelFuture = bootstrap.connect(props.getRemoteAddress()); Channel channel = channelFuture.awaitUninterruptibly().getChannel(); SslHandler sslHandler = (SslHandler) channel.getPipeline().get("ssl"); try { sslHandler.handshake().await(5000); X509Certificate peerCert = (X509Certificate) sslHandler.getEngine().getSession().getPeerCertificates()[0]; props.setPeerCert(peerCert); return doPostConnectSteps(channel, props); } catch (Exception e) { if (channel != null) channel.close(); // shutdown executors bootstrap.releaseExternalResources(); throw new RuntimeException("rpc-ssl connection failed", e); } } /** * Receive and set peer certificate for connection. */ private RpcConnection doPostConnectSteps(Channel channel, RpcConnectionProperties props) { if (channel != null && channel.isConnected()) { if (props.getPeerCert() != null) logger.trace("kraken-rpc: connected with peer {}", props.getPeerCert().getSubjectDN().getName()); return handler.newClientConnection(channel, props); } return null; } public void close() { if (ownHandler) handler.stop(); if (conn != null && conn.isOpen()) { conn.close(); } conn = null; if (bootstrap != null) { bootstrap.releaseExternalResources(); } bootstrap = null; } }