Index: topia2/src/java/org/codelutin/topia/service/TopiaApplicationServiceFactory.java diff -u /dev/null topia2/src/java/org/codelutin/topia/service/TopiaApplicationServiceFactory.java:1.1 --- /dev/null Fri May 25 16:11:09 2007 +++ topia2/src/java/org/codelutin/topia/service/TopiaApplicationServiceFactory.java Fri May 25 16:11:04 2007 @@ -0,0 +1,389 @@ +/* *##% + * Copyright (C) 2006 Code Lutin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +/** + * + */ +package org.codelutin.topia.service; + +import java.lang.reflect.Proxy; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.codelutin.topia.TopiaContext; +import org.codelutin.topia.TopiaException; +import org.codelutin.topia.TopiaNotFoundException; +import org.codelutin.topia.framework.TopiaUtil; +import org.codelutin.topia.service.clients.RMIProxy; +import org.codelutin.topia.service.clients.SOAPProxy; +import org.codelutin.topia.service.clients.XMLRPCProxy; + +/** + * TopiaServiceFactory.java + * + * Classe utilisee pour charger les services. + * + * Deux utilisations possibles : + *
  • client: pour avoir une interface sur un service local ou distant + *
  • serveur: pour avoir un service local au serveur + * + * Sert aussi au serveur pour declarer des services + * + * @author chatellier + * @version $Revision: 1.1 $ + * + * Last update : $Date: 2007/05/25 16:11:04 $ By : $Author: ndupont $ + */ +public class TopiaApplicationServiceFactory { + + /** Fichier de configuration par defaut */ + static final String DEFAULT_CONFIG_PROPERTIES = "TopiaContextImpl.properties"; + + /** Nom de la propriete de definition des services utilises */ + static final String TOPIA_APPLICATION_SERVICE_BEGIN = "topia.application.service."; + + /** Nom de la propriete de definition des service fournit */ + static final String TOPIA_APPLICATION_PROVIDE_BEGIN = "topia.application.provide."; + + /** + * Nom du dossier ou sont generer certains fichiers (doit etre dans le + * classpath ) + */ + public static String TOPIA_GENERATION_DIRECTORY = "topiagen"; + + /** Fichier de configuration */ + private static Properties config; + + /** Dispatcher (servers) */ + private static final TopiaServiceProvider mainDispatcher = new TopiaServiceProvider(); + + /** + * Stockage des services deja instancies + */ + private static Map, TopiaApplicationService> mapServiceCache = new HashMap, TopiaApplicationService>(); + + /** Logger (common logging) */ + private static final Log log = LogFactory + .getLog(TopiaApplicationServiceFactory.class); + + private static TopiaContext defaultServiceContext; + + /** + * Retourne la configuration. Charge le fichier s'il n'a pas deja ete + * charge. + * + * @throws TopiaNotFoundException + * si le fichier de configuration ne peut pas etre charge + */ + static Properties getConfiguration() throws TopiaNotFoundException { + if (config == null) { + config = TopiaUtil.getProperties(DEFAULT_CONFIG_PROPERTIES); + } + return config; + } + + /** + * Charge et lance tous les services contenus dans le fichier de + * configuration + * + * @param config + * les proprietes du fichier de configuration + * @param context + * le contexte pere des contextes fournis aux services + * @throws TopiaException + */ + @SuppressWarnings("unchecked") + public static void loadServices(Properties config, TopiaContext context) + throws TopiaException { + if (context == null) + throw new NullPointerException( + "I need a valid TopiaContext to initialise application services"); + defaultServiceContext = context; + // lecture du fichier de configuration + if (config == null) { + try { + config = getConfiguration(); + } catch (TopiaNotFoundException e) { + throw new TopiaNotFoundException( + "Can't find configuration file " + + DEFAULT_CONFIG_PROPERTIES); + } + } + // pour chaque service applicatif + Set keySet = config.keySet(); + for (Object key : keySet) { + + if (key.toString().startsWith(TOPIA_APPLICATION_SERVICE_BEGIN)) { + + // convertir l'URI pour recuperer les parametres + String serviceUrl = config.getProperty(key.toString()); + URI uriService = null; + try { + uriService = new URI(serviceUrl); + } catch (URISyntaxException e) { + log.error("URI Syntax isn't valid"); + } + String proto = uriService.getScheme(); + String host = uriService.getHost(); + String fragment = uriService.getFragment(); + String serviceClassName = ((String) key).replace( + TOPIA_APPLICATION_SERVICE_BEGIN, ""); + Protocole protocole = null; + + // instancier puis lancer le service + if (!proto.equalsIgnoreCase("local")) { + protocole = Protocole.valueOf(proto.replace('-', '_') + .toUpperCase()); + Class serviceInterface = null; + Class serviceImplement = null; + try { + serviceInterface = Class.forName(serviceClassName); + serviceImplement = Class.forName(serviceClassName + + "Impl"); + } catch (ClassNotFoundException e) { + log.error("Class not found for " + serviceClassName); + } + try { + Object newInstance = serviceImplement.newInstance(); + TopiaApplicationService service = (TopiaApplicationService) newInstance; + TopiaContext serviceContext = context + .beginTransaction(); + service.init(serviceContext); + addService(serviceInterface, service, protocole); + } catch (InstantiationException e) { + throw new TopiaException( + "Can't instanciate service class " + + serviceImplement); + } catch (IllegalAccessException e) { + throw new TopiaException( + "Can't access to service class " + + serviceImplement); + } + } + log.info("service " + serviceClassName + " added"); + } + } + } + + /** + * Fournit une interface sur un service en l'implementant comme definit dans + * la configuration. + * + * Configuration (TopiaApplicationServices.properties par defaut) : + * topia.application.service.fqn=local://fqnImpl/#new + * topia.application.service.fqn=local://fqnImpl + * topia.application.service.fqn=rmi://127.0.0.1:1099 + * topia.application.service.fqn=xmlrpc://127.0.0.1:9090 ... + * + * @param + * l'interface doit etendre TopiaApplicationService + * @param serviceclazz + * l'interface du service + * @return l'implementation du service ou null si le service ne + * peut etre charge + * @see TopiaApplicationService + * @throws TopiaNotFoundException + * si le fichier de configuration n'existe pas + * @throws TopiaException + * si le service ne peut pas etre charge + */ + @SuppressWarnings("unchecked") + public static E getService( + Class serviceclazz) throws TopiaNotFoundException, + TopiaException { + + // l'implementation a retourner + E serviceimpl = (E) mapServiceCache.get(serviceclazz); + + // s'il etait dans le cache on le retourne directement + // dans le cas "#new" il n'est jamais dans le cache, donc recree + if (serviceimpl != null) { + return serviceimpl; + } + + // configuration + Properties config = getConfiguration(); + + // recherche du service + String serviceUrl = (String) config.get(TOPIA_APPLICATION_SERVICE_BEGIN + + serviceclazz.getCanonicalName()); + if (serviceUrl == null) { + log.error("Properties '" + TOPIA_APPLICATION_SERVICE_BEGIN + + serviceclazz.getCanonicalName() + + "' not found in configuration"); + throw new TopiaNotFoundException("Service '" + + serviceclazz.getCanonicalName() + + "' not definided in configuration"); + } else { + + try { + // conversion de l'URI + URI uriService = new URI(serviceUrl); + String protocole = uriService.getScheme(); + String host = uriService.getHost(); + String fragment = uriService.getFragment(); + + // ensuite ca depend si le service est local ou distant + // local = local://fqn + // sinon, c'est distant + if ("local".equalsIgnoreCase(protocole)) { + + String fqClassImpl = host; + log.debug("Trying to load local service : " + fqClassImpl); + + // instanciation dynamique + try { + Class resultClass = (Class) Class + .forName(fqClassImpl); + serviceimpl = resultClass.newInstance(); + serviceimpl.init(defaultServiceContext.beginTransaction()); + + // mise en cache + // sauf dans le cas ou il y a un "#new" a la fin de + // l'uri + if (!"new".equalsIgnoreCase(fragment)) { + mapServiceCache.put(serviceclazz, serviceimpl); + } + } catch (ClassNotFoundException eee) { + throw new TopiaException("Can't find service class " + + fqClassImpl); + } catch (InstantiationException eee) { + throw new TopiaException( + "Can't instanciate service class " + + fqClassImpl); + } catch (IllegalAccessException eee) { + throw new TopiaException("Can't access service class " + + fqClassImpl); + } + } + // prefix non local => proxy + else { + log.debug("Trying to get remote service : " + serviceUrl); + + TopiaProxy tProxy = getProxyForURI(uriService); + + if (tProxy == null) { + log.debug("Unsupported protocole : " + protocole); + } else { + + tProxy.setURI(uriService); + tProxy.setClass(serviceclazz); + + // le proxy tProxy definit suivant le protocole + // gere maintenant tous les appels sur l'interface + // demandee (serviceclazz) + serviceimpl = (E) Proxy.newProxyInstance(serviceclazz + .getClassLoader(), + new Class[] { serviceclazz }, tProxy); + + // mise en cache + mapServiceCache.put(serviceclazz, serviceimpl); + } + } + } catch (URISyntaxException e) { + log.warn("URI for service '" + serviceclazz.getCanonicalName() + + "' is invalid !", e); + return null; + } + } + + return serviceimpl; + } + + /** + * Retourne l'implementation d'un TopiaProxy en fonction du protocole de + * l'URI + * + * @param uriService + * @return l'implementation ou null si le protocol n'est pas géré + */ + private static TopiaProxy getProxyForURI(URI uriService) { + + // result + TopiaProxy tProxy = null; + + if ("rmi".equalsIgnoreCase(uriService.getScheme())) { + tProxy = new RMIProxy(); + } else if ("xml-rpc".equalsIgnoreCase(uriService.getScheme())) { + tProxy = new XMLRPCProxy(); + } else if ("soap".equalsIgnoreCase(uriService.getScheme())) { + tProxy = new SOAPProxy(); + } + + return tProxy; + } + + /** + * Ajoute un service fournit par ToPIA. + * + * @param interfaze + * l'interface du service + * @param clazz + * la classe qui permet de creer des instances de + * l'implementation du service + * @param protocoles + * les protocoles de diffusion du service + * + * @see TopiaApplicationService + */ + public static void addService( + Class interfaze, + Class clazz, + Protocole... protocoles) { + log.debug("Adding service for '" + interfaze + "' in protocoles : " + + Arrays.toString(protocoles)); + + for (Protocole protocole : protocoles) { + mainDispatcher.addServiceClass(interfaze, clazz, protocole); + } + } + + /** + * Ajoute un service fournit par ToPIA. + * + * Celle-ci renvoie toujours la meme instance du service. + * + * @param + * un type qui etend TopiaApplicationService + * @param interfaze + * l'interface du service + * @param instance + * l'instance de l'implementation du service + * @param protocoles + * les protocoles de diffusion du service + * + * @see TopiaApplicationService + */ + public static void addService( + Class interfaze, E instance, Protocole... protocoles) { + log.debug("Adding service for '" + interfaze + + "'(unique instance) in protocoles : " + + Arrays.toString(protocoles)); + + for (Protocole protocole : protocoles) { + mainDispatcher.addServiceInstance(interfaze, instance, protocole); + } + } +}