001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.runtime.orm;
020
021import java.io.IOException;
022import java.util.ArrayList;
023import java.util.Iterator;
024
025import lucee.commons.digest.MD5;
026import lucee.commons.io.res.Resource;
027import lucee.commons.io.res.util.ResourceUtil;
028import lucee.commons.lang.StringUtil;
029import lucee.runtime.PageContext;
030import lucee.runtime.PageSource;
031import lucee.runtime.config.Config;
032import lucee.runtime.config.ConfigWebImpl;
033import lucee.runtime.engine.ThreadLocalPageContext;
034import lucee.runtime.exp.ExpressionException;
035import lucee.runtime.listener.ApplicationContext;
036import lucee.runtime.op.Caster;
037import lucee.runtime.op.Decision;
038import lucee.runtime.type.Array;
039import lucee.runtime.type.ArrayImpl;
040import lucee.runtime.type.Collection;
041import lucee.runtime.type.Collection.Key;
042import lucee.runtime.type.KeyImpl;
043import lucee.runtime.type.Struct;
044import lucee.runtime.type.StructImpl;
045import lucee.runtime.type.util.KeyConstants;
046import lucee.runtime.type.util.ListUtil;
047
048import org.w3c.dom.Attr;
049import org.w3c.dom.Element;
050import org.w3c.dom.NamedNodeMap;
051
052import flex.messaging.util.ExceptionUtil;
053
054public class ORMConfigurationImpl implements ORMConfiguration {
055        public static final int DBCREATE_NONE=0;
056        public static final int DBCREATE_UPDATE=1;
057        public static final int DBCREATE_DROP_CREATE=2;
058        
059
060        public static final Key AUTO_GEN_MAP = KeyImpl.init("autogenmap");
061        public static final Key CATALOG = KeyImpl.init("catalog");
062        public static final Key IS_DEFAULT_CFC_LOCATION = KeyImpl.init("isDefaultCfclocation");
063        public static final Key DB_CREATE = KeyImpl.init("dbCreate");
064        public static final Key DIALECT = KeyImpl.init("dialect");
065        public static final Key FLUSH_AT_REQUEST_END = KeyImpl.init("flushAtRequestEnd");
066        public static final Key LOG_SQL = KeyImpl.init("logSql");
067        public static final Key SAVE_MAPPING = KeyImpl.init("savemapping");
068        public static final Key SCHEMA = KeyImpl.init("schema");
069        public static final Key SECONDARY_CACHE_ENABLED = KeyImpl.init("secondarycacheenabled");
070        public static final Key SQL_SCRIPT = KeyImpl.init("sqlscript");
071        public static final Key USE_DB_FOR_MAPPING = KeyImpl.init("useDBForMapping");
072        public static final Key CACHE_CONFIG = KeyImpl.init("cacheconfig");
073        public static final Key CACHE_PROVIDER = KeyImpl.init("cacheProvider");
074        public static final Key ORM_CONFIG = KeyImpl.init("ormConfig");
075        public static final Key EVENT_HANDLING = KeyImpl.init("eventHandling");
076        public static final Key EVENT_HANDLER = KeyImpl.init("eventHandler");
077        public static final Key AUTO_MANAGE_SESSION = KeyImpl.init("autoManageSession");
078        public static final Key NAMING_STRATEGY = KeyImpl.init("namingstrategy");
079        public static final Key CFC_LOCATION = KeyImpl.init("cfcLocation");
080        
081        
082        private boolean autogenmap=true;
083        private String catalog;
084        private Resource[] cfcLocations;
085        private int dbCreate=DBCREATE_NONE;
086        private String dialect;
087        private Boolean eventHandling=null;
088        private boolean flushAtRequestEnd=true;
089        private boolean logSQL;
090        private boolean saveMapping;
091        private String schema;
092        private boolean secondaryCacheEnabled;
093        private Resource sqlScript;
094        private boolean useDBForMapping=true;
095        private Resource cacheConfig;
096        private String cacheProvider;
097        private Resource ormConfig;
098        private String eventHandler;
099        private String namingStrategy;
100        private boolean isDefaultCfcLocation=true;
101        private boolean skipCFCWithError=true;
102        private boolean autoManageSession=true;
103
104        private ORMConfigurationImpl(){
105                autogenmap=true;
106                dbCreate=DBCREATE_NONE;
107                flushAtRequestEnd=true;
108                useDBForMapping=true;           
109        }
110        
111        
112        
113        
114
115
116        public static ORMConfiguration load(Config config,ApplicationContext ac, Element el, Resource defaultCFCLocation,ORMConfiguration defaultConfig) {
117                return _load(config,ac, new _GetElement(el),defaultCFCLocation,defaultConfig);
118        }
119        
120        public static ORMConfiguration load(Config config,ApplicationContext ac,Struct settings, Resource defaultCFCLocation,ORMConfiguration defaultConfig) {
121                return _load(config,ac, new _GetStruct(settings),defaultCFCLocation,defaultConfig);
122        }
123
124        private static ORMConfiguration _load(Config config,ApplicationContext ac, _Get settings, Resource defaultCFCLocation,ORMConfiguration dc) {
125                
126                if(dc==null)dc=new ORMConfigurationImpl();
127                ORMConfigurationImpl c = ((ORMConfigurationImpl)dc).duplicate();
128                c.cfcLocations=defaultCFCLocation==null?new Resource[0]:new Resource[]{defaultCFCLocation};
129                
130                // autogenmap
131                c.autogenmap=Caster.toBooleanValue(settings.get(AUTO_GEN_MAP,dc.autogenmap()),dc.autogenmap());
132                
133                // catalog
134                c.catalog=StringUtil.trim(Caster.toString(settings.get(CATALOG,dc.getCatalog()),dc.getCatalog()),dc.getCatalog());
135                
136                // cfclocation
137                Object obj = settings.get(KeyConstants._cfcLocation,null);
138                
139                if(obj!=null){
140                        java.util.List<Resource> list=loadCFCLocation(config,ac,obj,true);
141                        
142                        if(list!=null && list.size()>0){
143                                c.cfcLocations=list.toArray(new Resource[list.size()]);
144                                c.isDefaultCfcLocation=false;
145                        }
146                }
147                if(c.cfcLocations == null)
148                        c.cfcLocations=defaultCFCLocation==null?new Resource[0]:new Resource[]{defaultCFCLocation};
149                
150                // dbcreate
151                obj = settings.get(DB_CREATE,null);
152                if(obj!=null){
153                        String str = Caster.toString(obj,"").trim().toLowerCase();
154                        c.dbCreate=dbCreateAsInt(str);
155                }
156                
157                // dialect
158                c.dialect = StringUtil.trim(Caster.toString(settings.get(DIALECT,dc.getDialect()),dc.getDialect()),dc.getDialect());
159                
160                // namingstrategy
161                c.namingStrategy=Caster.toString(settings.get(NAMING_STRATEGY,dc.namingStrategy()),dc.namingStrategy());
162                
163                // eventHandler
164                c.eventHandler=Caster.toString(settings.get(EVENT_HANDLER,dc.eventHandler()),dc.eventHandler());
165                
166                // eventHandling
167                Boolean b=Caster.toBoolean(settings.get(EVENT_HANDLING,null),null);
168                if(b==null) {
169                        if(dc.eventHandling()) 
170                                b=Boolean.TRUE;
171                        else 
172                                b=!StringUtil.isEmpty(c.eventHandler,true);
173                }
174                c.eventHandling=b;
175                
176                // flushatrequestend
177                c.flushAtRequestEnd=Caster.toBooleanValue(settings.get(FLUSH_AT_REQUEST_END,dc.flushAtRequestEnd()),dc.flushAtRequestEnd());
178                
179                // logSQL
180                c.logSQL=Caster.toBooleanValue(settings.get(LOG_SQL,dc.logSQL()),dc.logSQL());
181                
182
183                // autoManageSession
184                c.autoManageSession=Caster.toBooleanValue(settings.get(AUTO_MANAGE_SESSION,dc.autoManageSession()),dc.autoManageSession());
185                
186                // skipCFCWithError
187                c.skipCFCWithError=Caster.toBooleanValue(settings.get(KeyConstants._skipCFCWithError,dc.skipCFCWithError()),dc.skipCFCWithError());
188                
189                // savemapping
190                c.saveMapping=Caster.toBooleanValue(settings.get(SAVE_MAPPING,dc.saveMapping()),dc.saveMapping());
191                
192                // schema
193                c.schema=StringUtil.trim(Caster.toString(settings.get(SCHEMA,dc.getSchema()),dc.getSchema()),dc.getSchema());
194                
195                // secondarycacheenabled
196                c.secondaryCacheEnabled=Caster.toBooleanValue(settings.get(SECONDARY_CACHE_ENABLED,dc.secondaryCacheEnabled()),dc.secondaryCacheEnabled());
197                
198                // sqlscript
199                obj = settings.get(SQL_SCRIPT,null);
200                if(!StringUtil.isEmpty(obj)){
201                        try {
202                                c.sqlScript=toRes(config, obj, true);
203                        } catch (ExpressionException e) {
204                                //print.e(e);
205                        }
206                }
207                
208                // useDBForMapping
209                c.useDBForMapping=Caster.toBooleanValue(settings.get(USE_DB_FOR_MAPPING,dc.useDBForMapping()),dc.useDBForMapping());
210                
211                // cacheconfig
212                obj = settings.get(CACHE_CONFIG,null);
213                if(!StringUtil.isEmpty(obj)){
214                        try {
215                                c.cacheConfig=toRes(config, obj, true);
216                        } catch (ExpressionException e) {
217                                //print.printST(e);
218                        }
219                }
220                
221                // cacheprovider
222                c.cacheProvider=StringUtil.trim(Caster.toString(settings.get(CACHE_PROVIDER,dc.getCacheProvider()),dc.getCacheProvider()),dc.getCacheProvider());
223                
224                // ormconfig
225                obj = settings.get(ORM_CONFIG,null);
226                if(!StringUtil.isEmpty(obj)){
227                        try {
228                                c.ormConfig=toRes(config, obj, true);
229                        } catch (ExpressionException e) {
230                                //print.printST(e);
231                        }
232                }
233                
234                return c;
235        }       
236
237        public static java.util.List<Resource> loadCFCLocation(Config config, ApplicationContext ac,Object obj, boolean onlyDir) {
238                
239                Resource res;
240                if(!Decision.isArray(obj)){
241                        String list = Caster.toString(obj,null);
242                        if(!StringUtil.isEmpty(list)) {
243                                obj=ListUtil.listToArray(list, ',');
244                        }
245                }
246                
247                if(Decision.isArray(obj)) {
248                        Array arr=Caster.toArray(obj,null);
249                        java.util.List<Resource> list=new ArrayList<Resource>();
250                        Iterator<Object> it = arr.valueIterator();
251                        while(it.hasNext()){
252                                try     {
253                                        res=toResourceExisting(config,ac,it.next(),onlyDir);
254                                        if(res!=null) list.add(res);
255                                }
256                                catch(Throwable t){
257                                        lucee.commons.lang.ExceptionUtil.rethrowIfNecessary(t);
258                                }
259                        }
260                        return list;
261                }
262                
263                
264                return null;
265        }
266
267
268
269
270
271
272        private static Resource toRes(Config config, Object obj, boolean existing) throws ExpressionException {
273                PageContext pc = ThreadLocalPageContext.get();
274                if(pc!=null)return Caster.toResource(pc, obj, existing);
275                return Caster.toResource(config, obj, existing);
276        }
277
278        private static Resource toResourceExisting(Config config, ApplicationContext ac,Object obj, boolean onlyDir) {
279                //Resource root = config.getRootDirectory();
280                String path = Caster.toString(obj,null);
281                if(StringUtil.isEmpty(path,true)) return null;
282                path=path.trim();
283                Resource res;
284                PageContext pc = ThreadLocalPageContext.get();
285                
286                // first check relative to application.cfc
287                if(pc!=null) {
288                        if(ac==null) ac= pc.getApplicationContext();
289                        
290                        // abs path
291                        if(path.startsWith("/")){
292                                ConfigWebImpl cwi=(ConfigWebImpl) config;
293                                PageSource ps = cwi.getPageSourceExisting(
294                                                pc, ac==null?null:ac.getMappings(), path, false, false, true, false);
295                                //res=cwi.getPhysicalResourceExistingX(
296                                //              pc, 
297                                //              ac==null?null:ac.getMappings(), path, 
298                                //              false, false, true);
299                                if(ps!=null){
300                                        res=ps.getResource();
301                                        if(validExistingResource(res,onlyDir)) return res;
302                                }
303                                
304                        }
305                        // real path
306                        else {
307                                Resource src= ac!=null?ac.getSource():null;
308                                if(src!=null) {
309                                        res=src.getParentResource().getRealResource(path);
310                                        if(validExistingResource(res,onlyDir)) return res;
311                                }
312                                // happens when this is called from within the application.cfc (init)
313                                else {
314                                        res=ResourceUtil.toResourceNotExisting(pc, path);
315                                        if(validExistingResource(res,onlyDir)) return res;
316                                }
317                        }
318                }
319                
320                
321                
322                // then in the webroot
323                res=config.getRootDirectory().getRealResource(path);
324                if(validExistingResource(res,onlyDir)) return res;
325                
326                // then absolute
327                res = ResourceUtil.toResourceNotExisting(config, path);
328                
329                if(validExistingResource(res,onlyDir)) return res;
330                return null;
331        }
332
333
334
335
336
337
338        private static boolean validExistingResource(Resource res, boolean onlyDir) {
339                if(res==null) return false;
340                if(onlyDir) return res.isDirectory();
341                return res.exists();
342        }
343
344
345
346
347
348
349        private ORMConfigurationImpl duplicate() {
350                
351                ORMConfigurationImpl other = new ORMConfigurationImpl();
352                
353                
354                
355                other.autogenmap=autogenmap;
356                other.catalog=catalog;
357                other.cfcLocations=cfcLocations;
358                other.isDefaultCfcLocation=isDefaultCfcLocation;
359                other.dbCreate=dbCreate;
360                other.dialect=dialect;
361                other.eventHandler=eventHandler;
362                other.namingStrategy=namingStrategy;
363                other.eventHandling=eventHandling;
364                other.flushAtRequestEnd=flushAtRequestEnd;
365                other.logSQL=logSQL;
366                other.saveMapping=saveMapping;
367                other.schema=schema;
368                other.secondaryCacheEnabled=secondaryCacheEnabled;
369                other.sqlScript=sqlScript;
370                other.useDBForMapping=useDBForMapping;
371                other.cacheConfig=cacheConfig;
372                other.cacheProvider=cacheProvider;
373                other.ormConfig=ormConfig;
374                other.autoManageSession=autoManageSession;
375                other.skipCFCWithError=skipCFCWithError;
376                return other;
377        }
378
379        public String hash() {
380                
381                String data=autogenmap+":"+catalog+":"+isDefaultCfcLocation
382                +":"+dbCreate+":"+dialect+":"+eventHandling+":"+namingStrategy+":"+eventHandler+":"+flushAtRequestEnd+":"+logSQL+":"+autoManageSession+":"+skipCFCWithError+":"+saveMapping+":"+schema+":"+secondaryCacheEnabled+":"+
383                useDBForMapping+":"+cacheProvider
384                
385                +":"+toStr(cfcLocations)+":"+toStr(sqlScript)+":"+toStr(cacheConfig)+":"+toStr(ormConfig)
386                ;
387                
388                try {
389                        return MD5.getDigestAsString(data);
390                } catch (IOException e) {
391                        return null;
392                }
393        }
394
395
396
397
398
399        private String toStr(Resource res) {
400                if(res==null) return "";
401                return res.getAbsolutePath();
402        }
403        private String toStr(Resource[] reses) {
404                if(reses==null) return "";
405                StringBuilder sb=new StringBuilder();
406                for(int i=0;i<reses.length;i++){
407                        sb.append(toStr(reses[i]));
408                }
409                return sb.toString();
410        }
411
412
413
414
415
416
417        /**
418         * @return the autogenmap
419         */
420        public boolean autogenmap() {
421                return autogenmap;
422        }
423
424        /**
425         * @return the catalog
426         */
427        public String getCatalog() {
428                return catalog;
429        }
430
431        /**
432         * @return the cfcLocation
433         */
434        public Resource[] getCfcLocations() {
435                return cfcLocations;
436        }
437        public boolean isDefaultCfcLocation() {
438                return isDefaultCfcLocation;
439        }
440
441        /**
442         * @return the dbCreate
443         */
444        public int getDbCreate() {
445                return dbCreate;
446        }
447
448        /**
449         * @return the dialect
450         */
451        public String getDialect() {
452                return dialect;
453        }
454
455        /**
456         * @return the eventHandling
457         */
458        public boolean eventHandling() {
459                return eventHandling==null?false:eventHandling.booleanValue();
460        }
461
462        public String eventHandler() {
463                return eventHandler;
464        }
465
466        public String namingStrategy() {
467                return namingStrategy;
468        }
469        
470        
471
472        /**
473         * @return the flushAtRequestEnd
474         */
475        public boolean flushAtRequestEnd() {
476                return flushAtRequestEnd;
477        }
478
479        /**
480         * @return the logSQL
481         */
482        public boolean logSQL() {
483                return logSQL;
484        }
485
486        /**
487         * @return the saveMapping
488         */
489        public boolean saveMapping() {
490                return saveMapping;
491        }
492
493        /**
494         * @return the schema
495         */
496        public String getSchema() {
497                return schema;
498        }
499
500        /**
501         * @return the secondaryCacheEnabled
502         */
503        public boolean secondaryCacheEnabled() {
504                return secondaryCacheEnabled;
505        }
506
507        /**
508         * @return the sqlScript
509         */
510        public Resource getSqlScript() {
511                return sqlScript;
512        }
513
514        /**
515         * @return the useDBForMapping
516         */
517        public boolean useDBForMapping() {
518                return useDBForMapping;
519        }
520
521        /**
522         * @return the cacheConfig
523         */
524        public Resource getCacheConfig() {
525                return cacheConfig;
526        }
527
528        /**
529         * @return the cacheProvider
530         */
531        public String getCacheProvider() {
532                return cacheProvider;
533        }
534
535        /**
536         * @return the ormConfig
537         */
538        public Resource getOrmConfig() {
539                return ormConfig;
540        }
541
542        public boolean skipCFCWithError() {
543                return skipCFCWithError;
544        }
545        public boolean autoManageSession() {
546                return autoManageSession;
547        }
548
549
550
551
552        public Object toStruct() {
553                
554                Resource[] locs = getCfcLocations();
555                Array arrLocs=new ArrayImpl();
556                if(locs!=null)for(int i=0;i<locs.length;i++){
557                        arrLocs.appendEL(getAbsolutePath(locs[i]));
558                }
559                Struct sct=new StructImpl();
560                sct.setEL(AUTO_GEN_MAP,this.autogenmap());
561                sct.setEL(CATALOG,StringUtil.emptyIfNull(getCatalog()));
562                sct.setEL(CFC_LOCATION,arrLocs);
563                sct.setEL(IS_DEFAULT_CFC_LOCATION,isDefaultCfcLocation());
564                sct.setEL(DB_CREATE,dbCreateAsString(getDbCreate()));
565                sct.setEL(DIALECT,StringUtil.emptyIfNull(getDialect()));
566                sct.setEL(EVENT_HANDLING,eventHandling());
567                sct.setEL(EVENT_HANDLER,eventHandler());
568                sct.setEL(NAMING_STRATEGY,namingStrategy());
569                sct.setEL(FLUSH_AT_REQUEST_END,flushAtRequestEnd());
570                sct.setEL(LOG_SQL,logSQL());
571                sct.setEL(SAVE_MAPPING,saveMapping());
572                sct.setEL(SCHEMA,StringUtil.emptyIfNull(getSchema()));
573                sct.setEL(SECONDARY_CACHE_ENABLED,secondaryCacheEnabled());
574                sct.setEL(SQL_SCRIPT,StringUtil.toStringEmptyIfNull(getSqlScript()));
575                sct.setEL(USE_DB_FOR_MAPPING,useDBForMapping());
576                sct.setEL(CACHE_CONFIG,getAbsolutePath(getCacheConfig()));
577                sct.setEL(CACHE_PROVIDER,StringUtil.emptyIfNull(getCacheProvider()));
578                sct.setEL(ORM_CONFIG,getAbsolutePath(getOrmConfig()));
579                
580                
581                
582                return sct;
583        }
584
585
586        private static String getAbsolutePath(Resource res) {
587                if(res==null )return "";
588                return res.getAbsolutePath();
589        }
590
591
592
593
594
595
596        public static int dbCreateAsInt(String dbCreate) {
597                if(dbCreate==null)dbCreate="";
598                else dbCreate=dbCreate.trim().toLowerCase();
599                
600                if("update".equals(dbCreate))return DBCREATE_UPDATE;
601                if("dropcreate".equals(dbCreate))return DBCREATE_DROP_CREATE;
602                if("drop-create".equals(dbCreate))return DBCREATE_DROP_CREATE;
603                
604                return DBCREATE_NONE;
605        }
606        
607
608
609
610        public static String dbCreateAsString(int dbCreate) {
611                
612                switch(dbCreate){
613                case DBCREATE_DROP_CREATE: return "dropcreate";
614                case DBCREATE_UPDATE: return "update";
615                }
616                
617                return "none";
618        }
619
620
621
622
623
624
625        
626
627        
628
629
630
631
632        
633        
634        
635}
636
637interface _Get {
638        public Object get(Collection.Key name,Object defaultValue);
639}
640
641class _GetStruct implements _Get {
642        
643        private Struct sct;
644        public _GetStruct(Struct sct){
645                this.sct=sct;
646        }
647        
648        public Object get(Collection.Key name,Object defaultValue){
649                return sct.get(name,defaultValue);
650        }
651        
652        public String toString(){
653                return "_GetStruct:"+sct.toString();
654        }
655}
656
657class _GetElement implements _Get {
658        
659        private Element el;
660        public _GetElement(Element el){
661                this.el=el;
662        }
663        public Object get(Collection.Key name,Object defaultValue){
664                String value=_get(name.getString());
665                if(value==null)value = _get(StringUtil.camelToHypenNotation(name.getString()));
666                if(value==null)value = _get(name.getLowerString());
667                if(value==null){
668                        NamedNodeMap map = el.getAttributes();
669                        int len=map.getLength();
670                        Attr attr;
671                        String n;
672                        for(int i=0;i<len;i++){
673                                attr=(Attr) map.item(i);
674                                n=attr.getName();
675                                n=StringUtil.replace(n, "-", "", false).toLowerCase();
676                                if(n.equalsIgnoreCase(name.getLowerString())) return attr.getValue();
677                        }
678                        
679                }
680                
681                if(value==null) return defaultValue;
682                return value;
683        }
684        
685        private String _get(String name) {
686                if(el.hasAttribute(name)) return el.getAttribute(name);
687                return null;
688        }
689}
690