package org.jacorb.orb.giop;
/*
* JacORB - a free Java ORB
*
* Copyright (C) 1997-2014 Gerald Brose / The JacORB Team.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.jacorb.config.Configurable;
import org.jacorb.config.Configuration;
import org.jacorb.config.ConfigurationException;
import org.jacorb.orb.DefaultProfileSelector;
import org.jacorb.orb.ProfileSelector;
import org.jacorb.orb.diop.DIOPFactories;
import org.jacorb.orb.etf.ListenEndpoint;
import org.jacorb.orb.etf.ListenEndpoint.Protocol;
import org.jacorb.orb.etf.ProtocolAddressBase;
import org.jacorb.orb.factory.SocketFactoryManager;
import org.jacorb.orb.giop.TransportListener.Event;
import org.jacorb.orb.iiop.IIOPAddress;
import org.jacorb.util.ObjectUtil;
import org.omg.CORBA.BAD_PARAM;
import org.omg.ETF.Factories;
import org.slf4j.Logger;
/**
* This class manages Transports. On the one hand it creates them, and
* on the other it enforces an upper limit on the open transports.
*
* The class also receives notifications from threads that are about do use a
* Transport and notifies any interested listeners. "Use" is defined as
* sending (or handling) a request.
*
* @author Nicolas Noffke
* */
public class TransportManager
implements Configurable
{
/** the configuration object */
private org.jacorb.config.Configuration configuration = null;
/** configuration properties */
private Logger logger = null;
private List<String> factoryClassNames = null;
private ProfileSelector profileSelector = null;
private final SocketFactoryManager socketFactoryManager;
/**
* Denotes whether to use NIO for IIOP transport.
*/
private boolean useNonBlockingIIOPTransport = false;
/**
* Maps ETF Profile tags (Integer) to ETF Factories objects.
*/
private Map<Integer,Factories> factoriesMap = null;
/**
* List of all installed ETF Factories. This list contains an
* instance of each Factories class, ordered in the same way as
* they were specified in the jacorb.transport.factories property.
*/
private List<Factories> factoriesList = null;
/**
* List of all IIOP/SLIOP endpoint address-pairs
*/
private HashMap<Protocol,ArrayList<ListenEndpoint>> listenEndpointList = null;
/**
* The first listener (in a chain of instances), representing
* parties with interest in Transport events.
*/
private TransportListener listener = null;
public TransportManager( )
{
socketFactoryManager = new SocketFactoryManager();
}
@Override
public void configure(Configuration myConfiguration)
throws ConfigurationException
{
configuration = myConfiguration;
logger = configuration.getLogger("org.jacorb.orb.giop");
useNonBlockingIIOPTransport = configuration.getAttributeAsBoolean
("jacorb.connection.nonblocking", false);
socketFactoryManager.configure(configuration);
// get factory class names
factoryClassNames = configuration.getAttributeList("jacorb.transport.factories");
if (factoryClassNames.isEmpty())
{
factoryClassNames.add("org.jacorb.orb.iiop.IIOPFactories");
}
// pickup listen endpoints specified by arguments -ORBListenEndpoints
// and populate the array list listenEndpointList
updateListenEndpointAddresses();
// pickup the default OAAdress/OASSLAddress from prop file
// and add them to the end of the array list listenEndpointList as well.
updateDefaultEndpointAddresses();
// get profile selector info
profileSelector =
(ProfileSelector)configuration.getAttributeAsObject("jacorb.transport.client.selector");
if (profileSelector == null)
{
profileSelector = new DefaultProfileSelector();
}
}
public ProfileSelector getProfileSelector()
{
return profileSelector;
}
public SocketFactoryManager getSocketFactoryManager()
{
return socketFactoryManager;
}
/**
* Returns an ETF Factories object for the given tag, or null
* if no Factories class has been defined for this tag.
*/
public synchronized org.omg.ETF.Factories getFactories(int tag)
{
// This isn't ideal. If DIOPFactories was a full implementation then
// this class should be added to the
// TransportManager::loadFactories. This shortcut block (which is used
// by ParsedIOR) and the static caching in DIOPFactories wouldn't be
// needed.
if (tag == DIOPFactories.TAG_DIOP_UDP)
{
return DIOPFactories.getDIOPFactory();
}
if (factoriesMap == null)
{
loadFactories();
}
return factoriesMap.get (tag);
}
/**
* Returns a list of Factories for all configured transport plugins,
* in the same order as they were specified in the
* jacorb.transport.factories property.
*/
public synchronized List<Factories> getFactoriesList()
{
if (factoriesList == null)
{
loadFactories();
}
return Collections.unmodifiableList(factoriesList);
}
public ArrayList<ListenEndpoint> getListenEndpoints (Protocol p)
{
ArrayList<ListenEndpoint> endpoints = listenEndpointList.get (p);
if (endpoints == null)
{
if (logger.isDebugEnabled())
{
logger.debug ("Unable to find endpoints for " + p);
}
ListenEndpoint l = new ListenEndpoint();
l.setProtocol(p);
endpoints = new ArrayList<ListenEndpoint>();
endpoints.add(l);
listenEndpointList.put(p, endpoints);
}
return endpoints;
}
/**
* Build the factoriesMap and factoriesList.
*/
private void loadFactories()
{
if (configuration == null )
{
throw new org.omg.CORBA.BAD_INV_ORDER("TransportManager not configured!");
}
if (factoryClassNames == null )
{
throw new org.omg.CORBA.INTERNAL("factoryClassNames may not be null");
}
factoriesMap = new HashMap<Integer,Factories>();
factoriesList = new ArrayList<Factories>();
for (Iterator<String> i = factoryClassNames.iterator(); i.hasNext();)
{
String className = i.next();
Factories factories = instantiateFactories(className);
factoriesMap.put(factories.profile_tag(), factories); // NOPMD
factoriesList.add (factories);
}
}
/**
* Instantiates the given Factories class.
*/
private org.omg.ETF.Factories instantiateFactories (String className)
{
try
{
if (useNonBlockingIIOPTransport &&
"org.jacorb.orb.iiop.IIOPFactories".equals (className))
{
className = "org.jacorb.orb.nio.NIOFactories";
}
// ObjectUtil.classForName() uses the context class loader.
// This is important here because JacORB might be on the
// bootclasspath, and the external transport on the normal
// classpath.
Class<?> clazz = ObjectUtil.classForName(className);
Object instance = clazz.newInstance();
if (instance instanceof Configurable)
{
Configurable configurable = (Configurable)instance;
configurable.configure(configuration);
}
logger.debug("created org.omg.ETF.Factories: " + className);
return (Factories)instance;
}
catch (Exception e)
{
throw new BAD_PARAM
("could not instantiate Factories class " + className
+ ", exception: " + e);
}
}
public void notifyTransportListeners(GIOPConnection giopc)
{
if (listener != null)
{
listener.transportSelected (new Event (giopc));
}
}
public void addTransportListener(TransportListener tl)
{
if (logger.isInfoEnabled ())
{
logger.info ("Transport listener to add: " + tl);
}
if (tl != null)
{
addTransportListenerImpl (tl);
}
}
private synchronized void addTransportListenerImpl(final TransportListener tl)
{
if (listener == null)
{
listener = tl;
}
else
{
listener = new TransportListener ()
{
private final TransportListener next_ = listener;
@Override
public void transportSelected(Event event)
{
try
{
tl.transportSelected (event);
}
finally
{
next_.transportSelected (event);
}
}
};
}
}
private ProtocolAddressBase createProtocolAddress(String address_str) throws ConfigurationException
{
final IIOPAddress address = new IIOPAddress();
address.configure(configuration);
int proto_delim = address_str.indexOf (':');
Protocol proto;
try
{
proto = Protocol.valueOf(address_str.substring (0,proto_delim).toUpperCase(Locale.ENGLISH));
}
catch (IllegalArgumentException e)
{
throw new BAD_PARAM("Invalid protocol " + address_str);
}
address.setProtocol(proto);
final int addresss_start_ofs = proto_delim + 3;
if (!address.fromString(address_str.substring(addresss_start_ofs)))
{
throw new org.omg.CORBA.INTERNAL("Invalid protocol address string: " + address_str);
}
// set protocol string
return address;
}
/**
* Pick default OAAdress/OASSLAddress pair from property file
* and add them to the list of endpoint address list.
*/
private void updateDefaultEndpointAddresses() throws ConfigurationException
{
IIOPAddress address = null;
IIOPAddress ssl_address = null;
// If we already have an endpoint list defined there is no point creating a default.
if (listenEndpointList != null && listenEndpointList.size() > 0)
{
return;
}
ListenEndpoint defaultEndpoint = new ListenEndpoint();
String address_str = configuration.getAttribute("OAAddress",null);
if (address_str != null)
{
// build an iiop/ssliop protocol address.
// create_protocol_address will allow iiop and ssliop only
ProtocolAddressBase addr = createProtocolAddress(address_str);
address = (IIOPAddress)addr;
address.configure(configuration);
}
else
{
int oaPort = configuration.getAttributeAsInteger("OAPort",0);
String oaHost = configuration.getAttribute("OAIAddr","");
address = new IIOPAddress(oaHost,oaPort);
address.configure(configuration);
}
String ssl_address_str = configuration.getAttribute("OASSLAddress",null);
if (ssl_address_str != null)
{
// build a protocol address
ProtocolAddressBase ssl_addr = createProtocolAddress(ssl_address_str);
ssl_address = (IIOPAddress)ssl_addr;
ssl_address.configure(configuration);
}
else
{
int ssl_oaPort = configuration.getAttributeAsInteger("OASSLPort",0);
String ssl_oaHost = configuration.getAttribute("OAIAddr","");
ssl_address = new IIOPAddress(ssl_oaHost,ssl_oaPort);
ssl_address.configure(configuration);
}
if (address.getProtocol() == null)
{
address.setProtocol(Protocol.IIOP);
}
if (ssl_address.getProtocol() == null || ssl_address.getProtocol() == Protocol.SSLIOP)
{
ssl_address.setProtocol(Protocol.IIOP);
}
defaultEndpoint.setAddress(address);
defaultEndpoint.setSSLAddress(ssl_address);
defaultEndpoint.setProtocol(address.getProtocol());
ArrayList<ListenEndpoint> s = listenEndpointList.get(address.getProtocol());
s = new ArrayList<ListenEndpoint>();
s.add (defaultEndpoint);
listenEndpointList.put(address.getProtocol(), s);
}
/**
* Pickup endpoint addresses from command-line arguments -ORBListenEndPoints
*/
private void updateListenEndpointAddresses() throws ConfigurationException
{
listenEndpointList = new HashMap <Protocol, ArrayList<ListenEndpoint>>();
// get original argument list from ORB
String[] args = configuration.getORB().getArgs();
if (args != null)
{
for (int i = 0; i < args.length; i++)
{
if (args[i] == null) {
continue;
}
if (!args[i].equalsIgnoreCase("-ORBListenEndpoints"))
{
continue;
}
if (i+1 >= args.length || args[i+1] == null)
{
throw new BAD_PARAM("Invalid ORBListenEndpoint <value> format: -ORBListenEndpoints argument without value" );
}
String ep_args = args[i+1];
String ep_args_trim = ep_args.trim();
//check and remove single quotes if needed
if (ep_args_trim.charAt(0) == '\'' &&
ep_args_trim.charAt(ep_args_trim.length()-1) == '\'')
{
ep_args_trim = ep_args.trim().substring(1,ep_args.trim().length()-1);
}
// split up argument into segments using the semi-clone as delimiters
String[] seg_addr_list = ep_args_trim.split(";");
for (int xx = 0; xx < seg_addr_list.length; xx++)
{
String seg_args = seg_addr_list[xx].trim();
if (seg_args.equals(""))
{
continue;
}
// split up group of args into individual arg segments
// using the coma as delimiters
String[] indiv_list = seg_args.trim().split(",");
for (int xxx = 0; xxx < indiv_list.length; xxx++)
{
String address_str = null;
String ssl_port = null;
String host_str = "";
String[] options_args = null;
String addr_arg = indiv_list[xxx].trim();
if (addr_arg.equals(""))
{
continue;
}
// locate the first colon delimiter and
// pickup the protocol identifier string
int delim = addr_arg.indexOf(":");
Protocol protocol;
try
{
protocol = Protocol.valueOf (addr_arg.substring (0,delim).toUpperCase(Locale.ENGLISH));
}
catch (IllegalArgumentException e)
{
throw new BAD_PARAM("Invalid ORBListenEndPoints protocol " + addr_arg);
}
// locate the double slash delimiter
int db_slash = addr_arg.indexOf("//", delim+1);
if (db_slash == -1)
{
throw new BAD_PARAM("Invalid ORBListenEndPoints <value;value;...> format: listen endpoint \'" + addr_arg + "\' is malformed!" );
}
// check if additional option delimiter is present
// and pick up the protocol address
String dbs = "/";
if (protocol == Protocol.UIOP)
{
dbs = "|";
}
int opt_slash = addr_arg.indexOf(dbs, db_slash + 2);
if (opt_slash == -1)
{
address_str = addr_arg.substring(0);
}
else
{
address_str = addr_arg.substring(0, opt_slash);
}
// pick up optional arguments if present
if (opt_slash != -1)
{
options_args = addr_arg.substring(opt_slash+1).split("&");
for (int y = 0; y < options_args.length; y++)
{
String options_args_trim = options_args[xxx].trim();
int opt_delim = options_args_trim.indexOf('=');
if(opt_delim == -1)
{
throw new BAD_PARAM("error: listen endpoint options \'" + options_args[y] + "\' is malformed!");
}
else
{
String opt_str = options_args_trim.substring(0, opt_delim);
String opt_value = options_args_trim.substring(opt_delim+1);
if(opt_str.equalsIgnoreCase("ssl_port"))
{
ssl_port = opt_value;
}
else
{
throw new BAD_PARAM("error: listen endpoint options \'" + options_args[y] + "\' is not supported!");
}
}
}
}
if(address_str != null)
{
String address_trim = address_str.trim();
// build an iiop/ssliop protocol address.
// create_protocol_address will allow iiop and ssliop only
IIOPAddress address = null;
IIOPAddress ssl_address = null;
if (protocol == Protocol.IIOP)
{
ProtocolAddressBase addr1 = createProtocolAddress(address_trim);
if (addr1 instanceof IIOPAddress)
{
address = (IIOPAddress)addr1;
address.configure(configuration);
}
if (ssl_port != null)
{
int colon_delim = address_trim.indexOf(":");
int port_delim = address_trim.indexOf(":", colon_delim+2);
if (port_delim > 0)
{
host_str = address_trim.substring(colon_delim+3, port_delim);
}
else
{
host_str = "";
}
ssl_address = new IIOPAddress(host_str,Integer.parseInt(ssl_port));
ssl_address.configure(configuration);
}
}
else if(protocol == Protocol.SSLIOP)
{
ProtocolAddressBase addr2 = createProtocolAddress(address_trim);
if (addr2 instanceof IIOPAddress)
{
ssl_address = (IIOPAddress)addr2;
ssl_address.configure(configuration);
}
// Set the protocol to IIOP for using IIOP Protocol Factory
protocol = Protocol.IIOP;
}
else
{
ProtocolAddressBase addr1 = createProtocolAddress(address_trim);
if (addr1 instanceof IIOPAddress)
{
address = (IIOPAddress)addr1;
address.configure(configuration);
}
}
if (configuration.getAttributeAsBoolean("jacorb.security.support_ssl", false) &&
ssl_address == null)
{
throw new org.omg.CORBA.BAD_PARAM
("Error: an SSL port (ssl_port) is required when property jacorb.security.support_ssl is enabled");
}
ListenEndpoint listen_ep = new ListenEndpoint();
listen_ep.setAddress(address);
listen_ep.setSSLAddress(ssl_address);
listen_ep.setProtocol(protocol);
ArrayList<ListenEndpoint> s = listenEndpointList.get(protocol);
if ( s == null)
{
s = new ArrayList<ListenEndpoint>();
listenEndpointList.put(protocol, s);
}
s.add(listen_ep);
}
} // end for - inner
} //end for - outter
} //end for
} // end if
}
}