/*---------------------------------------------------------------------------
* Speculoos, LDAP to objet mapping.
* Copyright (C) 2006 Norsys and oQube
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Created on 26 sept. 2005
* Author: Arnaud Bailly
* --------------------------------------------------------------------------*/
package speculoos.manager;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import speculoos.core.Mapper;
import speculoos.core.MapperException;
import speculoos.spi.Source;
/**
* Base entry point for a business component.
* <p>
* There should be a single instance of MapperManager per instance of the
* business component. This would normally imply that we must use a
* <strong>Singleton </strong> pattern but we follow the widely accepted wisdom
* that <quote>Singleton is Evil! </quote>. It is the responsibility of the
* application developer to ensure that he uses one manager. If this is not the
* case, behavior is unspecified as there may be conflicts with resources
* associated with the manager that <em>are</em> statically allocated.
* </p>
* <p>
* As its name implies, a <code>MapperManager</code> manages a collection of
* <code>Mapper</code> objects. A manager can be in the following states:
* <ul>
* <li><strong>not configured </strong>: configuration has not completed</li>
* <li><strong>configured </strong>: configuration has been completed but
* manager has not been started</li>
* <li><strong>ready </strong>: configuration has been completed and manager is
* operating correctly</li>
* </ul>
* </p>
* <p>
* The design documentation gives a picture of the manager's life-cycle as an
* automaton. See also the documentation of the interfaces {@link Manage}and
* {@link Configure}for a description of their behavior as an automaton.
* </p>
* <h3>Constructor Issue</h3>
* <p>
* Following a run of JDepend, I found a cyclic dependency between Configurator and
* Manager due to constructor parameterized by Configurator. I removed the
* constructor.
* </p>
*
* @author nono
* @version $Id: MapperManager.java 259 2006-05-23 10:34:50Z /C=FR/ST=Nord/L=Lille/O=Norsys SA/OU=UE/CN=Arnaud Bailly/emailAddress=abailly@norsys.fr $
* @see <a href="http://c2.com/cgi-bin/wiki?SingletonsAreEvil">Singletons Are
* Evil </a>
* @see <a
* href="https://srvue.norsys.fr/doc/DAA_PTC_v410_FrameworkLDAP_v1maj0.doc">Architecture
* applicative </a>
*/
public class MapperManager implements Manage, Configure {
/*
* this manager's logger. Defaults to a logger named with this classes'name
*/
private static final Log log = LogFactory.getLog(MapperManager.class);
/*
* are we configured ?
*/
private boolean configured;
/*
* are we ready to operate ?
*/
private boolean ready;
/*
* mappers
*/
private Map /* < String, Mapper > */mappers = new HashMap();
/*
* data sources
*/
private Map /* < String, Source > */sources = new HashMap();
/*
* mappers-ds association
*/
private Map /* < Mapper, Source > */mapperToSource = new HashMap();
/*
* default parameter values
*/
private Map /* < String, Object > */parameters = new HashMap();
/*
* current parameter values
*/
private Map /* < String, Object > */environment = new HashMap();
/**
* Default constructor. Produces an unconfigured manager that cannot operate
* properly without being configured.
*
*/
public MapperManager() {
this.configured = false;
this.ready = false;
}
/**
* Starts operating the manager.
*
* @throws MapperException
*
* @throws MapperConfigurationException
*/
public void start() throws MapperException {
this.configured = true;
/* multiple calls to start do nothing */
if (this.ready)
return;
/* allocate resources */
for (Iterator it = sources.values().iterator(); it.hasNext();) {
Map env = new HashMap(parameters);
env.putAll(environment);
((Source) it.next()).start(env);
}
log.info("MapperManager started");
/* done */
this.ready = true;
}
/**
* Returns the mapper associated with the given name in this manager.
*
* @param name
* name identifying mapper. May not be null.
* @return a mapper that is ready to operate.
* @throws MapperConfigurationException
*/
public Mapper getMapper(String name) throws MapperException {
if (!configured || !ready)
throw new MapperConfigurationException(
"Manager is not ready to operate.");
Mapper mapper = (Mapper) mappers.get(name);
if (mapper == null)
throw new MapperConfigurationException("No mapper named " + name
+ " in this manager");
/* configure mapper through source */
Source src = (Source) mapperToSource.get(mapper.getName());
if (src == null)
throw new MapperConfigurationException(
"No source is associated with mapper " + name);
/* delegate preparation */
Mapper niou = src.create(name, environment);
return niou;
}
/**
* Releases a mapper object which allows clean up and reuse of its
* associated resources.
*
* @param mapper
* the mapper object to release.
*/
public void release(Mapper mapper) throws MapperException {
if (mapper == null)
throw new IllegalArgumentException("Cannot release null mapper !");
/* configure mapper through source */
Source src = (Source) mapperToSource.get(mapper.getName());
if (src == null)
throw new MapperConfigurationException(
"No source is associated with mapper " + mapper);
/* delegate release */
src.release(mapper);
}
/**
* Stops the manager.
*
*/
public void stop() {
if(!ready)
return;
/* deallocate resources */
/* allocate resources */
for (Iterator it = sources.values().iterator(); it.hasNext();) {
((Source) it.next()).stop();
}
/* done */
this.ready = false;
log.info("MapperManager stopped");
}
/*
* (non-Javadoc)
*
* @see speculoos.core.core.Configure#addMapper(java.lang.String,
* speculoos.core.core.Mapper)
*/
public void addMapper(String name, Mapper mapper)
throws MapperConfigurationException {
if (configured == true)
throw new MapperConfigurationException("Manager is configured");
if (mapper == null || name == null || "".equals(name))
throw new IllegalArgumentException(
"Invalid arguments :"+name+", " +mapper);
Object o = mappers.get(name);
if (o != null)
throw new MapperConfigurationException(name
+ " is already defined as a mapper in this context");
mappers.put(name, mapper);
if(log.isDebugEnabled())
log.debug("adding mapper "+name+", class "+mapper.getClass());
}
/*
* (non-Javadoc)
*
* @see speculoos.core.core.Configure#addParameter(java.lang.String,
* java.lang.Object)
*/
public void addParameter(String param, Object value)
throws MapperConfigurationException {
if (configured == true)
throw new MapperConfigurationException("Manager is configured");
if (param == null || "".equals(param) || value == null)
throw new IllegalArgumentException(
"Invalid arguments :"+param);
/* check */
Object o = parameters.get(param);
if (o != null)
throw new MapperConfigurationException(param
+ " is already defined in this context");
parameters.put(param, value);
if(log.isDebugEnabled())
log.debug("adding parameter "+param+", value="+value);
}
/*
* (non-Javadoc)
*
* @see speculoos.core.core.Configure#reset()
*/
public void reset() throws MapperConfigurationException {
if (ready)
throw new MapperConfigurationException(
"Cannot reset this manager: stop it first");
configured = false;
ready = false;
/* clear all maps */
parameters.clear();
sources.clear();
environment.clear();
mappers.clear();
mapperToSource.clear();
log.info("MapperManager reset");
}
/*
* (non-Javadoc)
*
* @see speculoos.core.core.Configure#set(java.lang.String,
* java.lang.Object)
*/
public Object set(String param, Object value)
throws MapperConfigurationException {
if(this.configured || this.ready)
throw new MapperConfigurationException("Cannot set a variable after configuration is set");
Object v;
try {
v = lookup(param);
} catch (MapperException e) {
throw new MapperConfigurationException("Name " + param
+ " is not a valid parameter in this context");
}
this.environment.put(param, value);
if(log.isDebugEnabled())
log.debug("setting parameter "+param+", value="+value);
return v;
}
/**
* Lookup name in this context. First name is looked up in
* <code>environment</code>, if not found, it is looked up in
* <code>parameters</code>. If not found, an exception is thrown.
*
* @param param
* the name of the parameter to look up
* @return value of this parameter in this environment
* @throws MapperException
* if <code>param</code> is not defined in this context.
*/
public Object lookup(String param) throws MapperException {
Object o = environment.get(param);
if (o == null) {
o = parameters.get(param);
if (o == null)
throw new MapperConfigurationException("Name " + param
+ " has not been found in this context");
}
return o;
}
/*
* (non-Javadoc)
*
* @see speculoos.core.core.Configure#setConfigured()
*/
public void setConfigured() throws MapperConfigurationException {
if (this.ready == true)
throw new MapperConfigurationException("Manager already configured");
this.configured = true;
log.info("MapperManager configured");
}
/*
* (non-Javadoc)
*
* @see speculoos.core.core.Configure#unset(java.lang.String)
*/
public Object unset(String param) throws MapperConfigurationException {
Object o;
try {
o = lookup(param);
} catch (MapperException e) {
throw new MapperConfigurationException(e.getMessage());
}
environment.remove(param);
if(log.isDebugEnabled())
log.debug("unsetting parameter "+param);
return o;
}
/*
* (non-Javadoc)
*
* @see speculoos.core.core.Configure#addSource(java.lang.String,
* speculoos.core.core.spi.Source)
*/
public void addSource(String name, Source source)
throws MapperConfigurationException {
if (configured == true)
throw new MapperConfigurationException("Manager is configured");
if (source == null || name == null || "".equals(name))
throw new IllegalArgumentException(
"Incorrect arguments :"+name+", "+source);
Object o = sources.get(name);
if (o != null)
throw new MapperConfigurationException("Name " + name
+ " is already used for a source");
/* store source */
sources.put(name, source);
if(log.isDebugEnabled())
log.debug("adding source "+name+", class="+source.getClass());
}
/*
* (non-Javadoc)
*
* @see speculoos.core.core.Configure#link(java.lang.String,
* java.lang.String)
*/
public void link(String mapName, String srcName)
throws MapperConfigurationException {
Mapper m = (Mapper) mappers.get(mapName);
if (m == null)
throw new MapperConfigurationException(mapName
+ " is not the name of a mapper");
Source src = (Source) sources.get(srcName);
if (src == null)
throw new MapperConfigurationException(srcName
+ " is not the name of a source");
/* link */
mapperToSource.put(m.getName(), src);
if(log.isDebugEnabled())
log.debug("linking map "+mapName+" to source "+src);
}
}