Skip to content
Snippets Groups Projects
Commit 8f7d8939 authored by Patrick Dowler's avatar Patrick Dowler
Browse files

Merge branch 'ac2' of ssh://gimli2/srv/cadc/git/wopencadc into ac2

parents 5025ccb6 9a5989bb
Branches
Tags
No related merge requests found
Showing
with 845 additions and 264 deletions
...@@ -144,8 +144,7 @@ ...@@ -144,8 +144,7 @@
<pathelement path="${build}/test/class"/> <pathelement path="${build}/test/class"/>
<pathelement path="${jars}:${testingJars}"/> <pathelement path="${jars}:${testingJars}"/>
</classpath> </classpath>
<sysproperty key="ca.nrc.cadc.util.PropertiesReader.dir" value="test"/> <test name="ca.nrc.cadc.ac.admin.UserAdminTest" />
<test name="ca.nrc.cadc.ac.admin.integration.AdminIntTest" />
<formatter type="plain" usefile="false" /> <formatter type="plain" usefile="false" />
</junit> </junit>
</target> </target>
......
...@@ -136,7 +136,9 @@ public abstract class AbstractCommand implements PrivilegedAction<Object> ...@@ -136,7 +136,9 @@ public abstract class AbstractCommand implements PrivilegedAction<Object>
protected <T extends Principal> UserPersistence<T> getUserPersistence() protected <T extends Principal> UserPersistence<T> getUserPersistence()
{ {
System.setProperty("java.naming.factory.initial", ContextFactoryImpl.class.getName());
PluginFactory pluginFactory = new PluginFactory(); PluginFactory pluginFactory = new PluginFactory();
return pluginFactory.getUserPersistence(); return pluginFactory.createUserPersistence();
} }
} }
package ca.nrc.cadc.ac.admin;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.spi.InitialContextFactory;
import java.util.Hashtable;
/**
* A Simple ContextFactory.
*/
public class ContextFactoryImpl implements InitialContextFactory
{
public ContextFactoryImpl()
{
}
@Override
public Context getInitialContext(Hashtable environment)
throws NamingException
{
return new ContextImpl();
}
}
/*
************************************************************************
******************* CANADIAN ASTRONOMY DATA CENTRE *******************
************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES **************
*
* (c) 2011. (c) 2011.
* Government of Canada Gouvernement du Canada
* National Research Council Conseil national de recherches
* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6
* All rights reserved Tous droits réservés
*
* NRC disclaims any warranties, Le CNRC dénie toute garantie
* expressed, implied, or énoncée, implicite ou légale,
* statutory, of any kind with de quelque nature que ce
* respect to the software, soit, concernant le logiciel,
* including without limitation y compris sans restriction
* any warranty of merchantability toute garantie de valeur
* or fitness for a particular marchande ou de pertinence
* purpose. NRC shall not be pour un usage particulier.
* liable in any event for any Le CNRC ne pourra en aucun cas
* damages, whether direct or être tenu responsable de tout
* indirect, special or general, dommage, direct ou indirect,
* consequential or incidental, particulier ou général,
* arising from the use of the accessoire ou fortuit, résultant
* software. Neither the name de l'utilisation du logiciel. Ni
* of the National Research le nom du Conseil National de
* Council of Canada nor the Recherches du Canada ni les noms
* names of its contributors may de ses participants ne peuvent
* be used to endorse or promote être utilisés pour approuver ou
* products derived from this promouvoir les produits dérivés
* software without specific prior de ce logiciel sans autorisation
* written permission. préalable et particulière
* par écrit.
*
* This file is part of the Ce fichier fait partie du projet
* OpenCADC project. OpenCADC.
*
* OpenCADC is free software: OpenCADC est un logiciel libre ;
* you can redistribute it and/or vous pouvez le redistribuer ou le
* modify it under the terms of modifier suivant les termes de
* the GNU Affero General Public la “GNU Affero General Public
* License as published by the License” telle que publiée
* Free Software Foundation, par la Free Software Foundation
* either version 3 of the : soit la version 3 de cette
* License, or (at your option) licence, soit (à votre gré)
* any later version. toute version ultérieure.
*
* OpenCADC is distributed in the OpenCADC est distribué
* hope that it will be useful, dans l’espoir qu’il vous
* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE
* without even the implied GARANTIE : sans même la garantie
* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ
* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF
* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence
* General Public License for Générale Publique GNU Affero
* more details. pour plus de détails.
*
* You should have received Vous devriez avoir reçu une
* a copy of the GNU Affero copie de la Licence Générale
* General Public License along Publique GNU Affero avec
* with OpenCADC. If not, see OpenCADC ; si ce n’est
* <http://www.gnu.org/licenses/>. pas le cas, consultez :
* <http://www.gnu.org/licenses/>.
*
* $Revision: 5 $
*
************************************************************************
*/
package ca.nrc.cadc.ac.admin;
import javax.naming.Binding;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.Name;
import javax.naming.NameClassPair;
import javax.naming.NameParser;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
/**
* A Simple JNDI context.
*/
public class ContextImpl implements Context
{
Map<String,Object> map = new HashMap<String,Object>(1);
@Override
public Object lookup(String name) throws NamingException
{
return map.get(name);
}
@Override
public void bind(String name, Object value) throws NamingException
{
map.put(name, value);
}
@Override
public Object addToEnvironment(String arg0, Object arg1)
throws NamingException
{
return null;
}
@Override
public void bind(Name arg0, Object arg1) throws NamingException
{
}
@Override
public void close() throws NamingException
{
}
@Override
public Name composeName(Name arg0, Name arg1) throws NamingException
{
return null;
}
@Override
public String composeName(String arg0, String arg1)
throws NamingException
{
return null;
}
@Override
public Context createSubcontext(Name arg0) throws NamingException
{
// TODO Auto-generated method stub
return null;
}
@Override
public Context createSubcontext(String arg0) throws NamingException
{
return null;
}
@Override
public void destroySubcontext(Name arg0) throws NamingException
{
}
@Override
public void destroySubcontext(String arg0) throws NamingException
{
// TODO Auto-generated method stub
}
@Override
public Hashtable<?, ?> getEnvironment() throws NamingException
{
return null;
}
@Override
public String getNameInNamespace() throws NamingException
{
return null;
}
@Override
public NameParser getNameParser(Name arg0) throws NamingException
{
return null;
}
@Override
public NameParser getNameParser(String arg0) throws NamingException
{
return null;
}
@Override
public NamingEnumeration<NameClassPair> list(Name arg0)
throws NamingException
{
return null;
}
@Override
public NamingEnumeration<NameClassPair> list(String arg0)
throws NamingException
{
// TODO Auto-generated method stub
return null;
}
@Override
public NamingEnumeration<Binding> listBindings(Name arg0)
throws NamingException
{
return null;
}
@Override
public NamingEnumeration<Binding> listBindings(String arg0)
throws NamingException
{
return null;
}
@Override
public Object lookup(Name arg0) throws NamingException
{
// TODO Auto-generated method stub
return null;
}
@Override
public Object lookupLink(Name arg0) throws NamingException
{
return null;
}
@Override
public Object lookupLink(String arg0) throws NamingException
{
return null;
}
@Override
public void rebind(Name arg0, Object arg1) throws NamingException
{
}
@Override
public void rebind(String arg0, Object arg1) throws NamingException
{
}
@Override
public Object removeFromEnvironment(String arg0) throws NamingException
{
return null;
}
@Override
public void rename(Name arg0, Name arg1) throws NamingException
{
}
@Override
public void rename(String arg0, String arg1) throws NamingException
{
}
@Override
public void unbind(Name arg0) throws NamingException
{
}
@Override
public void unbind(String arg0) throws NamingException
{
}
}
\ No newline at end of file
# This are the configuration fields required by the unit tests ###############################################################
server = proc5-03.cadc.dao.nrc.ca #
# LDAP Connection and Pool Configuration
#
#
###############################################################
# Read-only connection pool
readOnly.servers = proc5-03.cadc.dao.nrc.ca
readOnly.poolInitSize = 1
readOnly.poolMaxSize = 2
readOnly.poolPolicy = roundRobin
readOnly.maxWait = 30000
readOnly.createIfNeeded = false
# Read-write connection pool
readWrite.servers = proc5-03.cadc.dao.nrc.ca
readWrite.poolInitSize = 1
readWrite.poolMaxSize = 2
readWrite.poolPolicy = roundRobin
readWrite.maxWait = 30000
readWrite.createIfNeeded = false
# Unbound-Read-only connection pool
unboundReadOnly.servers = proc5-03.cadc.dao.nrc.ca
unboundReadOnly.poolInitSize = 1
unboundReadOnly.poolMaxSize = 2
unboundReadOnly.poolPolicy = roundRobin
unboundReadOnly.maxWait = 30000
unboundReadOnly.createIfNeeded = false
# server configuration -- applies to all servers
#dbrcHost = devLdap
#port = 636
#proxyUser = uid=webproxy,ou=SpecialUsers,dc=canfar,dc=net
#usersDN = ou=Users,ou=ds,dc=canfar,dc=net
#userRequestsDN = ou=userRequests,ou=ds,dc=canfar,dc=net
#groupsDN = ou=Groups,ou=ds,dc=canfar,dc=net
#adminGroupsDN = ou=adminGroups,ou=ds,dc=canfar,dc=net
# tree without aci's
dbrcHost = devLdap
port = 389 port = 389
proxyUser = testproxy proxyUser = uid=testproxy,ou=SpecialUsers,dc=testcanfar
usersDn = ou=Users,ou=ds,dc=testcanfar usersDN = ou=Users,ou=ds,dc=testcanfar
userRequestsDN = ou=UserRequests,ou=ds,dc=testcanfar userRequestsDN = ou=UserRequests,ou=ds,dc=testcanfar
groupsDn = ou=Groups,ou=ds,dc=testcanfar groupsDN = ou=Groups,ou=ds,dc=testcanfar
adminGroupsDn = ou=adminGroups,ou=ds,dc=testcanfar adminGroupsDN = ou=adminGroups,ou=ds,dc=testcanfar
\ No newline at end of file \ No newline at end of file
...@@ -209,7 +209,7 @@ public class CmdLineParserTest ...@@ -209,7 +209,7 @@ public class CmdLineParserTest
} }
catch (UsageException e) catch (UsageException e)
{ {
String expected = "Missing command or ommand is not supported"; String expected = "Missing command or command is not supported";
Assert.assertTrue(e.getMessage().contains(expected)); Assert.assertTrue(e.getMessage().contains(expected));
} }
catch (Exception e) catch (Exception e)
......
...@@ -66,7 +66,7 @@ ...@@ -66,7 +66,7 @@
* *
************************************************************************ ************************************************************************
*/ */
package ca.nrc.cadc.ac.admin.integration; package ca.nrc.cadc.ac.admin;
import ca.nrc.cadc.ac.PersonalDetails; import ca.nrc.cadc.ac.PersonalDetails;
import ca.nrc.cadc.ac.User; import ca.nrc.cadc.ac.User;
...@@ -79,6 +79,7 @@ import ca.nrc.cadc.auth.DNPrincipal; ...@@ -79,6 +79,7 @@ import ca.nrc.cadc.auth.DNPrincipal;
import ca.nrc.cadc.auth.HttpPrincipal; import ca.nrc.cadc.auth.HttpPrincipal;
import ca.nrc.cadc.net.TransientException; import ca.nrc.cadc.net.TransientException;
import ca.nrc.cadc.util.Log4jInit; import ca.nrc.cadc.util.Log4jInit;
import ca.nrc.cadc.util.PropertiesReader;
import org.apache.log4j.Level; import org.apache.log4j.Level;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.junit.BeforeClass; import org.junit.BeforeClass;
...@@ -99,9 +100,9 @@ import static org.junit.Assert.assertEquals; ...@@ -99,9 +100,9 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
public class AdminIntTest public class UserAdminTest
{ {
private static final Logger log = Logger.getLogger(AdminIntTest.class); private static final Logger log = Logger.getLogger(UserAdminTest.class);
static final String EXEC_CMD = "./test/scripts/userAdminTest"; static final String EXEC_CMD = "./test/scripts/userAdminTest";
...@@ -117,6 +118,8 @@ public class AdminIntTest ...@@ -117,6 +118,8 @@ public class AdminIntTest
testCert = "build/test/class/cadcauthtest1.pem"; testCert = "build/test/class/cadcauthtest1.pem";
config = LdapConfig.getLdapConfig(); config = LdapConfig.getLdapConfig();
System.setProperty(PropertiesReader.class.getName() + ".dir", "test");
} }
@Test @Test
...@@ -350,15 +353,18 @@ public class AdminIntTest ...@@ -350,15 +353,18 @@ public class AdminIntTest
if (isPending) if (isPending)
{ {
userDAO.addPendingUser(userRequest); userDAO.addPendingUser(userRequest);
log.debug("added pending user: " + username);
} }
else else
{ {
userDAO.addUser(userRequest); userDAO.addUser(userRequest);
log.debug("added user: " + username);
} }
return null; return null;
} }
catch (Exception e) catch (Exception e)
{ {
log.error("Exception adding user: " + e.getMessage());
throw new Exception("Problems", e); throw new Exception("Problems", e);
} }
} }
...@@ -411,6 +417,7 @@ public class AdminIntTest ...@@ -411,6 +417,7 @@ public class AdminIntTest
<T extends Principal> LdapUserPersistence<T> getUserPersistence() <T extends Principal> LdapUserPersistence<T> getUserPersistence()
{ {
System.setProperty("java.naming.factory.initial", ContextFactoryImpl.class.getName());
return new LdapUserPersistence<T>(); return new LdapUserPersistence<T>();
} }
......
...@@ -20,6 +20,14 @@ readWrite.poolPolicy = roundRobin ...@@ -20,6 +20,14 @@ readWrite.poolPolicy = roundRobin
readWrite.maxWait = 30000 readWrite.maxWait = 30000
readWrite.createIfNeeded = false readWrite.createIfNeeded = false
# Unbound-Read-write connection pool
unboundReadOnly.servers = proc5-03.cadc.dao.nrc.ca
unboundReadOnly.poolInitSize = 1
unboundReadOnly.poolMaxSize = 1
unboundReadOnly.poolPolicy = roundRobin
unboundReadOnly.maxWait = 30000
unboundReadOnly.createIfNeeded = false
# server configuration -- applies to all servers # server configuration -- applies to all servers
dbrcHost = devLdap dbrcHost = devLdap
port = 636 port = 636
......
...@@ -21,6 +21,14 @@ readWrite.poolPolicy = <roundRobin || fewestConnections> ...@@ -21,6 +21,14 @@ readWrite.poolPolicy = <roundRobin || fewestConnections>
readWrite.maxWait = <timeout wait time in milliseconds> readWrite.maxWait = <timeout wait time in milliseconds>
readWrite.createIfNeeded = <true || false> Go beyond poolMaxSize readWrite.createIfNeeded = <true || false> Go beyond poolMaxSize
# Unbound-Read-only connection pool
unboundReadOnly.servers = <list of ldap servers for readonly unbound access>
unboundReadOnly.poolInitSize = <number of initial connections in the readonly pool>
unboundReadOnly.poolMaxSize = <maximum number of connections in the readonly pool>
unboundReadOnly.poolPolicy = <roundRobin || fewestConnections>
unboundReadOnly.maxWait = <timeout wait time in milliseconds>
unboundReadOnly.createIfNeeded = <true || false> Go beyond poolMaxSize
# server configuration -- applies to all servers # server configuration -- applies to all servers
dbrcHost = <prodLdap || devLdap> dbrcHost = <prodLdap || devLdap>
port = <389 or 636> port = <389 or 636>
......
/*
************************************************************************
******************* CANADIAN ASTRONOMY DATA CENTRE *******************
************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES **************
*
* (c) 2014. (c) 2014.
* Government of Canada Gouvernement du Canada
* National Research Council Conseil national de recherches
* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6
* All rights reserved Tous droits réservés
*
* NRC disclaims any warranties, Le CNRC dénie toute garantie
* expressed, implied, or énoncée, implicite ou légale,
* statutory, of any kind with de quelque nature que ce
* respect to the software, soit, concernant le logiciel,
* including without limitation y compris sans restriction
* any warranty of merchantability toute garantie de valeur
* or fitness for a particular marchande ou de pertinence
* purpose. NRC shall not be pour un usage particulier.
* liable in any event for any Le CNRC ne pourra en aucun cas
* damages, whether direct or être tenu responsable de tout
* indirect, special or general, dommage, direct ou indirect,
* consequential or incidental, particulier ou général,
* arising from the use of the accessoire ou fortuit, résultant
* software. Neither the name de l'utilisation du logiciel. Ni
* of the National Research le nom du Conseil National de
* Council of Canada nor the Recherches du Canada ni les noms
* names of its contributors may de ses participants ne peuvent
* be used to endorse or promote être utilisés pour approuver ou
* products derived from this promouvoir les produits dérivés
* software without specific prior de ce logiciel sans autorisation
* written permission. préalable et particulière
* par écrit.
*
* This file is part of the Ce fichier fait partie du projet
* OpenCADC project. OpenCADC.
*
* OpenCADC is free software: OpenCADC est un logiciel libre ;
* you can redistribute it and/or vous pouvez le redistribuer ou le
* modify it under the terms of modifier suivant les termes de
* the GNU Affero General Public la “GNU Affero General Public
* License as published by the License” telle que publiée
* Free Software Foundation, par la Free Software Foundation
* either version 3 of the : soit la version 3 de cette
* License, or (at your option) licence, soit (à votre gré)
* any later version. toute version ultérieure.
*
* OpenCADC is distributed in the OpenCADC est distribué
* hope that it will be useful, dans l’espoir qu’il vous
* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE
* without even the implied GARANTIE : sans même la garantie
* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ
* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF
* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence
* General Public License for Générale Publique GNU Affero
* more details. pour plus de détails.
*
* You should have received Vous devriez avoir reçu une
* a copy of the GNU Affero copie de la Licence Générale
* General Public License along Publique GNU Affero avec
* with OpenCADC. If not, see OpenCADC ; si ce n’est
* <http://www.gnu.org/licenses/>. pas le cas, consultez :
* <http://www.gnu.org/licenses/>.
*
* $Revision: 4 $
*
************************************************************************
*/
package ca.nrc.cadc.ac.server.ldap;
import java.util.Map;
/**
* The object that is bound in JNDI to hold the LDAP pools.
*/
public class ConnectionPools
{
private LdapConfig config;
private Map<String,LdapConnectionPool> pools;
public ConnectionPools(Map<String,LdapConnectionPool> pools, LdapConfig config)
{
this.pools = pools;
this.config = config;
}
public Map<String,LdapConnectionPool> getPools()
{
return pools;
}
public LdapConfig getConfig()
{
return config;
}
}
...@@ -81,7 +81,7 @@ import ca.nrc.cadc.util.MultiValuedProperties; ...@@ -81,7 +81,7 @@ import ca.nrc.cadc.util.MultiValuedProperties;
import ca.nrc.cadc.util.PropertiesReader; import ca.nrc.cadc.util.PropertiesReader;
/** /**
* Reads and stores the LDAP configuration information. The information * Reads and stores the LDAP configuration information.
* *
* @author adriand * @author adriand
* *
...@@ -94,6 +94,7 @@ public class LdapConfig ...@@ -94,6 +94,7 @@ public class LdapConfig
public static final String READONLY_PREFIX = "readOnly."; public static final String READONLY_PREFIX = "readOnly.";
public static final String READWRITE_PREFIX = "readWrite."; public static final String READWRITE_PREFIX = "readWrite.";
public static final String UB_READONLY_PREFIX = "unboundReadOnly.";
public static final String POOL_SERVERS = "servers"; public static final String POOL_SERVERS = "servers";
public static final String POOL_INIT_SIZE = "poolInitSize"; public static final String POOL_INIT_SIZE = "poolInitSize";
public static final String POOL_MAX_SIZE = "poolMaxSize"; public static final String POOL_MAX_SIZE = "poolMaxSize";
...@@ -151,6 +152,23 @@ public class LdapConfig ...@@ -151,6 +152,23 @@ public class LdapConfig
return createIfNeeded; return createIfNeeded;
} }
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append(" Servers: ");
for (String server : servers)
{
sb.append(" [" + server + "]");
}
sb.append(" initSize: " + initSize);
sb.append(" maxSize: " + maxSize);
sb.append(" policy: " + policy);
sb.append(" maxWait: " + maxWait);
sb.append(" createIfNeeded: " + createIfNeeded);
return sb.toString();
}
@Override @Override
public boolean equals(Object other) public boolean equals(Object other)
{ {
...@@ -183,6 +201,7 @@ public class LdapConfig ...@@ -183,6 +201,7 @@ public class LdapConfig
private LdapPool readOnlyPool = new LdapPool(); private LdapPool readOnlyPool = new LdapPool();
private LdapPool readWritePool = new LdapPool(); private LdapPool readWritePool = new LdapPool();
private LdapPool unboundReadOnlyPool = new LdapPool();
private int port; private int port;
private String usersDN; private String usersDN;
private String userRequestsDN; private String userRequestsDN;
...@@ -204,16 +223,15 @@ public class LdapConfig ...@@ -204,16 +223,15 @@ public class LdapConfig
public static LdapConfig getLdapConfig() public static LdapConfig getLdapConfig()
{ {
return getLdapConfig(CONFIG); return loadLdapConfig(CONFIG);
} }
public static LdapConfig getLdapConfig(String ldapProperties) public static LdapConfig loadLdapConfig(String ldapProperties)
{ {
logger.debug("Reading LDAP properties from: " + ldapProperties); logger.debug("Reading LDAP properties from: " + ldapProperties);
PropertiesReader pr = new PropertiesReader(ldapProperties); PropertiesReader pr = new PropertiesReader(ldapProperties);
MultiValuedProperties config = pr.getAllProperties(); MultiValuedProperties config = pr.getAllProperties();
if (config == null || config.keySet() == null) if (config == null || config.keySet() == null)
{ {
throw new RuntimeException("failed to read any LDAP property "); throw new RuntimeException("failed to read any LDAP property ");
...@@ -221,19 +239,9 @@ public class LdapConfig ...@@ -221,19 +239,9 @@ public class LdapConfig
LdapConfig ldapConfig = new LdapConfig(); LdapConfig ldapConfig = new LdapConfig();
ldapConfig.readOnlyPool.servers = getMultiProperty(pr, READONLY_PREFIX + POOL_SERVERS); loadPoolConfig(ldapConfig.readOnlyPool, pr, READONLY_PREFIX);
ldapConfig.readOnlyPool.initSize = Integer.valueOf(getProperty(pr, READONLY_PREFIX + POOL_INIT_SIZE)); loadPoolConfig(ldapConfig.readWritePool, pr, READWRITE_PREFIX);
ldapConfig.readOnlyPool.maxSize = Integer.valueOf(getProperty(pr, READONLY_PREFIX + POOL_MAX_SIZE)); loadPoolConfig(ldapConfig.unboundReadOnlyPool, pr, UB_READONLY_PREFIX);
ldapConfig.readOnlyPool.policy = PoolPolicy.valueOf(getProperty(pr, READONLY_PREFIX + POOL_POLICY));
ldapConfig.readOnlyPool.maxWait = Long.valueOf(getProperty(pr, READONLY_PREFIX + MAX_WAIT));
ldapConfig.readOnlyPool.createIfNeeded = Boolean.valueOf(getProperty(pr, READONLY_PREFIX + CREATE_IF_NEEDED));
ldapConfig.readWritePool.servers = getMultiProperty(pr, READWRITE_PREFIX + POOL_SERVERS);
ldapConfig.readWritePool.initSize = Integer.valueOf(getProperty(pr, READWRITE_PREFIX + POOL_INIT_SIZE));
ldapConfig.readWritePool.maxSize = Integer.valueOf(getProperty(pr, READWRITE_PREFIX + POOL_MAX_SIZE));
ldapConfig.readWritePool.policy = PoolPolicy.valueOf(getProperty(pr, READWRITE_PREFIX + POOL_POLICY));
ldapConfig.readWritePool.maxWait = Long.valueOf(getProperty(pr, READONLY_PREFIX + MAX_WAIT));
ldapConfig.readWritePool.createIfNeeded = Boolean.valueOf(getProperty(pr, READONLY_PREFIX + CREATE_IF_NEEDED));
ldapConfig.dbrcHost = getProperty(pr, LDAP_DBRC_ENTRY); ldapConfig.dbrcHost = getProperty(pr, LDAP_DBRC_ENTRY);
ldapConfig.port = Integer.valueOf(getProperty(pr, LDAP_PORT)); ldapConfig.port = Integer.valueOf(getProperty(pr, LDAP_PORT));
...@@ -265,6 +273,16 @@ public class LdapConfig ...@@ -265,6 +273,16 @@ public class LdapConfig
return ldapConfig; return ldapConfig;
} }
private static void loadPoolConfig(LdapPool pool, PropertiesReader pr, String prefix)
{
pool.servers = getMultiProperty(pr, prefix + POOL_SERVERS);
pool.initSize = Integer.valueOf(getProperty(pr, prefix + POOL_INIT_SIZE));
pool.maxSize = Integer.valueOf(getProperty(pr, prefix + POOL_MAX_SIZE));
pool.policy = PoolPolicy.valueOf(getProperty(pr, prefix + POOL_POLICY));
pool.maxWait = Long.valueOf(getProperty(pr, prefix + MAX_WAIT));
pool.createIfNeeded = Boolean.valueOf(getProperty(pr, prefix + CREATE_IF_NEEDED));
}
private static String getProperty(PropertiesReader properties, String key) private static String getProperty(PropertiesReader properties, String key)
{ {
String prop = properties.getFirstPropertyValue(key); String prop = properties.getFirstPropertyValue(key);
...@@ -321,6 +339,9 @@ public class LdapConfig ...@@ -321,6 +339,9 @@ public class LdapConfig
if ( !(l.readWritePool.equals(readWritePool))) if ( !(l.readWritePool.equals(readWritePool)))
return false; return false;
if ( !(l.unboundReadOnlyPool.equals(unboundReadOnlyPool)))
return false;
return true; return true;
} }
...@@ -338,6 +359,11 @@ public class LdapConfig ...@@ -338,6 +359,11 @@ public class LdapConfig
return readWritePool; return readWritePool;
} }
public LdapPool getUnboundReadOnlyPool()
{
return unboundReadOnlyPool;
}
public String getUsersDN() public String getUsersDN()
{ {
return this.usersDN; return this.usersDN;
...@@ -386,12 +412,13 @@ public class LdapConfig ...@@ -386,12 +412,13 @@ public class LdapConfig
public String toString() public String toString()
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("ldap dbrc host = "); sb.append(" ReadOnlyPool: [" + readOnlyPool + "]");
sb.append(dbrcHost); sb.append(" ReadWritePool: [" + readWritePool + "]");
sb.append(" port = "); sb.append(" UnboundReadOnlyPool: [" + unboundReadOnlyPool + "]");
sb.append(port); sb.append(" Port: " + port);
sb.append(" proxyUserDN = "); sb.append(" dbrcHost: " + dbrcHost);
sb.append(proxyUserDN); sb.append(" proxyUserDN: " + proxyUserDN);
return sb.toString(); return sb.toString();
} }
} }
...@@ -81,7 +81,6 @@ import com.unboundid.ldap.sdk.LDAPConnection; ...@@ -81,7 +81,6 @@ import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPConnectionOptions; import com.unboundid.ldap.sdk.LDAPConnectionOptions;
import com.unboundid.ldap.sdk.LDAPConnectionPool; import com.unboundid.ldap.sdk.LDAPConnectionPool;
import com.unboundid.ldap.sdk.LDAPException; import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPReadWriteConnectionPool;
import com.unboundid.ldap.sdk.RoundRobinServerSet; import com.unboundid.ldap.sdk.RoundRobinServerSet;
import com.unboundid.ldap.sdk.ServerSet; import com.unboundid.ldap.sdk.ServerSet;
import com.unboundid.ldap.sdk.SimpleBindRequest; import com.unboundid.ldap.sdk.SimpleBindRequest;
...@@ -98,51 +97,50 @@ public class LdapConnectionPool ...@@ -98,51 +97,50 @@ public class LdapConnectionPool
{ {
private static final Logger logger = Logger.getLogger(LdapConnectionPool.class); private static final Logger logger = Logger.getLogger(LdapConnectionPool.class);
private static final int POOL_CHECK_INTERVAL_MILLESCONDS = 10000; // 10 seconds
Profiler profiler = new Profiler(LdapConnectionPool.class); Profiler profiler = new Profiler(LdapConnectionPool.class);
protected LdapConfig currentConfig; protected LdapConfig currentConfig;
private LDAPReadWriteConnectionPool pool; private String poolName;
private LDAPConnectionPool pool;
private Object poolMonitor = new Object(); private Object poolMonitor = new Object();
private LDAPConnectionOptions connectionOptions; private LDAPConnectionOptions connectionOptions;
private long lastPoolCheck = System.currentTimeMillis(); public LdapConnectionPool(LdapConfig config, LdapPool poolConfig, String poolName, boolean boundPool)
public LdapConnectionPool()
{
this(LdapConfig.getLdapConfig());
}
public LdapConnectionPool(LdapConfig config)
{ {
if (config == null) if (config == null)
throw new IllegalArgumentException("config required"); throw new IllegalArgumentException("config required");
if (poolConfig == null)
throw new IllegalArgumentException("poolConfig required");
if (poolName == null)
throw new IllegalArgumentException("poolName required");
connectionOptions = new LDAPConnectionOptions(); connectionOptions = new LDAPConnectionOptions();
connectionOptions.setUseSynchronousMode(true); connectionOptions.setUseSynchronousMode(true);
connectionOptions.setAutoReconnect(true); connectionOptions.setAutoReconnect(true);
currentConfig = config; currentConfig = config;
this.poolName = poolName;
synchronized (poolMonitor) synchronized (poolMonitor)
{ {
pool = createPool(currentConfig); if (!boundPool)
profiler.checkpoint("Create pool"); pool = createPool(config, poolConfig, poolName, null, null);
else
pool = createPool(config, poolConfig, poolName, config.getAdminUserDN(), config.getAdminPasswd());
logger.debug(poolName + " statistics after create:\n" + pool.getConnectionPoolStatistics());
profiler.checkpoint("Create read only pool.");
} }
} }
public LDAPConnection getReadOnlyConnection() throws TransientException public LDAPConnection getConnection() throws TransientException
{ {
poolCheck();
try try
{ {
LDAPConnection conn = null; LDAPConnection conn = null;
synchronized (poolMonitor) synchronized (poolMonitor)
{ {
conn = pool.getReadConnection(); conn = pool.getConnection();
} }
logger.debug("Read pool statistics after borrow:\n" + pool.getReadPoolStatistics()); logger.debug(poolName + " pool statistics after borrow:\n" + pool.getConnectionPoolStatistics());
profiler.checkpoint("get read only connection"); profiler.checkpoint("get " + poolName + " only connection");
conn.setConnectionOptions(connectionOptions); conn.setConnectionOptions(connectionOptions);
return conn; return conn;
...@@ -153,40 +151,10 @@ public class LdapConnectionPool ...@@ -153,40 +151,10 @@ public class LdapConnectionPool
} }
} }
public LDAPConnection getReadWriteConnection() throws TransientException public void releaseConnection(LDAPConnection conn)
{
poolCheck();
try
{
LDAPConnection conn = null;
synchronized (poolMonitor)
{ {
conn = pool.getWriteConnection(); pool.releaseConnection(conn);
} logger.debug(poolName + " pool statistics after release:\n" + pool.getConnectionPoolStatistics());
logger.debug("write pool statistics after borrow:\n" + pool.getWritePoolStatistics());
profiler.checkpoint("get read write connection");
conn.setConnectionOptions(connectionOptions);
return conn;
}
catch (LDAPException e)
{
throw new TransientException("Failed to get read write connection", e);
}
}
public void releaseReadOnlyConnection(LDAPConnection conn)
{
pool.releaseReadConnection(conn);
logger.debug("Read pool statistics after release:\n" + pool.getReadPoolStatistics());
}
public void releaseReadWriteConnection(LDAPConnection conn)
{
pool.releaseWriteConnection(conn);
logger.debug("write pool statistics after release:\n" + pool.getWritePoolStatistics());
} }
public LdapConfig getCurrentConfig() public LdapConfig getCurrentConfig()
...@@ -196,9 +164,9 @@ public class LdapConnectionPool ...@@ -196,9 +164,9 @@ public class LdapConnectionPool
public void shutdown() public void shutdown()
{ {
logger.debug("Shutting down pool"); logger.debug("Closing pool...");
pool.close(); pool.close();
profiler.checkpoint("Shutdown pool"); profiler.checkpoint("Pool closed.");
} }
@Override @Override
...@@ -209,104 +177,50 @@ public class LdapConnectionPool ...@@ -209,104 +177,50 @@ public class LdapConnectionPool
pool.close(); pool.close();
} }
private void poolCheck() private LDAPConnectionPool createPool(LdapConfig config, LdapPool poolConfig, String poolName, String bindID, String bindPW)
{
if (timeToCheckPool())
{
// check to see if the configuration has changed
logger.debug("checking for ldap config change");
LdapConfig newConfig = LdapConfig.getLdapConfig();
if (!newConfig.equals(currentConfig))
{
logger.debug("Detected ldap configuration change, rebuilding pools");
boolean poolRecreated = false;
final LDAPReadWriteConnectionPool oldPool = pool;
synchronized (poolMonitor)
{
// check to see if another thread has already
// done the work
if (timeToCheckPool())
{
this.currentConfig = newConfig;
pool = createPool(currentConfig);
profiler.checkpoint("Rebuild pool");
lastPoolCheck = System.currentTimeMillis();
poolRecreated = true;
}
}
if (poolRecreated)
{
// close the old pool in a separate thread
Runnable closeOldPool = new Runnable()
{
public void run()
{
logger.debug("Closing old pool...");
oldPool.close();
logger.debug("Old pool closed.");
}
};
Thread closePoolThread = new Thread(closeOldPool);
closePoolThread.start();
}
}
else
{
lastPoolCheck = System.currentTimeMillis();
}
}
}
private boolean timeToCheckPool()
{
return (System.currentTimeMillis() - lastPoolCheck) > POOL_CHECK_INTERVAL_MILLESCONDS;
}
private LDAPReadWriteConnectionPool createPool(LdapConfig config)
{
LDAPConnectionPool ro = createPool(config.getReadOnlyPool(), config);
LDAPConnectionPool rw = createPool(config.getReadOnlyPool(), config);
LDAPReadWriteConnectionPool pool = new LDAPReadWriteConnectionPool(ro, rw);
logger.debug("Read pool statistics after create:\n" + pool.getReadPoolStatistics());
logger.debug("Write pool statistics after create:\n" + pool.getWritePoolStatistics());
return pool;
}
private synchronized LDAPConnectionPool createPool(LdapPool pool, LdapConfig config)
{ {
try try
{ {
logger.debug("LDAP Config: " + config); logger.debug("LDAP Config: " + config);
String[] hosts = pool.getServers().toArray(new String[0]); String[] hosts = poolConfig.getServers().toArray(new String[0]);
int[] ports = new int[pool.getServers().size()]; int[] ports = new int[poolConfig.getServers().size()];
for (int i=0; i<pool.getServers().size(); i++) for (int i=0; i<poolConfig.getServers().size(); i++)
{ {
ports[i] = config.getPort(); ports[i] = config.getPort();
} }
ServerSet serverSet = null; ServerSet serverSet = null;
if (pool.getPolicy().equals(PoolPolicy.roundRobin)) if (poolConfig.getPolicy().equals(PoolPolicy.roundRobin))
{ {
serverSet = new RoundRobinServerSet(hosts, ports, LdapDAO.getSocketFactory(config)); serverSet = new RoundRobinServerSet(hosts, ports, LdapDAO.getSocketFactory(config));
} }
else if (pool.getPolicy().equals(PoolPolicy.fewestConnections)) else if (poolConfig.getPolicy().equals(PoolPolicy.fewestConnections))
{ {
serverSet = new FewestConnectionsServerSet(hosts, ports, LdapDAO.getSocketFactory(config)); serverSet = new FewestConnectionsServerSet(hosts, ports, LdapDAO.getSocketFactory(config));
} }
else else
{ {
throw new IllegalStateException("Unconfigured pool policy: " + pool.getPolicy()); throw new IllegalStateException("Unconfigured pool policy: " + poolConfig.getPolicy());
} }
SimpleBindRequest bindRequest = new SimpleBindRequest(config.getAdminUserDN(), config.getAdminPasswd()); SimpleBindRequest bindRequest = null;
if (bindID != null && bindPW != null)
{
logger.debug("Binding pool as " + bindID);
bindRequest = new SimpleBindRequest(bindID, bindPW);
}
else
{
logger.debug("Binding pool annonymously");
bindRequest = new SimpleBindRequest();
}
LDAPConnectionPool connectionPool = new LDAPConnectionPool( LDAPConnectionPool connectionPool = new LDAPConnectionPool(
serverSet, bindRequest, pool.getInitSize(), pool.getMaxSize()); serverSet, bindRequest, poolConfig.getInitSize(), poolConfig.getMaxSize());
connectionPool.setCreateIfNecessary(pool.getCreateIfNeeded()); connectionPool.setCreateIfNecessary(poolConfig.getCreateIfNeeded());
connectionPool.setMaxWaitTimeMillis(pool.getMaxWait()); connectionPool.setMaxWaitTimeMillis(poolConfig.getMaxWait());
connectionPool.setConnectionPoolName(poolName);
return connectionPool; return connectionPool;
} }
......
...@@ -75,6 +75,7 @@ import ca.nrc.cadc.net.TransientException; ...@@ -75,6 +75,7 @@ import ca.nrc.cadc.net.TransientException;
import ca.nrc.cadc.profiler.Profiler; import ca.nrc.cadc.profiler.Profiler;
import com.unboundid.ldap.sdk.LDAPConnection; import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPConnectionPool;
import com.unboundid.ldap.sdk.LDAPException; import com.unboundid.ldap.sdk.LDAPException;
/** /**
...@@ -95,14 +96,19 @@ class LdapConnections ...@@ -95,14 +96,19 @@ class LdapConnections
Profiler profiler = new Profiler(LdapConnections.class); Profiler profiler = new Profiler(LdapConnections.class);
private LdapPersistence persistence; private LdapPersistence persistence;
private LdapConfig config;
private LdapConnectionPool readOnlyPool;
private LdapConnectionPool readWritePool;
private LdapConnectionPool unboundReadOnlyPool;
private LDAPConnection autoConfigReadOnlyConn; private LDAPConnection autoConfigReadOnlyConn;
private LDAPConnection autoConfigReadWriteConn; private LDAPConnection autoConfigReadWriteConn;
private LDAPConnection autoConfigUnboundReadOnlyConn;
private LdapConnectionPool pool;
private LDAPConnection manualConfigReadOnlyConn; private LDAPConnection manualConfigReadOnlyConn;
private LDAPConnection manualConfigReadWriteConn; private LDAPConnection manualConfigReadWriteConn;
private LDAPConnection manualConfigUnboundReadOnlyConn;
LdapConnections(LdapPersistence persistence) LdapConnections(LdapPersistence persistence)
{ {
...@@ -111,11 +117,12 @@ class LdapConnections ...@@ -111,11 +117,12 @@ class LdapConnections
this.persistence = persistence; this.persistence = persistence;
} }
LdapConnections(LdapConnectionPool pool) LdapConnections(LdapConfig config)
{ {
if (pool == null) if (config == null)
throw new RuntimeException("pool object is required"); throw new RuntimeException("config object is required");
this.pool = pool;
this.config = config;
} }
LDAPConnection getReadOnlyConnection() throws TransientException LDAPConnection getReadOnlyConnection() throws TransientException
...@@ -125,7 +132,7 @@ class LdapConnections ...@@ -125,7 +132,7 @@ class LdapConnections
if (autoConfigReadOnlyConn == null) if (autoConfigReadOnlyConn == null)
{ {
log.debug("Getting new auto config read only connection."); log.debug("Getting new auto config read only connection.");
autoConfigReadOnlyConn = persistence.getReadOnlyConnection(); autoConfigReadOnlyConn = persistence.getConnection(LdapPersistence.POOL_READONLY);
profiler.checkpoint("Get read only connection"); profiler.checkpoint("Get read only connection");
} }
else else
...@@ -136,10 +143,14 @@ class LdapConnections ...@@ -136,10 +143,14 @@ class LdapConnections
} }
else else
{ {
if (readOnlyPool == null)
{
readOnlyPool = new LdapConnectionPool(config, config.getReadOnlyPool(), LdapPersistence.POOL_READONLY, true);
}
if (manualConfigReadOnlyConn == null) if (manualConfigReadOnlyConn == null)
{ {
log.debug("Getting new manual config read only connection."); log.debug("Getting new manual config read only connection.");
manualConfigReadOnlyConn = pool.getReadOnlyConnection(); manualConfigReadOnlyConn = readOnlyPool.getConnection();
} }
else else
{ {
...@@ -156,7 +167,7 @@ class LdapConnections ...@@ -156,7 +167,7 @@ class LdapConnections
if (autoConfigReadWriteConn == null) if (autoConfigReadWriteConn == null)
{ {
log.debug("Getting new auto config read write connection."); log.debug("Getting new auto config read write connection.");
autoConfigReadWriteConn = persistence.getReadWriteConnection(); autoConfigReadWriteConn = persistence.getConnection(LdapPersistence.POOL_READWRITE);
profiler.checkpoint("Get read write connection"); profiler.checkpoint("Get read write connection");
} }
else else
...@@ -167,10 +178,14 @@ class LdapConnections ...@@ -167,10 +178,14 @@ class LdapConnections
} }
else else
{ {
if (readWritePool == null)
{
readWritePool = new LdapConnectionPool(config, config.getReadWritePool(), LdapPersistence.POOL_READWRITE, true);
}
if (manualConfigReadWriteConn == null) if (manualConfigReadWriteConn == null)
{ {
log.debug("Getting new manual config read write connection."); log.debug("Getting new manual config read write connection.");
manualConfigReadWriteConn = pool.getReadWriteConnection(); manualConfigReadWriteConn = readWritePool.getConnection();
} }
else else
{ {
...@@ -180,66 +195,80 @@ class LdapConnections ...@@ -180,66 +195,80 @@ class LdapConnections
} }
} }
void releaseConnections() LDAPConnection getUnboundReadOnlyConnection() throws TransientException
{ {
if (persistence != null) if (persistence != null)
{ {
if (autoConfigReadOnlyConn != null) if (autoConfigUnboundReadOnlyConn == null)
{ {
log.debug("Releasing read only auto config connection."); log.debug("Getting new auto config unbound read only connection.");
persistence.releaseReadOnlyConnection(autoConfigReadOnlyConn); autoConfigUnboundReadOnlyConn = persistence.getConnection(LdapPersistence.POOL_UNBOUNDREADONLY);
profiler.checkpoint("Release read only connection"); profiler.checkpoint("Get read write connection");
} }
if (autoConfigReadWriteConn != null) else
{ {
log.debug("Releasing read write auto config connection."); log.debug("Getting reused auto config unbound read only connection.");
persistence.releaseReadWriteConnection(autoConfigReadWriteConn);
profiler.checkpoint("Release read write connection");
} }
return autoConfigUnboundReadOnlyConn;
} }
else else
{ {
if (manualConfigReadOnlyConn != null) if (unboundReadOnlyPool == null)
{ {
log.debug("Releasing read only manual config connection."); unboundReadOnlyPool = new LdapConnectionPool(config, config.getUnboundReadOnlyPool(), LdapPersistence.POOL_UNBOUNDREADONLY, false);
pool.releaseReadOnlyConnection(manualConfigReadOnlyConn);
} }
if (manualConfigReadWriteConn != null) if (manualConfigUnboundReadOnlyConn == null)
{ {
log.debug("Releasing read write manual config connection."); log.debug("Getting new manual config unbound read only connection.");
pool.releaseReadWriteConnection(manualConfigReadWriteConn); manualConfigUnboundReadOnlyConn = unboundReadOnlyPool.getConnection();
} }
else
{
log.debug("Getting reused manual config unbound read only connection.");
}
return manualConfigUnboundReadOnlyConn;
} }
} }
void releaseConnectionsAfterError() void releaseConnections()
{ {
if (persistence != null) if (persistence != null)
{ {
if (autoConfigReadOnlyConn != null) if (autoConfigReadOnlyConn != null)
{ {
log.debug("Releasing read only auto config connection."); log.debug("Releasing read only auto config connection.");
persistence.releaseReadOnlyConnection(autoConfigReadOnlyConn); persistence.releaseConnection(LdapPersistence.POOL_READONLY, autoConfigReadOnlyConn);
profiler.checkpoint("Release read only connection"); profiler.checkpoint("Release read only connection");
} }
if (autoConfigReadWriteConn != null) if (autoConfigReadWriteConn != null)
{ {
log.debug("Releasing read write auto config connection."); log.debug("Releasing read write auto config connection.");
persistence.releaseReadWriteConnection(autoConfigReadWriteConn); persistence.releaseConnection(LdapPersistence.POOL_READWRITE, autoConfigReadWriteConn);
profiler.checkpoint("Release read write connection"); profiler.checkpoint("Release read write connection");
} }
if (autoConfigUnboundReadOnlyConn != null)
{
log.debug("Releasing read only auto config connection.");
persistence.releaseConnection(LdapPersistence.POOL_UNBOUNDREADONLY, autoConfigUnboundReadOnlyConn);
profiler.checkpoint("Release read only connection");
}
} }
else else
{ {
if (manualConfigReadOnlyConn != null) if (manualConfigReadOnlyConn != null)
{ {
log.debug("Releasing read only manual config connection."); log.debug("Releasing read only manual config connection.");
pool.releaseReadOnlyConnection(manualConfigReadOnlyConn); readOnlyPool.releaseConnection(manualConfigReadOnlyConn);
} }
if (manualConfigReadWriteConn != null) if (manualConfigReadWriteConn != null)
{ {
log.debug("Releasing read write manual config connection."); log.debug("Releasing read write manual config connection.");
pool.releaseReadWriteConnection(manualConfigReadWriteConn); readWritePool.releaseConnection(manualConfigReadWriteConn);
}
if (manualConfigUnboundReadOnlyConn != null)
{
log.debug("Releasing read only manual config connection.");
unboundReadOnlyPool.releaseConnection(manualConfigUnboundReadOnlyConn);
} }
} }
} }
...@@ -250,11 +279,23 @@ class LdapConnections ...@@ -250,11 +279,23 @@ class LdapConnections
@Override @Override
public void finalize() public void finalize()
{ {
if (pool != null) if (readOnlyPool != null)
{ {
log.debug("Closing manual config connection pool--should only see this " + log.debug("Closing manual config readonly connection pool--should only see this " +
"message when running unit tests."); "message when running unit tests.");
pool.shutdown(); readOnlyPool.shutdown();
}
if (readWritePool != null)
{
log.debug("Closing manual config readwrite connection pool--should only see this " +
"message when running unit tests.");
readWritePool.shutdown();
}
if (unboundReadOnlyPool != null)
{
log.debug("Closing manual config unboundreadonly connection pool--should only see this " +
"message when running unit tests.");
unboundReadOnlyPool.shutdown();
} }
} }
...@@ -263,8 +304,7 @@ class LdapConnections ...@@ -263,8 +304,7 @@ class LdapConnections
if (persistence != null) if (persistence != null)
return persistence.getCurrentConfig(); return persistence.getCurrentConfig();
else else
return pool.getCurrentConfig(); return config;
} }
} }
...@@ -109,6 +109,7 @@ public abstract class LdapDAO ...@@ -109,6 +109,7 @@ public abstract class LdapDAO
{ {
this.connections = connections; this.connections = connections;
config = connections.getCurrentConfig(); config = connections.getCurrentConfig();
logger.debug("New LdapDAO instance, config: " + config);
} }
public LDAPConnection getReadOnlyConnection() throws TransientException public LDAPConnection getReadOnlyConnection() throws TransientException
...@@ -121,6 +122,11 @@ public abstract class LdapDAO ...@@ -121,6 +122,11 @@ public abstract class LdapDAO
return connections.getReadWriteConnection(); return connections.getReadWriteConnection();
} }
public LDAPConnection getUnboundReadConnection() throws TransientException
{
return connections.getUnboundReadOnlyConnection();
}
public void close() public void close()
{ {
connections.releaseConnections(); connections.releaseConnections();
......
...@@ -69,6 +69,9 @@ ...@@ -69,6 +69,9 @@
package ca.nrc.cadc.ac.server.ldap; package ca.nrc.cadc.ac.server.ldap;
import java.util.HashMap;
import java.util.Map;
import javax.naming.InitialContext; import javax.naming.InitialContext;
import javax.naming.NameNotFoundException; import javax.naming.NameNotFoundException;
import javax.naming.NamingException; import javax.naming.NamingException;
...@@ -76,8 +79,10 @@ import javax.naming.NamingException; ...@@ -76,8 +79,10 @@ import javax.naming.NamingException;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import com.unboundid.ldap.sdk.LDAPConnection; import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPConnectionPool;
import com.unboundid.ldap.sdk.LDAPException; import com.unboundid.ldap.sdk.LDAPException;
import ca.nrc.cadc.ac.server.ldap.LdapConfig.LdapPool;
import ca.nrc.cadc.net.TransientException; import ca.nrc.cadc.net.TransientException;
import ca.nrc.cadc.profiler.Profiler; import ca.nrc.cadc.profiler.Profiler;
...@@ -87,12 +92,20 @@ import ca.nrc.cadc.profiler.Profiler; ...@@ -87,12 +92,20 @@ import ca.nrc.cadc.profiler.Profiler;
*/ */
public class LdapPersistence public class LdapPersistence
{ {
// pool names
public static final String POOL_READONLY = "readOnly";
public static final String POOL_READWRITE = "readWrite";
public static final String POOL_UNBOUNDREADONLY = "unboundReadOnly";
private static final Logger logger = Logger.getLogger(LdapPersistence.class); private static final Logger logger = Logger.getLogger(LdapPersistence.class);
private static final String LDAP_POOL_JNDI_NAME = LdapConnectionPool.class.getName(); private static final String LDAP_POOL_JNDI_NAME = ConnectionPools.class.getName();
private static final int POOL_CHECK_INTERVAL_MILLESCONDS = 10000; // 10 seconds
Profiler profiler = new Profiler(LdapPersistence.class); Profiler profiler = new Profiler(LdapPersistence.class);
private LdapConnectionPool pool; private long lastPoolCheck = System.currentTimeMillis();
private ConnectionPools pools;
// static monitor is required for when multiple LdapPersistence objects // static monitor is required for when multiple LdapPersistence objects
// are created. // are created.
...@@ -100,38 +113,31 @@ public class LdapPersistence ...@@ -100,38 +113,31 @@ public class LdapPersistence
LdapPersistence() LdapPersistence()
{ {
initPool(); initPools();
} }
protected LDAPConnection getReadOnlyConnection() throws TransientException protected LDAPConnection getConnection(String poolName) throws TransientException
{ {
return pool.getReadOnlyConnection(); poolCheck();
return pools.getPools().get(poolName).getConnection();
} }
protected LDAPConnection getReadWriteConnection() throws TransientException protected void releaseConnection(String poolName, LDAPConnection conn)
{ {
return pool.getReadWriteConnection(); pools.getPools().get(poolName).releaseConnection(conn);
}
protected void releaseReadOnlyConnection(LDAPConnection conn)
{
pool.releaseReadOnlyConnection(conn);
}
protected void releaseReadWriteConnection(LDAPConnection conn)
{
pool.releaseReadWriteConnection(conn);
} }
protected LdapConfig getCurrentConfig() protected LdapConfig getCurrentConfig()
{ {
return pool.currentConfig; return pools.getConfig();
} }
protected void shutdown() protected void shutdown()
{ {
// shutdown the pool // shutdown the pools
pool.shutdown(); pools.getPools().get(POOL_READONLY).shutdown();
pools.getPools().get(POOL_READWRITE).shutdown();
pools.getPools().get(POOL_UNBOUNDREADONLY).shutdown();
// unbind the pool // unbind the pool
try try
...@@ -141,31 +147,31 @@ public class LdapPersistence ...@@ -141,31 +147,31 @@ public class LdapPersistence
} }
catch (NamingException e) catch (NamingException e)
{ {
logger.warn("Could not unbind ldap pool", e); logger.warn("Could not unbind ldap pools", e);
} }
} }
private void initPool() private void initPools()
{ {
try try
{ {
pool = lookupPool(); pools = lookupPool();
logger.debug("Pool from JNDI lookup: " + pool); logger.debug("Pool from JNDI lookup: " + pools);
if (pool == null) if (pools == null)
{ {
synchronized (jndiMonitor) synchronized (jndiMonitor)
{ {
pool = lookupPool(); pools = lookupPool();
logger.debug("Pool from second JNDI lookup: " + pool); logger.debug("Pool from second JNDI lookup: " + pools);
if (pool == null) if (pools == null)
{ {
pool = new LdapConnectionPool(); LdapConfig config = LdapConfig.getLdapConfig();
profiler.checkpoint("Created LDAP connection pool"); pools = createPools(config);
InitialContext ic = new InitialContext(); InitialContext ic = new InitialContext();
ic.bind(LDAP_POOL_JNDI_NAME, pool); ic.bind(LDAP_POOL_JNDI_NAME, pools);
profiler.checkpoint("Bound LDAP pool to JNDI"); profiler.checkpoint("Bound LDAP pools to JNDI");
logger.debug("Bound LDAP pool to JNDI"); logger.debug("Bound LDAP pools to JNDI");
} }
} }
} }
...@@ -177,12 +183,25 @@ public class LdapPersistence ...@@ -177,12 +183,25 @@ public class LdapPersistence
} }
} }
private LdapConnectionPool lookupPool() throws NamingException private ConnectionPools createPools(LdapConfig config)
{
Map<String,LdapConnectionPool> poolMap = new HashMap<String,LdapConnectionPool>(3);
poolMap.put(POOL_READONLY, new LdapConnectionPool(
config, config.getReadOnlyPool(), POOL_READONLY, true));
poolMap.put(POOL_READWRITE, new LdapConnectionPool(
config, config.getReadWritePool(), POOL_READWRITE, true));
poolMap.put(POOL_UNBOUNDREADONLY, new LdapConnectionPool(
config, config.getUnboundReadOnlyPool(), POOL_UNBOUNDREADONLY, false));
profiler.checkpoint("Created 3 LDAP connection pools");
return new ConnectionPools(poolMap, config);
}
private ConnectionPools lookupPool() throws NamingException
{ {
try try
{ {
InitialContext ic = new InitialContext(); InitialContext ic = new InitialContext();
return (LdapConnectionPool) ic.lookup(LDAP_POOL_JNDI_NAME); return (ConnectionPools) ic.lookup(LDAP_POOL_JNDI_NAME);
} }
catch (NameNotFoundException e) catch (NameNotFoundException e)
{ {
...@@ -190,4 +209,79 @@ public class LdapPersistence ...@@ -190,4 +209,79 @@ public class LdapPersistence
} }
} }
private void poolCheck() throws TransientException
{
if (timeToCheckPool())
{
// check to see if the configuration has changed
logger.debug("checking for ldap config change");
LdapConfig newConfig = LdapConfig.getLdapConfig();
if (!newConfig.equals(pools.getConfig()))
{
logger.debug("Detected ldap configuration change, rebuilding pools");
boolean poolRecreated = false;
final ConnectionPools oldPools = pools;
synchronized (jndiMonitor)
{
// check to see if another thread has already
// done the work
if (timeToCheckPool())
{
try
{
ConnectionPools newPools = createPools(newConfig);
InitialContext ic = new InitialContext();
try
{
ic.unbind(LDAP_POOL_JNDI_NAME);
}
catch (NamingException e)
{
logger.warn("Could not unbind previous JNDI instance", e);
}
ic.bind(LDAP_POOL_JNDI_NAME, pools);
profiler.checkpoint("Rebuild pools");
lastPoolCheck = System.currentTimeMillis();
pools = newPools;
poolRecreated = true;
}
catch (NamingException e)
{
logger.debug("JNDI Naming Exception: " + e.getMessage());
throw new TransientException("JNDI Naming Exception", e);
}
}
}
if (poolRecreated)
{
// close the old pool in a separate thread
Runnable closeOldPools = new Runnable()
{
public void run()
{
logger.debug("Closing old pools...");
oldPools.getPools().get(POOL_READONLY).shutdown();
oldPools.getPools().get(POOL_READWRITE).shutdown();
oldPools.getPools().get(POOL_UNBOUNDREADONLY).shutdown();
logger.debug("Old pools closed.");
}
};
Thread closePoolsThread = new Thread(closeOldPools);
closePoolsThread.start();
}
}
else
{
lastPoolCheck = System.currentTimeMillis();
}
}
}
private boolean timeToCheckPool()
{
return (System.currentTimeMillis() - lastPoolCheck) > POOL_CHECK_INTERVAL_MILLESCONDS;
}
} }
...@@ -213,11 +213,14 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO ...@@ -213,11 +213,14 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO
{ {
BindRequest bindRequest = new SimpleBindRequest( BindRequest bindRequest = new SimpleBindRequest(
getUserDN(username, config.getUsersDN()), password); getUserDN(username, config.getUsersDN()), password);
//
String server = config.getReadOnlyPool().getServers().get(0); // String server = config.getReadOnlyPool().getServers().get(0);
int port = config.getPort(); // int port = config.getPort();
LDAPConnection conn = new LDAPConnection(LdapDAO.getSocketFactory(config), server, // LDAPConnection conn = new LDAPConnection(LdapDAO.getSocketFactory(config), server,
config.getPort()); // config.getPort());
// BindResult bindResult = conn.bind(bindRequest);
LDAPConnection conn = this.getUnboundReadConnection();
BindResult bindResult = conn.bind(bindRequest); BindResult bindResult = conn.bind(bindRequest);
if (bindResult != null && bindResult.getResultCode() == ResultCode.SUCCESS) if (bindResult != null && bindResult.getResultCode() == ResultCode.SUCCESS)
...@@ -472,9 +475,10 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO ...@@ -472,9 +475,10 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO
} }
SearchResultEntry searchResult = null; SearchResultEntry searchResult = null;
Filter filter = null;
try try
{ {
Filter filter = Filter.createEqualityFilter(searchField, userID.getName()); filter = Filter.createEqualityFilter(searchField, userID.getName());
logger.debug("search filter: " + filter); logger.debug("search filter: " + filter);
SearchRequest searchRequest = SearchRequest searchRequest =
...@@ -494,12 +498,29 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO ...@@ -494,12 +498,29 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO
LdapDAO.checkLdapResult(e.getResultCode()); LdapDAO.checkLdapResult(e.getResultCode());
} }
if (searchResult == null)
{
// determine if the user is not there of if the calling user
// doesn't have permission to see it
SearchRequest searchRequest =
new SearchRequest(usersDN, SearchScope.ONE, filter, userAttribs);
try
{
searchResult = getReadOnlyConnection().searchForEntry(searchRequest);
}
catch (LDAPException e)
{
LdapDAO.checkLdapResult(e.getResultCode());
}
if (searchResult == null) if (searchResult == null)
{ {
String msg = "User not found " + userID.toString(); String msg = "User not found " + userID.toString();
logger.debug(msg); logger.debug(msg);
throw new UserNotFoundException(msg); throw new UserNotFoundException(msg);
} }
throw new AccessControlException("Permission denied");
}
User<T> user = new User<T>(userID); User<T> user = new User<T>(userID);
String username = searchResult.getAttributeValue(userLdapAttrib.get(HttpPrincipal.class)); String username = searchResult.getAttributeValue(userLdapAttrib.get(HttpPrincipal.class));
...@@ -795,9 +816,10 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO ...@@ -795,9 +816,10 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO
new PasswordModifyExtendedRequest( new PasswordModifyExtendedRequest(
userDN.toNormalizedString(), oldPassword, newPassword, controls); userDN.toNormalizedString(), oldPassword, newPassword, controls);
String server = config.getReadWritePool().getServers().get(0); LdapConfig ldapConfig = LdapConfig.getLdapConfig();
int port = config.getPort(); String server = ldapConfig.getReadWritePool().getServers().get(0);
LDAPConnection conn = new LDAPConnection(LdapDAO.getSocketFactory(config), server, port); int port = ldapConfig.getPort();
LDAPConnection conn = new LDAPConnection(LdapDAO.getSocketFactory(ldapConfig), server, port);
PasswordModifyExtendedResult passwordModifyResult = (PasswordModifyExtendedResult) PasswordModifyExtendedResult passwordModifyResult = (PasswordModifyExtendedResult)
conn.processExtendedOperation(passwordModifyRequest); conn.processExtendedOperation(passwordModifyRequest);
......
...@@ -19,6 +19,14 @@ readWrite.poolPolicy = roundRobin ...@@ -19,6 +19,14 @@ readWrite.poolPolicy = roundRobin
readWrite.maxWait = 30000 readWrite.maxWait = 30000
readWrite.createIfNeeded = false readWrite.createIfNeeded = false
# Unbound-Read-write connection pool
unboundReadOnly.servers = proc5-03.cadc.dao.nrc.ca
unboundReadOnly.poolInitSize = 1
unboundReadOnly.poolMaxSize = 1
unboundReadOnly.poolPolicy = roundRobin
unboundReadOnly.maxWait = 30000
unboundReadOnly.createIfNeeded = false
# server configuration -- applies to all servers # server configuration -- applies to all servers
dbrcHost = devLdap dbrcHost = devLdap
port = 636 port = 636
......
...@@ -21,6 +21,14 @@ readWrite.poolPolicy = fewestConnections ...@@ -21,6 +21,14 @@ readWrite.poolPolicy = fewestConnections
readWrite.maxWait = 30000 readWrite.maxWait = 30000
readWrite.createIfNeeded = false readWrite.createIfNeeded = false
# Unbound-Read-only connection pool
unboundReadOnly.servers = server1 server2 server3
unboundReadOnly.poolInitSize = 3
unboundReadOnly.poolMaxSize = 8
unboundReadOnly.poolPolicy = roundRobin
unboundReadOnly.maxWait = 30000
unboundReadOnly.createIfNeeded = false
# server configuration -- applies to all servers # server configuration -- applies to all servers
dbrcHost = devLdap dbrcHost = devLdap
port = 389 port = 389
......
...@@ -21,6 +21,14 @@ readWrite.poolPolicy = fewestConnections ...@@ -21,6 +21,14 @@ readWrite.poolPolicy = fewestConnections
readWrite.maxWait = 30000 readWrite.maxWait = 30000
readWrite.createIfNeeded = false readWrite.createIfNeeded = false
# Unbound-Read-only connection pool
unboundReadOnly.servers = serverA serverB serverC
unboundReadOnly.poolInitSize = 0
unboundReadOnly.poolMaxSize = 1
unboundReadOnly.poolPolicy = fewestConnections
unboundReadOnly.maxWait = 30000
unboundReadOnly.createIfNeeded = false
# server configuration -- applies to all servers # server configuration -- applies to all servers
dbrcHost = devLdap dbrcHost = devLdap
port = 389 port = 389
......
...@@ -70,7 +70,6 @@ package ca.nrc.cadc.ac.server; ...@@ -70,7 +70,6 @@ package ca.nrc.cadc.ac.server;
import ca.nrc.cadc.ac.Role; import ca.nrc.cadc.ac.Role;
import ca.nrc.cadc.ac.server.web.groups.AddUserMemberActionTest; import ca.nrc.cadc.ac.server.web.groups.AddUserMemberActionTest;
import ca.nrc.cadc.auth.AuthenticationUtil;
import ca.nrc.cadc.auth.IdentityType; import ca.nrc.cadc.auth.IdentityType;
import ca.nrc.cadc.util.Log4jInit; import ca.nrc.cadc.util.Log4jInit;
import ca.nrc.cadc.uws.Parameter; import ca.nrc.cadc.uws.Parameter;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment