/* * Created on Mar 13, 2007 Copyright (C) 2001-6, Anthony Harrison anh23@pitt.edu * (jactr.org) This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the License, * or (at your option) any later version. This library is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU Lesser General Public License for more details. You should have * received a copy of the GNU Lesser General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ package org.jactr.tools.async.common; import java.lang.reflect.Constructor; import java.net.SocketAddress; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.commonreality.net.handler.IMessageHandler; import org.commonreality.net.protocol.IProtocolConfiguration; import org.commonreality.net.service.INetworkService; import org.commonreality.net.session.ISessionInfo; import org.commonreality.net.session.ISessionListener; import org.commonreality.net.transport.ITransportProvider; import org.commonreality.netty.protocol.SerializingProtocol; import org.commonreality.netty.service.ClientService; import org.commonreality.netty.transport.NIOTransportProvider; import org.jactr.core.concurrent.GeneralThreadFactory; import org.jactr.core.utils.parameter.IParameterized; import org.jactr.tools.async.credentials.ICredentials; import org.jactr.tools.async.credentials.PlainTextCredentials; /** * Both RemoteInterface and ShadowControll rely on this common substrate that * deals with all the networking * * @author developer */ public abstract class NetworkedEndpoint implements IParameterized { /** * Logger definition */ static private final transient Log LOGGER = LogFactory .getLog(NetworkedEndpoint.class); public static final String TRANSPORT_CLASS = "transportClass"; public static final String PROTOCOL_CLASS = "protocolClass"; public static final String ADDRESS = "address"; public static final String SERVICE_CLASS = "serviceClass"; public static final String CREDENTAILS = "credentials"; public static final String CRED_CLASS = "credentialsClass"; private ITransportProvider _transport = new NIOTransportProvider(); private INetworkService _service = new ClientService(); private IProtocolConfiguration _protocol = new SerializingProtocol(); private Class<? extends ICredentials> _credentialsClass = PlainTextCredentials.class; private String _addressInformation; private String _credentialInformation; private ICredentials _actualCredentials; private SocketAddress _actualAddress; private volatile ISessionInfo<?> _sessionInfo; protected Map<Class<?>, IMessageHandler<?>> _defaultHandlers = new HashMap<Class<?>, IMessageHandler<?>>(); private ISessionListener _defaultListener; private Lock _lock = new ReentrantLock(); private Condition _connected = _lock .newCondition(); /** * */ public NetworkedEndpoint() { super(); createDefaultHandlers(); _defaultListener = createSessionListener(); } /** * Override to set what message handlers are used by this endpoint. */ protected void createDefaultHandlers() { } /** * return the actual backing map for the default handlers. Used to confiugre * the handlers (again, before the first connection), by non-extenders * * @return */ public Map<Class<?>, IMessageHandler<?>> getDefaultHandlers() { return _defaultHandlers; } /** * override to provide a session listener. defaults handles * * @return */ protected ISessionListener createSessionListener() { return new ISessionListener() { @Override public void opened(ISessionInfo<?> session) { sessionOpened(session); } @Override public void destroyed(ISessionInfo<?> session) { // TODO Auto-generated method stub } @Override public void created(ISessionInfo<?> session) { } @Override public void closed(ISessionInfo<?> session) { sessionClosed(session); } }; } public boolean waitForConnection(long timeOut) throws InterruptedException { try { _lock.lock(); long start = System.currentTimeMillis(); while (getSession() == null) { if (timeOut > 0 && System.currentTimeMillis() - start >= timeOut) break; if (timeOut > 0) _connected.await(timeOut, TimeUnit.MILLISECONDS); else _connected.await(); } return getSession() != null; } finally { _lock.unlock(); } } /** * The active session, if connected. Exposed publicly so that other tools can * hook into the existing connection * * @return */ public ISessionInfo<?> getActiveSession() { return getSession(); } protected ISessionInfo<?> getSession() { try { _lock.lock(); return _sessionInfo; } finally { _lock.unlock(); } } protected void sessionOpened(ISessionInfo<?> session) { setSession(session); } protected void sessionClosed(ISessionInfo<?> session) { setSession(null); } protected void setSession(ISessionInfo<?> session) { try { _lock.lock(); _sessionInfo = session; _connected.signalAll(); } finally { _lock.unlock(); } } /** * set the provider and possibly update the address information * * @param provider */ public void setTransportProvider(ITransportProvider provider) { _transport = provider; if (_addressInformation != null) { /* * split strings */ String[] address = _addressInformation.split(":"); _actualAddress = _transport.createAddress(address); } } /** * set the service for this end point (typically ClientServie or * ServerService) * * @param service */ public void setService(INetworkService service) { _service = service; } /** * set our protocol * * @param protocol */ public void setProtocol(IProtocolConfiguration protocol) { _protocol = protocol; } /** * set the string version of the address, if the transport has already been * provided, the acutal address will be recomputed * * @param addressInfo */ public void setAddressInfo(String addressInfo) { _addressInformation = addressInfo; if (_transport != null) { /* * split strings */ String[] address = _addressInformation.split(":"); _actualAddress = _transport.createAddress(address); } } /** * set the connection credentials * * @param credentialInfo */ public ICredentials setCredentialInformation(String credentialInfo) { _credentialInformation = credentialInfo; if (_credentialsClass != null) { String[] creds = _credentialInformation.split(":"); /* * create the credentials */ try { String[] obj = new String[0]; Constructor<? extends ICredentials> cons = _credentialsClass .getConstructor(new Class[] { obj.getClass() }); _actualCredentials = cons.newInstance(new Object[] { creds }); // getIOHandler().setCredentials(_actualCredentials); return _actualCredentials; } catch (Exception e) { throw new RuntimeException("Could not create valid credentials for " + _credentialInformation + " with " + _credentialsClass.getSimpleName(), e); } } return null; } /** * @param credClass */ public void setCredentialsClass(Class<? extends ICredentials> credClass) { _credentialsClass = credClass; if (_credentialInformation != null) setCredentialInformation(_credentialInformation); } /** * return the actual credentials that we are using * * @return */ public ICredentials getActualCredentials() { return _actualCredentials; } /** * return the socket address that we are actually connected to * * @return */ public SocketAddress getActualAddress() { return _actualAddress; } synchronized protected void connect() throws Exception { /* * let's connect */ if (_transport == null) throw new RuntimeException( "Must specify transport (IMINATransportProvider)"); if (_protocol == null) throw new RuntimeException( "Must specify protocol (IMINAProcotolConfiguration)"); if (_addressInformation == null) throw new RuntimeException("Must specify adderss information"); if (_service == null) throw new RuntimeException("Must specific service (INetworkService)"); if (_credentialInformation == null) throw new RuntimeException("Must specify credential information"); if (_credentialsClass == null) throw new RuntimeException("Must specify credentials provider class"); if (_actualCredentials == null) throw new RuntimeException("Actual credentials must be set"); try { _service.configure(_transport, _protocol, _defaultHandlers, _defaultListener, new GeneralThreadFactory("NetworkedEndpoint")); _actualAddress = _service.start(_actualAddress); } catch (Exception e) { _service = null; throw new RuntimeException("Could not start service", e); } } /** * wait for all the pending writes * * @throws Exception */ protected void disconnect() throws Exception { disconnect(false); } /** * try to establish the connection */ synchronized protected void disconnect(boolean force) throws Exception { try { if (LOGGER.isDebugEnabled()) LOGGER.debug("Waiting for pending writes"); ISessionInfo<?> session = getSession(); if (session != null) { if (!force) session.waitForPendingWrites(); /* * close all connections.. */ session.close(); } if (LOGGER.isDebugEnabled()) LOGGER.debug("Shutting down"); _service.stop(_actualAddress); } catch (Exception e) { throw new RuntimeException("Could not shutdown service ", e); } } /** * @see org.jactr.core.utils.parameter.IParameterized#getParameter(java.lang.String) */ public String getParameter(String key) { if (key.equalsIgnoreCase(ADDRESS)) return _addressInformation; if (key.equalsIgnoreCase(CREDENTAILS)) return _credentialInformation; if (key.equalsIgnoreCase(CRED_CLASS)) return _credentialsClass.getName(); if (key.equalsIgnoreCase(SERVICE_CLASS)) { if (_service != null) return _service.getClass().getName(); return null; } if (key.equalsIgnoreCase(PROTOCOL_CLASS)) { if (_protocol != null) return _protocol.getClass().getName(); return null; } if (key.equalsIgnoreCase(TRANSPORT_CLASS)) { if (_transport != null) return _transport.getClass().getName(); return null; } return null; } /** * @see org.jactr.core.utils.parameter.IParameterized#getPossibleParameters() */ public Collection<String> getPossibleParameters() { ArrayList<String> str = new ArrayList<String>(); str.add(ADDRESS); str.add(CREDENTAILS); str.add(CRED_CLASS); str.add(SERVICE_CLASS); str.add(PROTOCOL_CLASS); str.add(TRANSPORT_CLASS); return str; } /** * @see org.jactr.core.utils.parameter.IParameterized#getSetableParameters() */ public Collection<String> getSetableParameters() { return getPossibleParameters(); } /** * @see org.jactr.core.utils.parameter.IParameterized#setParameter(java.lang.String, * java.lang.String) */ public void setParameter(String key, String value) { if (ADDRESS.equalsIgnoreCase(key)) setAddressInfo(value); else if (CREDENTAILS.equalsIgnoreCase(key)) setCredentialInformation(value); else if (CRED_CLASS.equalsIgnoreCase(key)) setCredentialsClass(getClass(value)); else if (PROTOCOL_CLASS.equalsIgnoreCase(key)) setProtocol((IProtocolConfiguration) instance(value)); else if (TRANSPORT_CLASS.equalsIgnoreCase(key)) setTransportProvider((ITransportProvider) instance(value)); else if (SERVICE_CLASS.equalsIgnoreCase(key)) setService((INetworkService) instance(value)); } /** * utility for the instantiation of mina parameters * * @param className * @return */ protected Class getClass(String className) { try { return getClass().getClassLoader().loadClass(className); } catch (Exception e) { throw new RuntimeException("Could not find class " + className, e); } } /** * utility for the instantiation of mina parameters * * @param className * @return */ protected Object instance(String className) { try { Class c = getClass(className); return c.newInstance(); } catch (Exception e) { throw new RuntimeException("Could not instantiate " + className, e); } } }