package railo.runtime.orm.hibernate; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import org.hibernate.EntityMode; import org.hibernate.SessionFactory; import org.hibernate.engine.query.QueryPlanCache; import org.hibernate.event.EventListeners; import org.hibernate.event.PostDeleteEventListener; import org.hibernate.event.PostInsertEventListener; import org.hibernate.event.PostLoadEventListener; import org.hibernate.event.PostUpdateEventListener; import org.hibernate.event.PreDeleteEventListener; import org.hibernate.event.PreLoadEventListener; import org.hibernate.tuple.entity.EntityTuplizerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import railo.commons.io.res.Resource; import railo.loader.util.Util; import railo.runtime.Component; import railo.runtime.PageContext; import railo.runtime.db.DataSource; import railo.runtime.db.DataSourcePro; import railo.runtime.db.DatasourceConnection; import railo.runtime.exp.PageException; import railo.runtime.listener.ApplicationContext; import railo.runtime.listener.ApplicationContextPro; import railo.runtime.op.Duplicator; import railo.runtime.orm.ORMConfiguration; import railo.runtime.orm.ORMEngine; import railo.runtime.orm.ORMSession; import railo.runtime.orm.ORMUtil; import railo.runtime.orm.hibernate.event.AllEventListener; import railo.runtime.orm.hibernate.event.EventListener; import railo.runtime.orm.hibernate.event.InterceptorImpl; import railo.runtime.orm.hibernate.event.PostDeleteEventListenerImpl; import railo.runtime.orm.hibernate.event.PostInsertEventListenerImpl; import railo.runtime.orm.hibernate.event.PostLoadEventListenerImpl; import railo.runtime.orm.hibernate.event.PostUpdateEventListenerImpl; import railo.runtime.orm.hibernate.event.PreDeleteEventListenerImpl; import railo.runtime.orm.hibernate.event.PreInsertEventListenerImpl; import railo.runtime.orm.hibernate.event.PreLoadEventListenerImpl; import railo.runtime.orm.hibernate.event.PreUpdateEventListenerImpl; import railo.runtime.orm.hibernate.tuplizer.AbstractEntityTuplizerImpl; import railo.runtime.text.xml.XMLCaster; import railo.runtime.type.Collection; import railo.runtime.type.util.ArrayUtil; public class HibernateORMEngine implements ORMEngine { private static final int INIT_NOTHING=1; private static final int INIT_CFCS=2; private static final int INIT_ALL=2; private Map<String,SessionFactoryData> factories=new ConcurrentHashMap<String, SessionFactoryData>(); public HibernateORMEngine() {} @Override public void init(PageContext pc) throws PageException{ getSessionFactory(pc,INIT_CFCS); } @Override public ORMSession createSession(PageContext pc) throws PageException { ApplicationContextPro appContext = (ApplicationContextPro) pc.getApplicationContext(); Object o=appContext.getORMDataSource(); DataSource ds=o instanceof DataSource? (DataSource)o: CommonUtil.getDataSource(pc,CommonUtil.toString(o)); DatasourceConnection dc = CommonUtil.getDatasourceConnection(pc,ds); try{ SessionFactoryData data = getSessionFactoryData(pc, INIT_NOTHING); return new HibernateORMSession(data,dc); } catch(PageException pe){ //manager.releaseConnection(pc, dc);// connection is closed when session ends throw pe; } } QueryPlanCache getQueryPlanCache(PageContext pc) throws PageException { return getSessionFactoryData(pc,INIT_NOTHING).getQueryPlanCache(); } @Override public SessionFactory getSessionFactory(PageContext pc) throws PageException{ return getSessionFactory(pc,INIT_NOTHING); } public boolean reload(PageContext pc, boolean force) throws PageException { if(force) { getSessionFactoryData(pc, INIT_ALL); } else { if(factories.containsKey(hash(pc)))return false; } getSessionFactoryData(pc, INIT_CFCS); return true; } private SessionFactory getSessionFactory(PageContext pc,int initType) throws PageException { return getSessionFactoryData(pc, initType).getFactory(); } private SessionFactoryData getSessionFactoryData(PageContext pc,int initType) throws PageException { ApplicationContextPro appContext = (ApplicationContextPro) pc.getApplicationContext(); if(!appContext.isORMEnabled()) throw ExceptionUtil.createException((ORMSession)null,null,"ORM is not enabled",""); // datasource DataSource ds = ORMUtil.getDataSource(pc); ORMConfiguration ormConf=appContext.getORMConfiguration(); String key = hash(ormConf,ds); SessionFactoryData data = factories.get(key); if(initType==INIT_ALL && data!=null) { data.reset(); data=null; } if(data==null) { data=new SessionFactoryData(this,ormConf,ds); factories.put(key, data); } // config try{ //arr=null; if(initType!=INIT_NOTHING){ synchronized (data) { if(ormConf.autogenmap()){ data.tmpList=HibernateSessionFactory.loadComponents(pc, this, ormConf); data.cfcs.clear(); } else throw ExceptionUtil.createException(data,null,"orm setting autogenmap=false is not supported yet",null); // load entities if(!ArrayUtil.isEmpty(data.tmpList)) { data.getNamingStrategy();// caled here to make sure, it is called in the right context the first one DatasourceConnection dc = CommonUtil.getDatasourceConnection(pc, ds); try { Iterator<Component> it = data.tmpList.iterator(); while(it.hasNext()){ createMapping(pc,it.next(),dc,ormConf,data); } } finally { CommonUtil.releaseDatasourceConnection(pc, dc); } if(data.tmpList.size()!=data.cfcs.size()){ Component cfc; String name,lcName; Map<String,String> names=new HashMap<String,String>(); Iterator<Component> it = data.tmpList.iterator(); while(it.hasNext()){ cfc=it.next(); name=HibernateCaster.getEntityName(cfc); lcName=name.toLowerCase(); if(names.containsKey(lcName)) throw ExceptionUtil.createException(data,null,"Entity Name ["+name+"] is ambigous, ["+names.get(lcName)+"] and ["+cfc.getPageSource().getDisplayPath()+"] use the same entity name.",""); names.put(lcName,cfc.getPageSource().getDisplayPath()); } } } } } } finally { data.tmpList=null; } // already initialized for this application context if(data.getConfiguration()!=null) return data; //MUST //cacheconfig //cacheprovider //... String mappings=HibernateSessionFactory.createMappings(ormConf,data); DatasourceConnection dc = CommonUtil.getDatasourceConnection(pc,ds); try{ data.setConfiguration(mappings,dc); } catch (Exception e) { throw CommonUtil.toPageException(e); } finally { CommonUtil.releaseDatasourceConnection(pc, dc); } addEventListeners(pc, data); EntityTuplizerFactory tuplizerFactory = data.getConfiguration().getEntityTuplizerFactory(); //tuplizerFactory.registerDefaultTuplizerClass(EntityMode.MAP, CFCEntityTuplizer.class); //tuplizerFactory.registerDefaultTuplizerClass(EntityMode.MAP, MapEntityTuplizer.class); tuplizerFactory.registerDefaultTuplizerClass(EntityMode.MAP, AbstractEntityTuplizerImpl.class); tuplizerFactory.registerDefaultTuplizerClass(EntityMode.POJO, AbstractEntityTuplizerImpl.class); //tuplizerFactory.registerDefaultTuplizerClass(EntityMode.POJO, AbstractEntityTuplizerImpl.class); //configuration.setEntityResolver(new CFCEntityResolver()); //configuration.setEntityNotFoundDelegate(new EntityNotFoundDelegate()); data.buildSessionFactory(); return data; } private static void addEventListeners(PageContext pc, SessionFactoryData data) throws PageException { if(!data.getORMConfiguration().eventHandling()) return; String eventHandler = data.getORMConfiguration().eventHandler(); AllEventListener listener=null; if(!Util.isEmpty(eventHandler,true)){ //try { Component c = pc.loadComponent(eventHandler.trim()); listener = new AllEventListener(c); //config.setInterceptor(listener); //}catch (PageException e) {e.printStackTrace();} } data.getConfiguration().setInterceptor(new InterceptorImpl(listener)); EventListeners listeners = data.getConfiguration().getEventListeners(); // post delete List<EventListener> list=merge(listener,data.cfcs,CommonUtil.POST_DELETE); listeners.setPostDeleteEventListeners(list.toArray(new PostDeleteEventListener[list.size()])); // post insert list=merge(listener,data.cfcs,CommonUtil.POST_INSERT); listeners.setPostInsertEventListeners(list.toArray(new PostInsertEventListener[list.size()])); // post update list=merge(listener,data.cfcs,CommonUtil.POST_UPDATE); listeners.setPostUpdateEventListeners(list.toArray(new PostUpdateEventListener[list.size()])); // post load list=merge(listener,data.cfcs,CommonUtil.POST_LOAD); listeners.setPostLoadEventListeners(list.toArray(new PostLoadEventListener[list.size()])); // pre delete list=merge(listener,data.cfcs,CommonUtil.PRE_DELETE); listeners.setPreDeleteEventListeners(list.toArray(new PreDeleteEventListener[list.size()])); // pre insert //list=merge(listener,cfcs,CommonUtil.PRE_INSERT); //listeners.setPreInsertEventListeners(list.toArray(new PreInsertEventListener[list.size()])); // pre load list=merge(listener,data.cfcs,CommonUtil.PRE_LOAD); listeners.setPreLoadEventListeners(list.toArray(new PreLoadEventListener[list.size()])); // pre update //list=merge(listener,cfcs,CommonUtil.PRE_UPDATE); //listeners.setPreUpdateEventListeners(list.toArray(new PreUpdateEventListener[list.size()])); } private static List<EventListener> merge(EventListener listener, Map<String, CFCInfo> cfcs, Collection.Key eventType) { List<EventListener> list=new ArrayList<EventListener>(); Iterator<Entry<String, CFCInfo>> it = cfcs.entrySet().iterator(); Entry<String, CFCInfo> entry; Component cfc; while(it.hasNext()){ entry = it.next(); cfc = entry.getValue().getCFC(); if(EventListener.hasEventType(cfc,eventType)) { if(CommonUtil.POST_DELETE.equals(eventType)) list.add(new PostDeleteEventListenerImpl(cfc)); if(CommonUtil.POST_INSERT.equals(eventType)) list.add(new PostInsertEventListenerImpl(cfc)); if(CommonUtil.POST_LOAD.equals(eventType)) list.add(new PostLoadEventListenerImpl(cfc)); if(CommonUtil.POST_UPDATE.equals(eventType)) list.add(new PostUpdateEventListenerImpl(cfc)); if(CommonUtil.PRE_DELETE.equals(eventType)) list.add(new PreDeleteEventListenerImpl(cfc)); if(CommonUtil.PRE_INSERT.equals(eventType)) list.add(new PreInsertEventListenerImpl(cfc)); if(CommonUtil.PRE_LOAD.equals(eventType)) list.add(new PreLoadEventListenerImpl(cfc)); if(CommonUtil.PRE_UPDATE.equals(eventType)) list.add(new PreUpdateEventListenerImpl(cfc)); } } // general listener if(listener!=null && EventListener.hasEventType(listener.getCFC(),eventType)) list.add(listener); return list; } private static Object hash(PageContext pc) throws PageException { ApplicationContextPro appContext=(ApplicationContextPro) pc.getApplicationContext(); Object o=appContext.getORMDataSource(); DataSource ds; if(o instanceof DataSource) ds=(DataSource) o; else ds=CommonUtil.getDataSource(pc,CommonUtil.toString(o)); return hash(appContext.getORMConfiguration(),ds); } private static String hash(ORMConfiguration ormConf,DataSource ds) { if(ds instanceof DataSourcePro) return ((DataSourcePro)ds).id()+":"+ormConf.hash(); return ds.getClazz()+":"+ds.getDsnTranslated()+":"+ormConf.hash(); } public void createMapping(PageContext pc,Component cfc, DatasourceConnection dc, ORMConfiguration ormConf,SessionFactoryData data) throws PageException { String id=HibernateUtil.id(HibernateCaster.getEntityName(cfc)); CFCInfo info=data.cfcs.get(id); //Long modified=cfcs.get(id); String xml; long cfcCompTime = HibernateUtil.getCompileTime(pc,cfc.getPageSource()); if(info==null || (ORMUtil.equals(info.getCFC(),cfc) )) {//&& info.getModified()!=cfcCompTime StringBuilder sb=new StringBuilder(); long xmlLastMod = loadMapping(sb,ormConf, cfc); Element root; // create maaping if(true || xmlLastMod< cfcCompTime) {//MUSTMUST data.reset(); Document doc=null; try { doc=CommonUtil.newDocument(); }catch(Throwable t){t.printStackTrace();} root=doc.createElement("hibernate-mapping"); doc.appendChild(root); pc.addPageSource(cfc.getPageSource(), true); try{ HBMCreator.createXMLMapping(pc,dc,cfc,root,data); } finally{ pc.removeLastPageSource(true); } xml=XMLCaster.toString(root.getChildNodes(),true,true); saveMapping(ormConf,cfc,root); } // load else { xml=sb.toString(); root=CommonUtil.toXML(xml).getOwnerDocument().getDocumentElement(); /*print.o("1+++++++++++++++++++++++++++++++++++++++++"); print.o(xml); print.o("2+++++++++++++++++++++++++++++++++++++++++"); print.o(root); print.o("3+++++++++++++++++++++++++++++++++++++++++");*/ } data.cfcs.put(id, new CFCInfo(HibernateUtil.getCompileTime(pc,cfc.getPageSource()),xml,cfc)); } } private static void saveMapping(ORMConfiguration ormConf, Component cfc, Element hm) { if(ormConf.saveMapping()){ Resource res=cfc.getPageSource().getResource(); if(res!=null){ res=res.getParentResource().getRealResource(res.getName()+".hbm.xml"); try{ CommonUtil.write(res, XMLCaster.toString(hm,false,true, HibernateSessionFactory.HIBERNATE_3_PUBLIC_ID, HibernateSessionFactory.HIBERNATE_3_SYSTEM_ID, HibernateSessionFactory.HIBERNATE_3_CHARSET.name()), HibernateSessionFactory.HIBERNATE_3_CHARSET, false); } catch(Exception e){} } } } private static long loadMapping(StringBuilder sb,ORMConfiguration ormConf, Component cfc) { Resource res=cfc.getPageSource().getResource(); if(res!=null){ res=res.getParentResource().getRealResource(res.getName()+".hbm.xml"); try{ sb.append(CommonUtil.toString(res, CommonUtil.UTF8)); return res.lastModified(); } catch(Exception e){} } return 0; } @Override public int getMode() { //MUST impl return MODE_LAZY; } @Override public String getLabel() { return "Hibernate"; } @Override public ORMConfiguration getConfiguration(PageContext pc) { ApplicationContext ac = pc.getApplicationContext(); if(!ac.isORMEnabled()) return null; return ac.getORMConfiguration(); } /** * @param pc * @param session * @param entityName name of the entity to get * @param unique create a unique version that can be manipulated * @param init call the nit method of the cfc or not * @return * @throws PageException */ public Component create(PageContext pc, HibernateORMSession session,String entityName, boolean unique) throws PageException { SessionFactoryData data = session.getSessionFactoryData(); // get existing entity Component cfc = _create(pc,entityName,unique,data); if(cfc!=null)return cfc; // reinit ORMEngine SessionFactory _old= getSessionFactory(pc,INIT_NOTHING);//_factory; SessionFactory _new = getSessionFactory(pc,INIT_CFCS); if(_old!=_new){ session.resetSession(_new); cfc = _create(pc,entityName,unique,data); if(cfc!=null)return cfc; } ORMConfiguration ormConf = pc.getApplicationContext().getORMConfiguration(); Resource[] locations = ormConf.getCfcLocations(); throw ExceptionUtil.createException(data,null, "No entity (persitent component) with name ["+entityName+"] found, available entities are ["+CommonUtil.toList(data.getEntityNames(), ", ")+"] ", "component are searched in the following directories ["+toString(locations)+"]"); } private String toString(Resource[] locations) { if(locations==null) return ""; StringBuilder sb=new StringBuilder(); for(int i=0;i<locations.length;i++){ if(i>0) sb.append(", "); sb.append(locations[i].getAbsolutePath()); } return sb.toString(); } private static Component _create(PageContext pc, String entityName, boolean unique, SessionFactoryData data) throws PageException { CFCInfo info = data.cfcs.get(HibernateUtil.id(entityName)); if(info!=null) { Component cfc = info.getCFC(); if(unique){ cfc=(Component)Duplicator.duplicate(cfc,false); if(cfc.contains(pc,CommonUtil.INIT))cfc.call(pc, "init",new Object[]{}); } return cfc; } return null; } } class CFCInfo { private String xml; private long modified; private Component cfc; public CFCInfo(long modified, String xml, Component cfc) { this.modified=modified; this.xml=xml; this.cfc=cfc; } /** * @return the cfc */ public Component getCFC() { return cfc; } /** * @return the xml */ public String getXML() { return xml; } /** * @return the modified */ public long getModified() { return modified; } }