001    package railo.runtime.orm.hibernate;
002    
003    import java.io.BufferedReader;
004    import java.io.IOException;
005    import java.sql.SQLException;
006    import java.sql.Statement;
007    import java.util.ArrayList;
008    import java.util.HashSet;
009    import java.util.Iterator;
010    import java.util.List;
011    import java.util.Map;
012    import java.util.Map.Entry;
013    import java.util.Set;
014    
015    import org.hibernate.MappingException;
016    import org.hibernate.cfg.Configuration;
017    import org.hibernate.tool.hbm2ddl.SchemaExport;
018    import org.hibernate.tool.hbm2ddl.SchemaUpdate;
019    import org.w3c.dom.Document;
020    
021    import railo.commons.io.IOUtil;
022    import railo.commons.io.res.Resource;
023    import railo.commons.io.res.filter.ExtensionResourceFilter;
024    import railo.commons.io.res.util.ResourceUtil;
025    import railo.commons.lang.StringUtil;
026    import railo.commons.sql.SQLUtil;
027    import railo.runtime.Component;
028    import railo.runtime.InterfacePage;
029    import railo.runtime.Mapping;
030    import railo.runtime.MappingImpl;
031    import railo.runtime.Page;
032    import railo.runtime.PageContext;
033    import railo.runtime.PageSource;
034    import railo.runtime.component.ComponentLoader;
035    import railo.runtime.config.ConfigImpl;
036    import railo.runtime.config.Constants;
037    import railo.runtime.db.DataSource;
038    import railo.runtime.db.DatasourceConnection;
039    import railo.runtime.exp.PageException;
040    import railo.runtime.listener.ApplicationContext;
041    import railo.runtime.op.Caster;
042    import railo.runtime.orm.ORMConfiguration;
043    import railo.runtime.orm.ORMException;
044    import railo.runtime.orm.ORMUtil;
045    import railo.runtime.text.xml.XMLUtil;
046    import railo.runtime.type.cfc.ComponentAccess;
047    import railo.runtime.type.util.ArrayUtil;
048    
049    public class HibernateSessionFactory {
050            
051            
052            public static final String HIBERNATE_3_PUBLIC_ID = "-//Hibernate/Hibernate Mapping DTD 3.0//EN";
053            public static final String HIBERNATE_3_SYSTEM_ID = "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd";
054            public static final String HIBERNATE_3_ENCODING = "UTF-8";
055            public static final String HIBERNATE_3_DOCTYPE_DEFINITION = "<!DOCTYPE hibernate-mapping PUBLIC \""+HIBERNATE_3_PUBLIC_ID+"\" \""+HIBERNATE_3_SYSTEM_ID+"\">";
056            
057    
058            public static Configuration createConfiguration(HibernateORMEngine engine,String mappings, DatasourceConnection dc, ORMConfiguration ormConf) throws SQLException, IOException, PageException {
059                    /*
060                     autogenmap
061                     cacheconfig
062                     cacheprovider
063                     cfclocation
064                     datasource
065                     dbcreate
066                     eventHandling
067                     flushatrequestend
068                     ormconfig
069                     sqlscript
070                     useDBForMapping
071                     */
072                    
073                    // dialect
074                    DataSource ds = dc.getDatasource();
075                    String dialect=null;
076                    try     {
077                            if (Class.forName(ormConf.getDialect()) != null) {
078                                    dialect = ormConf.getDialect();
079                            }
080                    }
081                    catch (Exception e) {
082                            // MZ: The dialect value could not be bound to a classname or instantiation causes an exception - ignore and use the default dialect entries
083                    }
084                    if (dialect == null) {
085                            dialect = Dialect.getDialect(ormConf.getDialect());
086                            if(StringUtil.isEmpty(dialect)) dialect=Dialect.getDialect(ds);
087                    }
088                    if(StringUtil.isEmpty(dialect))
089                            throw new ORMException(engine,"A valid dialect definition inside the "+Constants.APP_CFC+"/"+Constants.CFAPP_NAME+" is missing. The dialect cannot be determinated automatically");
090                    
091                    // Cache Provider
092                    String cacheProvider = ormConf.getCacheProvider();
093                    if(StringUtil.isEmpty(cacheProvider) || "EHCache".equalsIgnoreCase(cacheProvider))                      
094                                                                                                                                    cacheProvider="org.hibernate.cache.EhCacheProvider";
095                    else if("JBossCache".equalsIgnoreCase(cacheProvider))   cacheProvider="org.hibernate.cache.TreeCacheProvider";
096                    else if("HashTable".equalsIgnoreCase(cacheProvider))    cacheProvider="org.hibernate.cache.HashtableCacheProvider";
097                    else if("SwarmCache".equalsIgnoreCase(cacheProvider))   cacheProvider="org.hibernate.cache.SwarmCacheProvider";
098                    else if("OSCache".equalsIgnoreCase(cacheProvider))              cacheProvider="org.hibernate.cache.OSCacheProvider";
099            
100                    Resource cacheConfig = ormConf.getCacheConfig();
101                    Configuration configuration = new Configuration();
102                    
103                    // ormConfig
104                    Resource conf = ormConf.getOrmConfig();
105                    if(conf!=null){
106                            try {
107                                    Document doc = XMLUtil.parse(XMLUtil.toInputSource(conf), null, false);
108                                    configuration.configure(doc);
109                            } 
110                            catch (Throwable t) {
111                                    ORMUtil.printError(t, engine);
112                                    
113                            }
114                    }
115                    
116                    try{
117                            configuration.addXML(mappings);
118                    }
119                    catch(MappingException me){
120                            throw HibernateException.toPageException(engine, me);
121                    }
122                    
123                    configuration
124            
125            // Database connection settings
126            .setProperty("hibernate.connection.driver_class", ds.getClazz().getName())
127            .setProperty("hibernate.connection.url", ds.getDsnTranslated())
128            .setProperty("hibernate.connection.username", ds.getUsername())
129            .setProperty("hibernate.connection.password", ds.getPassword())
130            //.setProperty("hibernate.connection.release_mode", "after_transaction")
131            .setProperty("hibernate.transaction.flush_before_completion", "false")
132            .setProperty("hibernate.transaction.auto_close_session", "false")
133            
134            // JDBC connection pool (use the built-in)
135            //.setProperty("hibernate.connection.pool_size", "2")//MUST
136            
137            
138            // SQL dialect
139            .setProperty("hibernate.dialect", dialect)
140            // Enable Hibernate's current session context
141            .setProperty("hibernate.current_session_context_class", "thread")
142            
143            // Echo all executed SQL to stdout
144            .setProperty("hibernate.show_sql", Caster.toString(ormConf.logSQL()))
145            .setProperty("hibernate.format_sql", Caster.toString(ormConf.logSQL()))
146            // Specifies whether secondary caching should be enabled
147            .setProperty("hibernate.cache.use_second_level_cache", Caster.toString(ormConf.secondaryCacheEnabled()))
148                    // Drop and re-create the database schema on startup
149            .setProperty("hibernate.exposeTransactionAwareSessionFactory", "false")
150                    //.setProperty("hibernate.hbm2ddl.auto", "create")
151                    .setProperty("hibernate.default_entity_mode", "dynamic-map");
152                    
153                    if(!StringUtil.isEmpty(ormConf.getCatalog()))
154                            configuration.setProperty("hibernate.default_catalog", ormConf.getCatalog());
155                    if(!StringUtil.isEmpty(ormConf.getSchema()))
156                            configuration.setProperty("hibernate.default_schema",ormConf.getSchema());
157                    
158                    if(ormConf.secondaryCacheEnabled()){
159                            if(cacheConfig!=null && cacheConfig.isFile())
160                                    configuration.setProperty("hibernate.cache.provider_configuration_file_resource_path",cacheConfig.getAbsolutePath());
161                            configuration.setProperty("hibernate.cache.provider_class", cacheProvider);
162                    
163                    configuration.setProperty("hibernate.cache.use_query_cache", "true");
164                    }
165                    
166                    /*
167                    <!ELEMENT tuplizer EMPTY> 
168                <!ATTLIST tuplizer entity-mode (pojo|dom4j|dynamic-map) #IMPLIED>   <!-- entity mode for which tuplizer is in effect --> 
169                <!ATTLIST tuplizer class CDATA #REQUIRED>                           <!-- the tuplizer class to use --> 
170                    */
171            
172                    schemaExport(engine,configuration,ormConf,dc);
173                    
174                    return configuration;
175            }
176    
177            private static void schemaExport(HibernateORMEngine engine,Configuration configuration, ORMConfiguration ormConf,DatasourceConnection dc) throws PageException,ORMException, SQLException, IOException {
178                    if(ORMConfiguration.DBCREATE_NONE==ormConf.getDbCreate()) {
179                            return;
180                    }
181                    else if(ORMConfiguration.DBCREATE_DROP_CREATE==ormConf.getDbCreate()) {
182                            SchemaExport export = new SchemaExport(configuration);
183                            export.setHaltOnError(true);
184                        
185                            export.execute(false,true,false,false);
186                printError(engine,export.getExceptions(),false);
187                executeSQLScript(ormConf,dc);
188                    }
189                    else if(ORMConfiguration.DBCREATE_UPDATE==ormConf.getDbCreate()) {
190                            SchemaUpdate update = new SchemaUpdate(configuration);
191                update.setHaltOnError(true);
192                update.execute(false, true);
193                printError(engine,update.getExceptions(),false);
194            }
195            }
196    
197            private static void printError(HibernateORMEngine engine, List<Exception> exceptions,boolean throwException) throws PageException {
198                    if(ArrayUtil.isEmpty(exceptions)) return;
199                    Iterator<Exception> it = exceptions.iterator();
200            if(!throwException || exceptions.size()>1){
201                            while(it.hasNext()) {
202                            ORMUtil.printError(it.next(), engine);
203                    } 
204            }
205            if(!throwException) return;
206            
207            it = exceptions.iterator();
208            while(it.hasNext()) {
209                    throw HibernateException.toPageException(engine, it.next());
210            } 
211            }
212    
213            private static void executeSQLScript(ORMConfiguration ormConf,DatasourceConnection dc) throws SQLException, IOException {
214            Resource sqlScript = ormConf.getSqlScript();
215            if(sqlScript!=null && sqlScript.isFile()) {
216                BufferedReader br = IOUtil.toBufferedReader(IOUtil.getReader(sqlScript,null));
217                String line;
218                StringBuilder sql=new StringBuilder();
219                String str;
220                Statement stat = dc.getConnection().createStatement();
221                    try{
222                            while((line=br.readLine())!=null){
223                            line=line.trim();
224                            if(line.startsWith("//") || line.startsWith("--")) continue;
225                            if(line.endsWith(";")){
226                                    sql.append(line.substring(0,line.length()-1));
227                                    str=sql.toString().trim();
228                                    if(str.length()>0)stat.execute(str);
229                                    sql=new StringBuilder();
230                            }
231                            else {
232                                    sql.append(line).append(" ");
233                            }       
234                        }
235                            str=sql.toString().trim();
236                            if(str.length()>0){
237                                    stat.execute(str);
238                        }
239                    }
240                    finally {
241                            SQLUtil.closeEL(stat);
242                    }
243            }
244        }
245    
246    
247            public static String createMappings(HibernateORMEngine engine, Map<String, CFCInfo> cfcs) {
248                    
249                    Set<String> done=new HashSet<String>();
250                    StringBuffer mappings=new StringBuffer();
251                    mappings.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
252                    mappings.append(HIBERNATE_3_DOCTYPE_DEFINITION+"\n");
253                    mappings.append("<hibernate-mapping>\n");
254                    Iterator<Entry<String, CFCInfo>> it = cfcs.entrySet().iterator();
255                    Entry<String, CFCInfo> entry;
256                    while(it.hasNext()){
257                            entry = it.next();
258                            createMappings(engine,cfcs,entry.getKey(),entry.getValue(),done,mappings);
259                            
260                    }
261                    mappings.append("</hibernate-mapping>");
262                    return mappings.toString();
263            }
264    
265            private static void createMappings(HibernateORMEngine engine, Map<String, CFCInfo> cfcs,String key, CFCInfo value,Set<String> done,StringBuffer mappings) {
266                    if(done.contains(key)) return;
267                    CFCInfo v;
268                    String ext = value.getCFC().getExtends();
269                    if(!StringUtil.isEmpty(ext)){
270                            try {
271                                    Component base = engine.getEntityByCFCName(ext, false);
272                                    ext=HibernateCaster.getEntityName(base);
273                            } catch (Throwable t) {}
274                            
275                            
276                            ext=HibernateORMEngine.id(railo.runtime.type.util.ListUtil.last(ext, '.').trim());
277                            if(!done.contains(ext)) {
278                                    v = cfcs.get(ext);
279                                    if(v!=null)createMappings(engine,cfcs, ext, v, done, mappings);
280                            }
281                    }
282                    
283                    mappings.append(value.getXML());
284                    done.add(key);
285            }
286    
287            public static List<Component> loadComponents(PageContext pc,HibernateORMEngine engine, ORMConfiguration ormConf) throws PageException {
288                    ExtensionResourceFilter filter = new ExtensionResourceFilter(pc.getConfig().getCFCExtension(),true);
289                    List<Component> components=new ArrayList<Component>();
290                    loadComponents(pc,engine,components,ormConf.getCfcLocations(),filter,ormConf);
291                    return components;
292            }
293            
294            private static void loadComponents(PageContext pc, HibernateORMEngine engine,List<Component> components,Resource[] reses,ExtensionResourceFilter filter,ORMConfiguration ormConf) throws PageException {
295                    Mapping[] mappings = createMappings(pc, reses);
296                    ApplicationContext ac=pc.getApplicationContext();
297                    Mapping[] existing = ac.getComponentMappings();
298                    if(existing==null) existing=new Mapping[0];
299                    try{
300                            Mapping[] tmp = new Mapping[existing.length+1];
301                            for(int i=1;i<tmp.length;i++){
302                                    tmp[i]=existing[i-1];
303                            }
304                            ac.setComponentMappings(tmp);
305                            for(int i=0;i<reses.length;i++){
306                                    if(reses[i]!=null && reses[i].isDirectory()){
307                                            tmp[0] = mappings[i];
308                                            ac.setComponentMappings(tmp);
309                                            loadComponents(pc,engine,mappings[i],components,reses[i], filter,ormConf);
310                                    }
311                            }
312                    }
313                    finally {
314                            ac.setComponentMappings(existing);
315                    }
316            }
317            
318            private static void loadComponents(PageContext pc, HibernateORMEngine engine,Mapping cfclocation,List<Component> components,Resource res,ExtensionResourceFilter filter,ORMConfiguration ormConf) throws PageException {
319                    if(res==null) return;
320    
321                    if(res.isDirectory()){
322                            Resource[] children = res.listResources(filter);
323                            
324                            // first load all files
325                            for(int i=0;i<children.length;i++){
326                                    if(children[i].isFile())loadComponents(pc,engine,cfclocation,components,children[i], filter,ormConf);
327                            }
328                            
329                            // and then invoke subfiles
330                            for(int i=0;i<children.length;i++){
331                                    if(children[i].isDirectory())loadComponents(pc,engine,cfclocation,components,children[i], filter,ormConf);
332                            }
333                    }
334                    else if(res.isFile()){
335                            if(!res.getName().equalsIgnoreCase(Constants.APP_CFC))  {
336                                    try {
337                                            
338                                            // MUST still a bad solution
339                                            PageSource ps = pc.toPageSource(res,null);
340                                            if(ps==null || ps.getComponentName().indexOf("..")!=-1) {
341                                                    PageSource ps2=null;
342                                                    Resource root = cfclocation.getPhysical();
343                                    String path = ResourceUtil.getPathToChild(res, root);
344                                    if(!StringUtil.isEmpty(path,true)) {
345                                            ps2=cfclocation.getPageSource(path);
346                                    }
347                                    if(ps2!=null)ps=ps2;
348                                            }
349                                            
350                                            
351                                            //Page p = ps.loadPage(pc.getConfig());
352                                            String name=res.getName();
353                                            name=name.substring(0,name.length()-4);
354                                            Page p = ComponentLoader.loadPage(pc, ps,true);
355                                            if(!(p instanceof InterfacePage)){
356                                                    ComponentAccess cfc = ComponentLoader.loadComponent(pc, p, ps, name, true,true);
357                                                    if(cfc.isPersistent()){
358                                                            components.add(cfc);
359                                                    }
360                                            }
361                                    } 
362                                    catch (PageException e) {
363                                            if(!ormConf.skipCFCWithError())throw e;
364                                            //e.printStackTrace();
365                                    }
366                            }
367                    }
368            }
369    
370            
371            public static Mapping[] createMappings(PageContext pc,Resource[] resources) {
372                            
373                            MappingImpl[] mappings=new MappingImpl[resources.length];
374                            ConfigImpl config=(ConfigImpl) pc.getConfig();
375                            for(int i=0;i<mappings.length;i++) {
376                                    mappings[i]=new MappingImpl(config,
377                                                    "/",
378                                                    resources[i].getAbsolutePath(),
379                                                    null,ConfigImpl.INSPECT_UNDEFINED,true,false,false,false,true,true,null
380                                                    );
381                            }
382                            return mappings;
383                    }
384    }