package com.idega.business;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.ejb.CreateException;
import javax.ejb.RemoveException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import com.idega.idegaweb.IWApplicationContext;
import com.idega.idegaweb.IWUserContext;
import com.idega.idegaweb.IWUserContextImpl;
import com.idega.repository.data.RefactorClassRegistry;
import com.idega.repository.data.Singleton;
import com.idega.util.reflect.MethodFinder;
/**
* IBOLookup is a class use to get instances of IBO (Service and Session) objects.<br><br>
* <br>Instances of IBOService classes are stored in the IWApplicationContext and obtained by passing a reference to the application context and either a class representing a bean interface of implementation.
* <br>Instances of IBOSession classes are stored in the IWUserContext and obtained by passing a reference to the user context and either a class representing a bean interface of implementation.
* Copyright (c) 2002-2004 Idega Software
* @author <a href="tryggvi@idega.is">Tryggvi Larusson</a>
* @param <SB>
*/
public class IBOLookup<Service, ServiceBean, Home, S extends Service, SB extends ServiceBean> implements Singleton {
private static IBOLookup<IBOService, IBOServiceBean, IBOHome, IBOService, IBOServiceBean> instance;
protected static synchronized IBOLookup<IBOService, IBOServiceBean, IBOHome, IBOService, IBOServiceBean> getInstance() {
if (instance == null) {
instance = new IBOLookup<IBOService, IBOServiceBean, IBOHome, IBOService, IBOServiceBean>();
}
return instance;
}
/**
* Unload the previously loaded instance and all its resources
*/
public static void unload() {
instance=null;
}
protected final String HOME_SUFFIX = "Home";
protected final String FACTORY_SUFFIX = "HomeImpl";
private final String BEAN_SUFFIX = "Bean";
protected String getBeanSuffix() {
return this.BEAN_SUFFIX;
}
private Map<String, Home> homes;
private Map<Class<S>, Class<SB>> beanClasses;
private Map<Class<SB>, Class<S>> interfaceClasses;
private Map<Class<S>, SB> services;
private Properties jndiProperties;
private Map<Class<Home>, Method> createMethodsMap;
protected IBOLookup() {}
protected static <H> H getHomeForClass(Class<?> beanInterfaceClass) throws IBOLookupException {
H home = (H) getInstance().getEJBHomeInstance((Class<IBOService>) beanInterfaceClass);
return home;
}
protected static <H> H getIBOHomeForClass(Class<?> beanInterfaceClass) throws IBOLookupException {
return getHomeForClass(beanInterfaceClass);
}
/**
* Returns an instance of a IBOSession bean.
* The instance is stored in the session for the (current) user context. After <b>this</b> method is called there should be a corresponding call to removeSessionAttribute() to clean up from the users context.
* @param iwuc A reference to the user (context) the bean instance is working under.
* @param beanInterfaceClass The bean (implementation or interface) class to be used. (For example UserBusiness.class or UserBusinessBean.class)
*/
public static <S extends IBOSession> S getSessionInstance(IWUserContext iwuc, Class<S> beanInterfaceClass) throws IBOLookupException {
checkAnnotatedAsSpringBean(beanInterfaceClass);
return getInstance().getSessionInstanceImpl(iwuc, beanInterfaceClass);
}
private <Session extends IBOSession> Session getSessionInstanceImpl(IWUserContext iwuc, Class<Session> beanInterfaceClass) throws IBOLookupException {
Session session = (Session) iwuc.getSessionAttribute(this.getSessionKeyForObject(beanInterfaceClass));
if (session == null) {
try {
session = instanciateSessionBean(beanInterfaceClass);
iwuc.setSessionAttribute(getSessionKeyForObject(beanInterfaceClass), session);
session.setUserContext(iwuc);
}
catch (CreateException cre) {
throw new IBOLookupException("[IBOLookup] : CreateException : " + cre.getMessage());
}
catch (RemoteException cre)
{
throw new IBOLookupException("[IBOLookup] : RemoteException : " + cre.getMessage());
}
}
return session;
}
/**
* Cleans up after an an instance of a IBOSession bean has been used.
* @param iwuc A reference to the user (context) the bean instance is working under.
* @param beanInterfaceClass The bean (implementation or interface) class to be used. (For example UserBusiness.class or UserBusinessBean.class)
*/
public static void removeSessionInstance(IWUserContext iwuc, Class<? extends IBOSession> beanInterfaceClass) throws RemoteException, RemoveException {
getInstance().removeSessionInstanceImpl(iwuc, beanInterfaceClass);
}
private void removeSessionInstanceImpl(IWUserContext iwuc, Class<? extends IBOSession> beanInterfaceClass) throws RemoteException, RemoveException {
IBOSession session = getSessionInstance(iwuc, beanInterfaceClass);
session.remove();
iwuc.removeSessionAttribute(getSessionKeyForObject(beanInterfaceClass));
}
/**
* Cleans up after an an instance of a IBOSession bean has been used.
* @param iwuc A reference to the user (context) the bean instance is working under.
* @param beanInterfaceClass The bean (implementation or interface) class to be used. (For example UserBusiness.class or UserBusinessBean.class)
*/
public static void removeSessionInstance(HttpSession session, Class<? extends IBOSession> beanInterfaceClass) throws RemoteException, RemoveException {
getInstance().removeSessionInstanceImpl(session, beanInterfaceClass);
}
private void removeSessionInstanceImpl(HttpSession session, Class<? extends IBOSession> beanInterfaceClass) throws RemoteException, RemoveException {
IBOSession sessionBean = getSessionInstance(session, beanInterfaceClass);
sessionBean.remove();
session.removeAttribute(getSessionKeyForObject(beanInterfaceClass));
}
private <Session extends IBOSession> Session instanciateSessionBean(Class<Session> beanInterfaceClass) throws IBOLookupException, CreateException {
return instanciateServiceBean(beanInterfaceClass);
}
private <IService extends IBOService> IService instanciateServiceBean(Class<IService> beanInterfaceClass) throws IBOLookupException, CreateException {
IService service = null;
Home home = getIBOHomeForClass(beanInterfaceClass);
try{
Method defaultCreateMethod = getCreateMethod(home);
service = (IService) defaultCreateMethod.invoke(home, (Object[]) null);
} catch(InvocationTargetException ite){
//ite.printStackTrace();
Throwable e = ite.getTargetException();
//e.printStackTrace();
throw new CreateException("Exception invoking create method for: "+beanInterfaceClass.getName()+". Error was:"+e.getClass().getName()+" : "+e.getMessage());
} catch(Exception e){
throw new CreateException("Exception invoking create method for: "+beanInterfaceClass.getName()+". Error was:"+e.getClass().getName()+" : "+e.getMessage());
}
return service;
}
/**
* Method getCreateMethod.
* @param home
* @return Method
*/
private Method getCreateMethod(Home home) throws NoSuchMethodException{
Map<Class<Home>, Method> map = getCreateMethodsMap();
Class<Home> homeClass = (Class<Home>) home.getClass();
Method m = map.get(homeClass);
if(m==null){
m = MethodFinder.getInstance().getMethodWithNameAndNoParameters(homeClass,"create");
map.put(homeClass, m);
}
return m;
}
/**
* Method getCreateMethodsMap.
* @return Map
*/
private Map<Class<Home>, Method> getCreateMethodsMap() {
if(this.createMethodsMap==null){
this.createMethodsMap=new HashMap<Class<Home>, Method>();
}
return this.createMethodsMap;
}
/**
* Returns an instance of a IBOService bean.
* The instance is stored in the application context and is shared between all users.
* @param iwac A reference to the application (context) the bean should be looked up.
* @param beanInterfaceClass The bean (implementation or interface) class to be used. (For example UserBusiness.class or UserBusinessBean.class)
*/
public static <S extends IBOService> S getServiceInstance(IWApplicationContext iwac, Class<S> beanInterfaceClass) throws IBOLookupException {
S service = (S) getInstance().getServiceInstanceImpl(iwac, (Class<IBOService>) beanInterfaceClass);
return service;
}
private <IService extends IBOService> SB getServiceInstanceImpl(IWApplicationContext iwac, Class<IService> beanInterfaceClass) throws IBOLookupException {
Map<Class<S>, SB> map = this.getServicesMap(iwac);
SB service = map.get(beanInterfaceClass);
if (service == null) {
try {
IService iboService = instanciateServiceBean(beanInterfaceClass);
service = (SB) iboService;
if (iwac != null) {
((IBOServiceBean) service).setIWApplicationContext(iwac);
}
((IBOService) service).initializeBean();
getServicesMap(iwac).put((Class<S>) beanInterfaceClass, service);
}
catch (CreateException cre) {
throw new IBOLookupException("[IBOLookup] : CreateException : " + cre.getMessage());
}
}
return service;
}
protected <H extends Home> Class<H> getHomeInterfaceClassFor(Class<S> entityInterfaceClass) throws Exception {
try{
//First try to suffix the Home to the interface class name
String baseClassName = getInterfaceClassForNonStatic(entityInterfaceClass).getName();
String homeClassName = baseClassName + this.HOME_SUFFIX;
return RefactorClassRegistry.forName(homeClassName);
} catch(ClassNotFoundException cnfe){
//If that doesn't work then try to suffix the Home to the bean implementation class name - the bean suffix
String beanClassName = getBeanClassForNonStatic(entityInterfaceClass).getName();
String baseClassName = beanClassName.substring(0, beanClassName.indexOf(getBeanSuffix()));
String homeClassName = baseClassName + this.HOME_SUFFIX;
return RefactorClassRegistry.forName(homeClassName);
}
}
protected Class<Home> getFactoryClassFor(Class<S> entityInterfaceClass) throws Exception {
try{
//First try to suffix the FACTORY_SUFFIX to the bean implementation class name - the bean suffix
String beanClassName = getBeanClassForNonStatic(entityInterfaceClass).getName();
String baseClassName = beanClassName.substring(0,beanClassName.indexOf(getBeanSuffix()));
String homeClassName = baseClassName + this.FACTORY_SUFFIX;
return RefactorClassRegistry.forName(homeClassName);
} catch(ClassNotFoundException cnfe) {
//If that doesn't work then try to suffix the FACTORY_SUFFIX to the interface class name
String baseClassName = getInterfaceClassForNonStatic(entityInterfaceClass).getName();
String homeClassName = baseClassName + this.FACTORY_SUFFIX;
return RefactorClassRegistry.forName(homeClassName);
}
}
private String getSessionKeyForObject(Class<? extends IBOService> interfaceClass) {
return "IBO." + interfaceClass.getName();
}
/**
* Gets an instance of the implementation of the Home interface for the data bean.
* <br>The object returned can then needs to be casted to the specific home interface for the bean.
* @param entityInterfaceClass i the interface of the data bean.
*/
protected Home getEJBHomeInstance(Class<S> entityBeanOrInterfaceClass){
return getEJBHomeInstance(entityBeanOrInterfaceClass, null);
}
protected Home homesMapLookup(Class<S> entityInterfaceClass, String parameter) {
return getHomesMap().get(entityInterfaceClass+parameter);
}
/**
* Gets an instance of the implementation of the Home interface for the data bean.
* <br>The object retured can then needs to be casted to the specific home interface for the bean.
* @param entityInterfaceClass i the interface of the data bean.
* @param parameter is a parameter used to separete different instances of the same Home class
*/
protected Home getEJBHomeInstance(Class<S> entityBeanOrInterfaceClass, String parameter){
//Double check so it is not the bean class that is sent into the methods below
Class<S> entityInterfaceClass = getInterfaceClassForNonStatic(entityBeanOrInterfaceClass);
Home home = homesMapLookup(entityInterfaceClass, parameter);
if (home == null) {
try {
if (doLookupOverJNDI(entityInterfaceClass)) {
home = getHomeThroughJNDI(entityInterfaceClass);
} else{
Class<Home> factoryClass = getFactoryClassFor(entityInterfaceClass);
home = factoryClass.newInstance();
}
getHomesMap().put(entityInterfaceClass+parameter, home);
} catch (Exception e) {
//e.printStackTrace();
throw new RuntimeException(
"Error initializing Home for EJB Bean Interface class:"
+ entityInterfaceClass.getName()
+ " - Message: "
+ e.getMessage(),e);
}
}
return home;
}
/**
* Gets the Class object for the (BMP) bean class of a data bean.
* @param entityInterfaceClass i the (Remote) interface of the data bean.
*/
static <S extends IBOService, SB extends IBOServiceBean> Class<SB> getIBOBeanClassFor(Class<S> entityInterfaceClass) {
Class<SB> theClass = (Class<SB>) getInstance().getBeanClassForNonStatic((Class<IBOService>) entityInterfaceClass);
return theClass;
}
/**
* Gets the Class object for the (BMP) bean class of a data bean.
* @param entityInterfaceClass i the (Remote) interface of the data bean.
*/
protected Class<SB> getBeanClassForNonStatic(Class<S> entityInterfaceClass) {
try {
Map<Class<S>, Class<SB>> cache = getBeanClassesMap();
Class<SB> beanClass = cache.get(entityInterfaceClass);
if (beanClass == null) {
if (entityInterfaceClass.isInterface()) {
String className = entityInterfaceClass.getName();
String beanClassName = className + getBeanSuffix();
beanClass = RefactorClassRegistry.forName(beanClassName);
cache.put(entityInterfaceClass, beanClass);
getInterfaceClassesMap().put(beanClass, entityInterfaceClass);
} else {
beanClass = (Class<SB>) entityInterfaceClass;
}
}
return beanClass;
} catch (Exception e) {
throw new RuntimeException(e.getClass().getName() + " : " + e.getMessage());
}
}
/**
* Gets the Class object for the (Remote) interface of a data bean.
* @param entityBeanOrInterfaceClass can be either the BMP bean class or the interface class itself.
*/
protected Class<S> getInterfaceClassForNonStatic(Class<S> entityBeanOrInterfaceClass) {
if (entityBeanOrInterfaceClass.isInterface()) {
return entityBeanOrInterfaceClass;
} else {
Map<Class<SB>, Class<S>> cache2 = getInterfaceClassesMap();
Class<S> interfaceClass = cache2.get(entityBeanOrInterfaceClass);
try {
if (interfaceClass == null) {
String className = entityBeanOrInterfaceClass.getName();
int endIndex = className.indexOf(getBeanSuffix());
if (endIndex != -1) {
String interfaceClassName = className.substring(0, endIndex);
interfaceClass = RefactorClassRegistry.forName(interfaceClassName);
Map<Class<S>, Class<SB>> cache = getBeanClassesMap();
cache.put(interfaceClass, (Class<SB>) entityBeanOrInterfaceClass);
getInterfaceClassesMap().put((Class<SB>) entityBeanOrInterfaceClass, interfaceClass);
} else {
//For legacy beans
interfaceClass = entityBeanOrInterfaceClass;
}
}
return interfaceClass;
} catch (ClassNotFoundException e) {
throw new RuntimeException(e.getClass() + ": " + e.getMessage());
}
}
}
private Map<Class<S>, SB> getServicesMap(IWApplicationContext iwac) {
return getServicesMap();
}
private Map<Class<S>, SB> getServicesMap() {
if (this.services == null) {
this.services = new HashMap<Class<S>, SB>();
}
return this.services;
}
/**
* Clears all cached object instances of looked up objects (Home instances etc.)
**/
public static synchronized void clearAllCache() {
getInstance().getHomesMap().clear();
getInstance().getBeanClassesMap().clear();
getInstance().getInterfaceClassesMap().clear();
}
protected Home getHomeThroughJNDI(Class<S> beanInterfaceClass)throws RemoteException{
Home home = null;
try{
InitialContext jndiContext = getInitialContext();
Home homeObj = (Home) jndiContext.lookup(getBeanClassForNonStatic(beanInterfaceClass).getName());
//home = (ResponseHome) jndiContext.lookup("java:comp/env/"+ResponseBMPBean.class.getName());
home = (Home) PortableRemoteObject.narrow(homeObj, getHomeInterfaceClassFor(beanInterfaceClass));
return home;
} catch(Exception e){
throw new RemoteException("Error looking up home for "+beanInterfaceClass.getName()+". Errormessage was: "+e.getMessage());
}
}
protected boolean doLookupOverJNDI(Class<S> beanInterfaceClass){
return false;
}
private InitialContext getInitialContext() throws NamingException{
if(this.jndiProperties==null){
this.jndiProperties=new Properties();
try {
this.jndiProperties.load(new FileInputStream("/idega/jndi.properties"));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return new InitialContext(this.jndiProperties);
}
/**
* Interface -> Class
*
* @return
*/
public Map<Class<S>, Class<SB>> getBeanClassesMap(){
if (this.beanClasses == null) {
this.beanClasses = new HashMap<Class<S>, Class<SB>>();
}
return this.beanClasses;
}
public Map<Class<SB>, Class<S>> getInterfaceClassesMap(){
if(this.interfaceClasses==null){
this.interfaceClasses= new HashMap<Class<SB>, Class<S>>();
}
return this.interfaceClasses;
}
public Map<String, Home> getHomesMap(){
if(this.homes==null){
this.homes= new HashMap<String, Home>();
}
return this.homes;
}
/**
* Registers an implementation for a bean.
*
* @param interfaceClass
* @param beanClass
* @see #isImplementationRegistered(Class)
*/
public static void registerImplementationForBean(Class<? extends IBOService> interfaceClass, Class<? extends IBOServiceBean> beanClass) {
Map<Class<IBOService>, Class<IBOServiceBean>> cache = getInstance().getBeanClassesMap();
cache.put((Class<IBOService>) interfaceClass, (Class<IBOServiceBean>) beanClass);
Map<Class<IBOServiceBean>, Class<IBOService>> cache2 = getInstance().getInterfaceClassesMap();
cache2.put((Class<IBOServiceBean>) beanClass, (Class<IBOService>) interfaceClass);
}
/**
* Checks if an implementation of the specified bean is registered.
* This method should be called if the caller is not sure if an implementation exists.
* If an implementation does not exist call of the getServiceInstance method
* returns a RuntimeException when trying to get the bean.
*
* Returns true if there is an entry else false
*
* @param interfaceClass
* @return true if an implementation is registered else false
* @see #registerImplementationForBean(Class, Class)
*/
public static <S extends IBOService> boolean isImplementationRegistered(Class<S> interfaceClass) {
return getInstance().getBeanClassesMap().containsKey(interfaceClass);
}
/**
* <p>
* Checks if the session bean by class beanIntefaceClass has been initialized (and is alive in session).
* </p>
* @param request
* @param beanInterfaceClass
* @return
*/
public static boolean isSessionBeanInitialized(HttpServletRequest request, Class<? extends IBOSession> beanInterfaceClass){
return isSessionBeanInitialized(request.getSession(), beanInterfaceClass);
}
/**
* <p>
* Checks if the session bean by class beanIntefaceClass has been initialized (and is alive in session).
* </p>
* @param request
* @param beanInterfaceClass
* @return
*/
public static boolean isSessionBeanInitialized(HttpSession session, Class<? extends IBOSession> beanInterfaceClass){
String key = getInstance().getSessionKeyForObject(beanInterfaceClass);
Object bean = session.getAttribute(key);
if(bean!=null){
return true;
}
return false;
}
/**
* <p>
* Checks if the sessionbean by class beanIntefaceClass has been initialized (and is alive in session).
* </p>
* @param request
* @param beanInterfaceClass
* @return
*/
public static boolean isSessionBeanInitialized(IWUserContext iwuc, Class<? extends IBOSession> beanInterfaceClass) {
String key = getInstance().getSessionKeyForObject(beanInterfaceClass);
Object bean = iwuc.getSessionAttribute(key);
if(bean!=null){
return true;
}
return false;
}
/**
* Returns an instance of a IBOSession bean.
* The instance is stored in the session for t. After <b>this</b> method is called there should be a corresponding call to removeSessionAttribute() to clean up from the users context.
* @param iwuc A reference to the user (context) the bean instance is working under.
* @param beanInterfaceClass The bean (implementation or interface) class to be used. (For example UserBusiness.class or UserBusinessBean.class)
*/
public static <Session extends IBOSession> Session getSessionInstance(HttpServletRequest request, Class<Session> beanInterfaceClass) throws IBOLookupException {
checkAnnotatedAsSpringBean(beanInterfaceClass);
return getInstance().getSessionInstanceImpl(request.getSession(), beanInterfaceClass);
}
/**
* Returns an instance of a IBOSession bean.
* The instance is stored in the session for t. After <b>this</b> method is called there should be a corresponding call to removeSessionAttribute() to clean up from the users context.
* @param iwuc A reference to the user (context) the bean instance is working under.
* @param beanInterfaceClass The bean (implementation or interface) class to be used. (For example UserBusiness.class or UserBusinessBean.class)
*/
public static <Session extends IBOSession> Session getSessionInstance(HttpSession session, Class<Session> beanInterfaceClass) throws IBOLookupException {
checkAnnotatedAsSpringBean(beanInterfaceClass);
return getInstance().getSessionInstanceImpl(session, beanInterfaceClass);
}
private <Session extends IBOSession> Session getSessionInstanceImpl(HttpSession session, Class<Session> beanInterfaceClass) throws IBOLookupException {
Session sessionBean = (Session) session.getAttribute(this.getSessionKeyForObject(beanInterfaceClass));
if (sessionBean == null) {
IWUserContext iwuc = new IWUserContextImpl(session,session.getServletContext());
sessionBean = getSessionInstanceImpl(iwuc,beanInterfaceClass);
}
return sessionBean;
}
private static <S extends IBOService> void checkAnnotatedAsSpringBean(Class<S> interface_class) {
if (interface_class.isAnnotationPresent(SpringBeanName.class)) {
throw new UnsupportedOperationException(
"Provided interface: "+interface_class.getName()+
" is annotated as spring bean therefore it's not IBO bean anymore. " +
"You need to retrieve this bean either by letting spring to inject it (use this one if possible)" +
" or making a lookup from SpringBeanLookup -> getSpringBean(..)");
}
}
}