001    package railo.runtime.orm.hibernate;
002    
003    import java.io.Serializable;
004    import java.util.Iterator;
005    import java.util.List;
006    import java.util.Map;
007    import java.util.Map.Entry;
008    
009    import org.hibernate.Criteria;
010    import org.hibernate.FlushMode;
011    import org.hibernate.NonUniqueResultException;
012    import org.hibernate.QueryException;
013    import org.hibernate.Session;
014    import org.hibernate.SessionFactory;
015    import org.hibernate.Transaction;
016    import org.hibernate.criterion.Example;
017    import org.hibernate.criterion.Order;
018    import org.hibernate.criterion.Restrictions;
019    import org.hibernate.engine.query.HQLQueryPlan;
020    import org.hibernate.engine.query.ParameterMetadata;
021    import org.hibernate.engine.query.QueryPlanCache;
022    import org.hibernate.exception.ConstraintViolationException;
023    import org.hibernate.metadata.ClassMetadata;
024    import org.hibernate.type.Type;
025    
026    import railo.commons.lang.StringUtil;
027    import railo.commons.lang.types.RefBoolean;
028    import railo.commons.lang.types.RefBooleanImpl;
029    import railo.runtime.Component;
030    import railo.runtime.ComponentScope;
031    import railo.runtime.PageContext;
032    import railo.runtime.config.ConfigWebImpl;
033    import railo.runtime.db.DatasourceConnection;
034    import railo.runtime.db.SQLItem;
035    import railo.runtime.exp.PageException;
036    import railo.runtime.exp.PageExceptionImpl;
037    import railo.runtime.op.Caster;
038    import railo.runtime.op.Decision;
039    import railo.runtime.orm.ORMEngine;
040    import railo.runtime.orm.ORMException;
041    import railo.runtime.orm.ORMSession;
042    import railo.runtime.orm.ORMTransaction;
043    import railo.runtime.type.Array;
044    import railo.runtime.type.ArrayImpl;
045    import railo.runtime.type.Collection.Key;
046    import railo.runtime.type.KeyImpl;
047    import railo.runtime.type.Struct;
048    import railo.runtime.type.StructImpl;
049    import railo.runtime.type.scope.Argument;
050    import railo.runtime.type.scope.ArgumentImpl;
051    import railo.runtime.type.util.CollectionUtil;
052    
053    public class HibernateORMSession implements ORMSession{
054    
055            private HibernateORMEngine engine;
056            private Session _session;
057            private DatasourceConnection dc;
058    
059            public HibernateORMSession(HibernateORMEngine engine, SessionFactory factory, DatasourceConnection dc){
060                    this.engine=engine;
061                    this.dc=dc;
062                    resetSession(factory);
063                    //this._session=session;
064            }
065            
066            private Session session(){
067                    return _session;
068            }
069    
070            private SessionFactory getSessionFactory(PageContext pc){
071                    // engine.getSessionFactory(pc);
072                    return _session.getSessionFactory();
073            }
074            SessionFactory getSessionFactory(){
075                    // engine.getSessionFactory(pc);
076                    return _session.getSessionFactory();
077            }
078            
079            void resetSession(SessionFactory factory) {
080                    _session = factory.openSession(dc.getConnection());
081                    _session.setFlushMode(FlushMode.MANUAL);
082            }
083    
084            
085    
086            @Override
087            public DatasourceConnection getDatasourceConnection() {
088                    return dc;
089            }
090            
091    
092            @Override
093            public ORMEngine getEngine() {
094                    return engine;
095            }
096            
097            @Override
098            public void flush(PageContext pc) throws PageException {
099                    try {
100                            session().flush();
101                    }
102                    catch(ConstraintViolationException cve){
103                            PageException pe = HibernateException.toPageException(engine, cve);
104                            if(pe instanceof PageExceptionImpl && !StringUtil.isEmpty(cve.getConstraintName())) {
105                                    //print.o(cve.getConstraintName());
106                                    ((PageExceptionImpl)pe).setAdditional(KeyImpl.init("constraint name"), cve.getConstraintName() );
107                            }
108                            throw pe;
109                    }
110                    
111            }
112    
113            @Override
114            public void delete(PageContext pc, Object obj) throws PageException {
115                    if(Decision.isArray(obj)){
116                            Transaction trans = session().getTransaction();
117                            if(trans.isActive()) trans.begin();
118                            else trans=null;
119                            
120                            try{
121                                    Iterator it = Caster.toArray(obj).valueIterator();
122                                    while(it.hasNext()){
123                                            _delete(pc,HibernateCaster.toComponent(it.next()));
124                                    }
125                            }
126                            catch(Throwable t){
127                                    if(trans!=null)trans.rollback();
128                                    throw Caster.toPageException(t);
129                            }
130                            if(trans!=null)trans.commit();
131                    }
132                    else _delete(pc,HibernateCaster.toComponent(obj));
133            }
134            
135            public void _delete(PageContext pc, Component cfc) throws PageException {
136                    engine.checkExistent(pc,cfc);
137                    //Session session = getSession(pc,cfc);
138                    
139                    try{
140                            session().delete(HibernateCaster.getEntityName(cfc), cfc);
141                    }
142                    catch(Throwable t){
143                            throw Caster.toPageException(t);
144                    }
145            }
146            
147            
148            
149            @Override
150            public void save(PageContext pc, Object obj,boolean forceInsert) throws PageException {
151                    Component cfc = HibernateCaster.toComponent(obj);
152                    //Session session = getSession(pc, cfc);
153                    String name = HibernateCaster.getEntityName(cfc);
154                    try {
155                            if(forceInsert)
156                                    session().save(name, cfc);
157                            else
158                                            session().saveOrUpdate(name, cfc);
159                    }
160                    catch(Throwable t){
161                            throw HibernateException.toPageException(getEngine(), t);
162                    }
163            }
164            
165            @Override
166            public void reload(PageContext pc,Object obj) throws PageException {
167                    Component cfc = HibernateCaster.toComponent(obj);
168                    engine.checkExistent(pc,cfc);
169                    //Session session = getSession(pc,cfc);
170                    session().refresh(cfc);
171            }
172            
173    
174            @Override
175            public Component create(PageContext pc, String entityName)throws PageException {
176                    return engine.create(pc,this, entityName,true);
177            }
178            
179            @Override
180            public void clear(PageContext pc) throws PageException {
181                    session().clear();
182            }
183            
184            @Override
185            public void evictQueries(PageContext pc) throws PageException {
186                    evictQueries(pc, null);
187            }
188    
189            @Override
190            public void evictQueries(PageContext pc,String cacheName) throws PageException {
191                    SessionFactory f = getSessionFactory(pc);
192                    if(StringUtil.isEmpty(cacheName))f.evictQueries();
193                    else f.evictQueries(cacheName);
194            }
195            
196            @Override
197            public void evictEntity(PageContext pc, String entityName) throws PageException {
198                    evictEntity(pc, entityName, null);
199            }
200    
201            @Override
202            public void evictEntity(PageContext pc, String entityName, String id) throws PageException {
203                    SessionFactory f = getSessionFactory(pc);
204                    
205                    if(id==null) {
206                            f.evictEntity(entityName);
207                    }
208                    else {
209                            f.evictEntity(entityName,Caster.toSerializable(id));
210                    }
211            }
212            
213            @Override
214            public void evictCollection(PageContext pc, String entityName, String collectionName) throws PageException {
215                    evictCollection(pc, entityName, collectionName, null);
216            }
217    
218            @Override
219            public void evictCollection(PageContext pc, String entityName, String collectionName, String id) throws PageException {
220                    SessionFactory f = getSessionFactory(pc);
221                    String role=entityName+"."+collectionName;
222                    if(id==null) {
223                            f.evictCollection(role);
224                    }
225                    else {
226                            f.evictCollection(role,Caster.toSerializable(id));
227                    }
228            }
229            
230             
231            
232            
233            
234            
235            
236    
237            @Override
238            public Object executeQuery(PageContext pc,String hql, Array params, boolean unique,Struct queryOptions) throws PageException {
239                    return _executeQuery(pc, hql, params, unique, queryOptions);
240            }
241    
242            @Override
243            public Object executeQuery(PageContext pc,String hql, Struct params, boolean unique,Struct queryOptions) throws PageException {
244                    return _executeQuery(pc, hql, params, unique, queryOptions);
245            }
246            
247            public Object _executeQuery(PageContext pc,String hql, Object params, boolean unique,Struct queryOptions) throws PageException {
248                    try{
249                            return __executeQuery(pc, hql, params, unique, queryOptions);
250                    }
251                    catch(QueryException qe) {
252                            // argument scope is array and struct at the same time, by default it is handled as struct, if this fails try it as array
253                            if(params instanceof Argument) {
254                                    try{
255                                            return __executeQuery(pc, hql, ArgumentImpl.toArray((Argument)params), unique, queryOptions);
256                                    }
257                                    catch(Throwable t){t.printStackTrace();}
258                            }
259                            throw qe;
260                    }
261                    
262                    
263            }
264            
265            private Object __executeQuery(PageContext pc,String hql, Object params, boolean unique,Struct options) throws PageException {
266                    //Session session = getSession(pc,null);
267                    hql=hql.trim();
268                    org.hibernate.Query query = session().createQuery(hql); 
269                    // options
270                    if(options!=null){
271                            // maxresults
272                            Object obj=options.get("maxresults",null);
273                            if(obj!=null) {
274                                    int max=Caster.toIntValue(obj,-1);
275                                    if(max<0) throw new ORMException(engine,"option [maxresults] has an invalid value ["+obj+"], value should be a number bigger or equal to 0");
276                                    query.setMaxResults(max);
277                            }
278                            // offset
279                            obj=options.get("offset",null);
280                            if(obj!=null) {
281                                    int off=Caster.toIntValue(obj,-1);
282                                    if(off<0) throw new ORMException(engine,"option [offset] has an invalid value ["+obj+"], value should be a number bigger or equal to 0");
283                                    query.setFirstResult(off);
284                            }
285                            // readonly
286                            obj=options.get("readonly",null);
287                            if(obj!=null) {
288                                    Boolean ro=Caster.toBoolean(obj,null);
289                                    if(ro==null) throw new ORMException(engine,"option [readonly] has an invalid value ["+obj+"], value should be a boolean value");
290                                    query.setReadOnly(ro.booleanValue());
291                            }
292                            // timeout
293                            obj=options.get("timeout",null);
294                            if(obj!=null) {
295                                    int to=Caster.toIntValue(obj,-1);
296                                    if(to<0) throw new ORMException(engine,"option [timeout] has an invalid value ["+obj+"], value should be a number bigger or equal to 0");
297                                    query.setTimeout(to);
298                            }
299            }
300                    
301                    
302                    // params
303                    if(params!=null){
304                            QueryPlanCache cache=engine.getQueryPlanCache(pc);
305                            HQLQueryPlan plan = cache.getHQLQueryPlan(hql, false, java.util.Collections.EMPTY_MAP);
306                            ParameterMetadata meta = plan.getParameterMetadata();
307                            Type type;
308                            Object obj;
309                            
310    
311                            // struct
312                            if(Decision.isStruct(params)) {
313                                    Struct sct=Caster.toStruct(params);
314                                    Key[] keys = CollectionUtil.keys(sct);
315                                    String name;
316                                    // fix case-senstive
317                                    Struct names=new StructImpl();
318                                    if(meta!=null){
319                                            Iterator<String> it = meta.getNamedParameterNames().iterator();
320                                            while(it.hasNext()){
321                                                    name=it.next();
322                                                    names.setEL(name, name);
323                                            }
324                                    }
325                                    
326                                    RefBoolean isArray=new RefBooleanImpl();
327                                    for(int i=0;i<keys.length;i++){
328                                            obj=sct.get(keys[i],null);
329                                            if(meta!=null){
330                                                    name=(String) names.get(keys[i],null);
331                                                    if(name==null) continue; // param not needed will be ignored
332                                                    type = meta.getNamedParameterExpectedType(name);
333                                                    obj=HibernateCaster.toSQL(engine, type, obj,isArray);
334                                                    if(isArray.toBooleanValue())
335                                                            query.setParameterList(name, (Object[])obj,type);
336                                                    else
337                                                            query.setParameter(name, obj,type);
338                                                    
339                                                    
340                                            }
341                                            else
342                                                    query.setParameter(keys[i].getString(), obj);
343                                    }
344                            }
345                            
346                            // array
347                            else if(Decision.isArray(params)){
348                                    Array arr=Caster.toArray(params);
349                                    Iterator it = arr.valueIterator();
350                                    int index=0;
351                                    SQLItem item;
352                                    RefBoolean isArray=null;//new RefBooleanImpl();
353                                    while(it.hasNext()){
354                                            obj=it.next();
355                                            if(obj instanceof SQLItem) {
356                                                    item=(SQLItem) obj;
357                                                    obj=item.getValue();
358                                                    //HibernateCaster.toHibernateType(item.getType(), null); MUST
359                                                    //query.setParameter(index, item.getValue(),type);
360                                            }
361                                            if(meta!=null){
362                                                    type = meta.getOrdinalParameterExpectedType(index+1);
363                                                    obj=HibernateCaster.toSQL(engine, type, obj,isArray);
364                                                    // TOOD can the following be done somehow
365                                                    //if(isArray.toBooleanValue())
366                                                    //      query.setParameterList(index, (Object[])obj,type);
367                                                    //else
368                                                            query.setParameter(index, obj,type);
369                                            }
370                                            else
371                                                    query.setParameter(index, obj);
372                                            index++;
373                                    }
374                                    if(meta.getOrdinalParameterCount()>index)
375                                            throw new ORMException(engine,"parameter array is to small ["+arr.size()+"], need ["+meta.getOrdinalParameterCount()+"] elements");
376                            }
377                    }
378                    
379                    
380                    
381                    // select
382                    if(StringUtil.startsWithIgnoreCase(hql,"select") || StringUtil.startsWithIgnoreCase(hql,"from")){
383                            if(unique){
384                                    return uniqueResult(query);
385                            }
386                            
387                            return query.list();
388                    }
389                // update
390                    return Caster.toDouble(query.executeUpdate());
391            }
392            
393            
394            
395            private Object uniqueResult(org.hibernate.Query query) {
396                    try{
397                            return query.uniqueResult();
398                    }
399                    catch(NonUniqueResultException e){
400                            List list = query.list();
401                            if(list.size()>0) return list.iterator().next();
402                            throw e;
403                    }
404            }
405    
406            @Override
407            public railo.runtime.type.Query toQuery(PageContext pc, Object obj, String name) throws PageException {
408                    return HibernateCaster.toQuery(pc,this,obj,name);
409            }
410            
411            @Override
412            public void close(PageContext pc) throws PageException {
413                    session().close();
414                    ((ConfigWebImpl)pc.getConfig()).getDatasourceConnectionPool().releaseDatasourceConnection(dc);
415            }
416            
417            @Override
418            public Component merge(PageContext pc, Object obj) throws PageException {
419                    Component cfc = HibernateCaster.toComponent(obj);
420                    
421                    engine.checkExistent(pc,cfc);
422                    
423                    String name=HibernateCaster.getEntityName(cfc);
424                    
425                    //Session session = getSession(pc, cfc);
426            try     {
427                return Caster.toComponent(session().merge(name, cfc));
428            }
429            catch(HibernateException e) {
430                    throw new ORMException(e);
431            }
432            }
433            
434    
435            @Override
436            public Component load(PageContext pc, String name, Struct filter) throws PageException {
437                    return (Component) load(pc, name, filter, null, null, true);
438            }
439    
440            @Override
441            public Array loadAsArray(PageContext pc, String name, Struct filter) throws PageException {
442                    return loadAsArray(pc, name, filter,null,null);
443            }
444            
445            @Override
446            public Array loadAsArray(PageContext pc, String name, String id, String order) throws PageException{
447                    return loadAsArray(pc, name, id);// order is ignored in this case ACF compatibility
448            }
449            
450            @Override
451            public Array loadAsArray(PageContext pc, String name, String id) throws PageException {
452                    Array arr=new ArrayImpl();
453                    Component c = load(pc, name, id);
454                    if(c!=null)arr.append(c);
455                    return arr;
456            }
457            
458            @Override
459            public Array loadAsArray(PageContext pc, String name, Struct filter, Struct options) throws PageException {
460                    return loadAsArray(pc, name, filter,options,null);
461            }
462            
463            @Override
464            public Array loadAsArray(PageContext pc, String name, Struct filter, Struct options, String order) throws PageException {
465                    return Caster.toArray(load(pc, name, filter, options, order, false));
466            }
467            
468            @Override
469            public Component load(PageContext pc, String cfcName, String id) throws PageException {
470                    //Component cfc = create(pc,cfcName);
471                    
472                    
473                    Component cfc=engine.create(pc, this,cfcName,false);
474                    
475                    String name = HibernateCaster.getEntityName(cfc);
476                    Object obj=null;
477                    try{
478                            ClassMetadata metaData = getSessionFactory(pc).getClassMetadata(name);
479                            if(metaData==null) throw new ORMException(engine,"could not load meta information for entity ["+name+"]");
480                            Serializable oId = Caster.toSerializable(
481                                            Caster.castTo(pc, 
482                                                            metaData
483                                                                    .getIdentifierType()
484                                                                    .getReturnedClass(), 
485                                                            id));
486                            obj=session().get(name,oId);
487                    }
488                    catch(Throwable t){
489                            throw Caster.toPageException(t);
490                    }
491                    
492                    return (Component) obj;
493            }
494            
495            @Override
496            public Component loadByExample(PageContext pc, Object obj) throws PageException {
497                    return Caster.toComponent(loadByExample(pc,obj, true));
498            }
499            
500            @Override
501            public Array loadByExampleAsArray(PageContext pc, Object obj) throws PageException {
502                    return Caster.toArray(loadByExample(pc,obj, false));
503            }
504            
505            private Object loadByExample(PageContext pc, Object obj,  boolean unique) throws PageException {
506                     Component cfc=HibernateCaster.toComponent(obj);
507                     ComponentScope scope = cfc.getComponentScope();
508                     String name=HibernateCaster.getEntityName(cfc);
509                     //Session session=getSession(pc, cfc);
510                     
511                     Object rtn=null;
512                     
513                     try{
514                            //trans.begin();
515                            
516                            ClassMetadata metaData = getSessionFactory(pc).getClassMetadata(name);
517                            String idName = metaData.getIdentifierPropertyName();
518                            Type idType = metaData.getIdentifierType();
519                     
520                            Criteria criteria=session().createCriteria(name);
521                            if(!StringUtil.isEmpty(idName)){
522                                    Object idValue = scope.get(KeyImpl.init(idName),null);
523                                    if(idValue!=null){
524                                            criteria.add(Restrictions.eq(idName, HibernateCaster.toSQL(engine, idType, idValue,null)));
525                                    }
526                            }
527                            criteria.add(Example.create(cfc));
528                 
529                    // execute
530                            
531                            if(!unique){
532                                    rtn = criteria.list();
533                            }
534                            else {
535                                    //Map map=(Map) criteria.uniqueResult();
536                                    rtn= criteria.uniqueResult();
537                            }
538                     }
539                     catch(Throwable t){
540                            // trans.rollback();
541                            throw Caster.toPageException(t);
542                     }
543                     //trans.commit();
544    
545                     return rtn;
546            }
547            
548            
549            private Object load(PageContext pc, String cfcName, Struct filter, Struct options, String order, boolean unique) throws PageException {
550                    Component cfc=engine.create(pc, this,cfcName,false);
551                    
552                    String name = HibernateCaster.getEntityName(cfc);
553                    ClassMetadata metaData = null;
554                    
555                    Object rtn;
556                    try{
557                            //trans.begin();
558                            
559                            Criteria criteria = session().createCriteria(name);
560                            
561                            // filter
562                            if(filter!=null && !filter.isEmpty()){
563                                    
564                                    metaData = getSessionFactory(pc).getClassMetadata(name);
565                                    
566                                    
567                                    
568                                    Object value;
569                                    Map.Entry entry;
570                                    Iterator it = filter.entrySet().iterator();
571                                    String colName;
572                                    while(it.hasNext()){
573                                            entry=(Entry) it.next();
574                                            colName=HibernateUtil.validateColumnName(metaData, Caster.toString(entry.getKey()));
575                                            Type type = HibernateUtil.getPropertyType(metaData,colName,null);
576                                            value=HibernateCaster.toSQL(engine,type,entry.getValue(),null);
577                                            if(value!=null) criteria.add(Restrictions.eq(colName, value));
578                                            else                    criteria.add(Restrictions.isNull(colName));
579                                            
580                                            
581                                            
582                                            
583                                    }
584                            }
585                            
586                            // options
587                            boolean ignoreCase=false;
588                            if(options!=null && !options.isEmpty()){
589                                    // ignorecase
590                                    Boolean ignorecase=Caster.toBoolean(options.get("ignorecase",null),null);
591                            if(ignorecase!=null)ignoreCase=ignorecase.booleanValue();
592                            
593                                    // offset
594                                    int offset=Caster.toIntValue(options.get("offset",null),0);
595                                    if(offset>0) criteria.setFirstResult(offset);
596                            
597                                    // maxResults
598                                    int max=Caster.toIntValue(options.get("maxresults",null),-1);
599                                    if(max>-1) criteria.setMaxResults(max);
600                            
601                                    // cacheable
602                                    Boolean cacheable=Caster.toBoolean(options.get("cacheable",null),null);
603                            if(cacheable!=null)criteria.setCacheable(cacheable.booleanValue());
604                            
605                            // MUST cacheName ?
606                            
607                                    // maxResults
608                                    int timeout=Caster.toIntValue(options.get("timeout",null),-1);
609                                    if(timeout>-1) criteria.setTimeout(timeout);
610                            }
611                            
612                            // order 
613                            if(!StringUtil.isEmpty(order)){
614                                    if(metaData==null)metaData = getSessionFactory(pc).getClassMetadata(name);
615                                    
616                                    String[] arr = railo.runtime.type.util.ListUtil.listToStringArray(order, ',');
617                                    railo.runtime.type.util.ListUtil.trimItems(arr);
618                            String[] parts;
619                            String col;
620                            boolean isDesc;
621                            Order _order;
622                            //ColumnInfo ci;
623                            for(int i=0;i<arr.length;i++) {
624                                    parts=railo.runtime.type.util.ListUtil.toStringArray(railo.runtime.type.util.ListUtil.listToArray(arr[i],  " \t\n\b\r"));
625                                    railo.runtime.type.util.ListUtil.trimItems(parts);
626                                col=parts[0];
627                                
628                                col=HibernateUtil.validateColumnName(metaData, col);
629                                            isDesc=false;
630                                            if(parts.length>1){
631                                                    if(parts[1].equalsIgnoreCase("desc"))isDesc=true;
632                                                    else if(!parts[1].equalsIgnoreCase("asc")){
633                                                            throw new ORMException("invalid order direction defintion ["+parts[1]+"]","valid values are [asc, desc]");
634                                                    }
635                                                    
636                                            }
637                                            _order=isDesc?Order.desc(col):Order.asc(col);
638                                if(ignoreCase)_order.ignoreCase();
639                                
640                                criteria.addOrder(_order);
641                            
642                            }
643                            }
644                            
645                            // execute
646                            if(!unique){
647                                    rtn = HibernateCaster.toCFML(criteria.list());
648                            }
649                            else {
650                                    rtn= HibernateCaster.toCFML(criteria.uniqueResult());
651                            }
652                            
653                            
654                    }
655                    catch(Throwable t){
656                            throw Caster.toPageException(t);
657                    }
658                    
659                    return rtn;
660            }
661            
662            
663            
664    
665            @Override
666            public Session getRawSession() {
667                    return session();
668            }
669    
670            @Override
671            public boolean isValid() {
672                    return session()!=null && session().isOpen();
673            }
674    
675            @Override
676            public ORMTransaction getTransaction(boolean autoManage) {
677                    return new HibernateORMTransaction(session(),autoManage);
678            }
679    }