001package gu.sql2java;
002
003import static com.google.common.base.Preconditions.checkArgument;
004import static com.google.common.base.Preconditions.checkNotNull;
005import static gu.sql2java.SimpleLog.log;
006import static gu.sql2java.SimpleLog.logString;
007
008import java.lang.reflect.InvocationHandler;
009import java.lang.reflect.Proxy;
010import java.util.Collections;
011import java.util.EnumMap;
012import java.util.HashMap;
013import java.util.Map;
014import java.util.Map.Entry;
015import java.util.NoSuchElementException;
016import java.util.Properties;
017import java.util.concurrent.TimeUnit;
018
019import com.google.common.base.Function;
020import com.google.common.base.Predicate;
021import com.google.common.collect.ImmutableMap;
022import com.google.common.collect.Iterables;
023import com.google.common.collect.Maps;
024import gu.sql2java.Constant.JdbcProperty;
025import gu.sql2java.Constant.UpdateStrategy;
026
027/**
028 * 数据库操作实例({@link TableManager})管理类
029 * @author guyadong
030 *
031 */
032public class Managers {
033        /**
034         * 设置是否输出调试信息标志
035         */
036        private static boolean debug = false;
037        private Managers() {
038        }
039
040        private static final ImmutableMap<String, TableManager<? extends BaseBean>> 
041        tableManagerInstances = loadTableManager();
042        private static final  ImmutableMap<Class<?>, TableManager<? extends BaseBean>> 
043        tableManagerTypeMaps = asTypeMap(tableManagerInstances);
044        private static final  ImmutableMap<Class<?>, TableManager<? extends BaseBean>> 
045        tableManagerBeanTypeMaps = asBeanTypeMap(tableManagerInstances);
046        private static final  Map<Class<?>, TableManager<? extends BaseBean>> cacheManagers = Maps.newHashMap();
047        private static final  Map<Class<?>, TableManager<? extends BaseBean>> cacheBeanTypeManagers = Maps.newHashMap();
048        private static final  Map<String, TableManager<? extends BaseBean>> cacheNameManagers = Maps.newHashMap();
049
050        /**
051         * @return 返回所有数据库操作实例(非cache)
052         */
053        public static ImmutableMap<String, TableManager<? extends BaseBean>> getTableManagers() {
054                return tableManagerInstances;
055        }
056
057        /**
058         * SPI(Service Provider Interface)机制加载 {@link TableManager}所有实例
059         * @return 表名和 {@link TableManager}实例的映射对象 
060         */
061        private static ImmutableMap<String, TableManager<? extends BaseBean>> loadTableManager() {              
062                ImmutableMap.Builder<String, TableManager<? extends BaseBean>> builder = ImmutableMap.builder();
063                for(RowMetaData data:RowMetaData.tableMetadata.values()){
064                        TableManager<?> manager = TableManagerDecorator.makeInterfaceInstance(new BaseTableManager<>(data.tablename));
065                        builder.put(data.tablename,     manager);
066                        if(debug){
067                                log("TableManagerDecorator instance create for {} ",data.tablename);
068                        }
069                }
070                return builder.build();
071        }
072
073        private static final ImmutableMap<Class<?>, TableManager<? extends BaseBean>> asTypeMap(Map<String, TableManager<? extends BaseBean>>input){
074                return Maps.uniqueIndex(input.values(), new Function<TableManager<? extends BaseBean>,Class<?>>(){
075                        @Override
076                        public Class<?> apply(TableManager<? extends BaseBean> input) {
077                                for(Class<?> clazz:input.getClass().getInterfaces()){
078                                        for(Class<?> c:clazz.getInterfaces()){
079                                                if(c.equals(TableManager.class)){
080                                                        return clazz;
081                                                }
082                                        }
083                                }
084                                throw new IllegalStateException(logString("NOT FOUND immplements interface class for %s ", input.getClass()));
085                        }});
086        }
087
088        private static final ImmutableMap<Class<?>, TableManager<? extends BaseBean>> asBeanTypeMap(Map<String, TableManager<? extends BaseBean>>input){
089                return Maps.uniqueIndex(input.values(), new Function<TableManager<? extends BaseBean>,Class<?>>(){
090        
091                        @Override
092                        public Class<?> apply(TableManager<? extends BaseBean> input) {
093                                if(input instanceof BaseTableManager){
094                                        return ((BaseTableManager<?>)input).metaData.beanType;
095                                }else if(Proxy.isProxyClass(input.getClass())){
096                                        InvocationHandler handler = Proxy.getInvocationHandler(input);
097                                        checkArgument(handler instanceof TableManagerDecorator,"UNKNOW HANDLER %s",handler.getClass() );
098                                        return ((TableManagerDecorator<?>)handler).metaData.beanType;
099                                }
100                                throw new IllegalArgumentException(logString("UNKNOW SUPPORTED TableManager instance %s",input.getClass()));
101                        }});
102        }
103
104        /** 
105         * 根据表操作接口类型返回数据库操作实例(非cache)
106         * @param interfaceClass  接口类型
107         * @return {@link TableManager}实例,找不到则抛出异常
108         */
109        @SuppressWarnings("unchecked")
110        public static final <M extends TableManager<?>>M 
111        getTableManager(Class<M>interfaceClass) {
112                TableManager<? extends BaseBean> manager = tableManagerTypeMaps.get(interfaceClass);
113                return checkNotNull((M) manager,"INVALID manager type %s",interfaceClass);
114        }
115
116        /**
117         * 根据表记录类型返回数据库操作实例(非cache)
118         * @param beanType java bean type
119         * @return {@link TableManager}实例,找不到则抛出异常
120         */
121        @SuppressWarnings("unchecked")
122        public static final <T extends BaseBean,M extends TableManager<T>>M 
123        getTableManagerByBeanType(Class<T>beanType) {
124                TableManager<? extends BaseBean> manager = tableManagerBeanTypeMaps.get(beanType);
125                return checkNotNull((M) manager,"INVALID bean type %s",beanType);
126        }
127
128        /**
129         * 根据表名返回数据库操作实例(非cache)
130         * @param tablename table name
131         * @return {@link TableManager}实例,找不到则抛出异常
132         */
133        @SuppressWarnings("unchecked")
134        public static final <M extends TableManager<?>>M 
135        getTableManager(String tablename) {
136                TableManager<? extends BaseBean> manager = tableManagerInstances.get(tablename);
137                return checkNotNull((M) manager,"INVALID tablename %s",tablename);
138        }
139
140        /**
141         * 根据表名返回数据库操作实例(非cache)
142         * @param tablename table name
143         * @return {@link BaseTableManager}实例,找不到则抛出异常
144         */
145        @SuppressWarnings("unchecked")
146        public static final <M extends BaseTableManager<?>>M 
147        getBaseTableManager(String tablename) {
148                return (M) baseManagerOf(getTableManager(tablename));
149        }
150
151    /**
152     * 注册cache manager<br>
153     * @param tablename table name
154     * @param updateStrategy cache update strategy,{@link Constant#DEFAULT_STRATEGY} be used if {@code null}
155     * @param maximumSize maximum capacity of cache ,{@link Constant#DEFAULT_CACHE_MAXIMUMSIZE } be used if {@code null} or <=0,see also {@link CacheBuilder#maximumSize(long)}
156     * @param duration cache data expired time,{@link Constant#DEFAULT_DURATION} be used if {@code null} or <=0,see also {@link CacheBuilder#expireAfterWrite(long, TimeUnit)}
157     * @param unit time unit for {@code duration},{@link Constant#DEFAULT_TIME_UNIT} be used if {@code null},see also {@link CacheBuilder#expireAfterWrite(long, TimeUnit)}
158     */
159        public static synchronized final <I extends TableManager<?>>
160        void registerCacheManager(
161                        String tablename,
162                        UpdateStrategy updateStrategy,
163                        long maximumSize, 
164                        long duration, 
165                        TimeUnit unit){
166                TableManager<?> cacheManager = CacheManager.makeCacheInstance(tablename, updateStrategy, maximumSize, duration, unit);
167                BaseTableManager<?> manager = baseManagerOf(cacheManager);
168                cacheManagers.put(manager.metaData.managerInterfaceClass, cacheManager);
169                cacheNameManagers.put(manager.metaData.tablename, cacheManager);
170                cacheBeanTypeManagers.put(manager.metaData.beanType, cacheManager);
171                if(debug){
172                        log("REGISTER CACHE MANAGER {}",cacheManager);
173                }
174        }
175
176        /**
177         * @return 返回所有支持缓存的数据库操作实例
178         * @see CacheManager
179         */
180        public static Map<Class<?>, TableManager<? extends BaseBean>> getCacheManagers() {
181                return Collections.unmodifiableMap(cacheManagers);
182        }
183
184        /**
185         * 根据目标类型返回对应的支持缓存的 {@link TableManager}实例
186         * @param interfaceClass 目标接口类型
187         * @return {@link TableManager}实例
188         * @throws NoSuchElementException 找不到时抛出异常
189         */
190        @SuppressWarnings("unchecked")
191        public static final <M extends TableManager<? extends BaseBean>>M 
192        getCacheManager(final Class<M>interfaceClass) throws NoSuchElementException {
193                checkArgument(interfaceClass != null,"targetType is null");
194                TableManager<? extends BaseBean> manager;
195                if(null != (manager = cacheManagers.get(interfaceClass))){
196                        return (M) manager;
197                }
198                return (M) Iterables.find(cacheManagers.values(), new Predicate<TableManager<?>>() {
199        
200                        @Override
201                        public boolean apply(TableManager<?> input) {
202                                return interfaceClass.isInstance(input);
203                        }
204                });
205        }
206
207        /**
208         * 根据表记录类型返回支持缓存的数据库操作实例
209         * @param beanType java bean type
210         * @return {@link TableManager}实例,找不到时抛出异常
211         */
212        @SuppressWarnings("unchecked")
213        public static final <B extends BaseBean> TableManager<B>  
214        getCacheManagerByBeanType(Class<?> beanType)  {
215                TableManager<? extends BaseBean> manager = cacheBeanTypeManagers.get(beanType);
216                return ( TableManager<B>) checkNotNull(manager,"INVALID bean type %s",beanType);
217        }
218
219        /**
220         * 根据表名返回支持缓存的数据库操作实例
221         * @param tablename
222         * @return {@link TableManager}实例,找不到时抛出异常
223         */
224        @SuppressWarnings("unchecked")
225        public static final <B extends BaseBean> TableManager<B> 
226        getCacheManager(String tablename)  {
227                TableManager<? extends BaseBean> manager = cacheNameManagers.get(tablename);
228                return (TableManager<B>) checkNotNull(manager,"INVALID table name %s",tablename);
229        }
230
231        /**
232         * 根据表记录类型返回数据库操作实例<br>
233         * 优先返回支持缓存的数据库操作实例(cache)
234         * @param interfaceClass 接口类
235         * @return {@link TableManager}实例,找不到时抛出异常
236         */
237        public static <M extends TableManager<? extends BaseBean>>M 
238        instanceOf(Class<M>interfaceClass) {
239            try {
240                return getCacheManager(interfaceClass);
241            } catch (Exception e) {
242                return getTableManager(interfaceClass);
243            } 
244        }
245
246        /**
247         * 根据表名返回数据库操作实例<br>
248         * 优先返回支持缓存的数据库操作实例(cache)
249         * @param tablename table name
250         * @return {@link TableManager}实例,找不到时抛出异常
251         */
252        public static <B extends BaseBean>TableManager<B> managerOf(String tablename) {
253            try {
254                return getCacheManager(tablename);
255            } catch (Exception e) {
256                return getTableManager(tablename);
257            } 
258        }
259
260        /**
261         * 将数据库操作实例转换对应的{@link BaseTableManager}实例<br>
262         * @param manager
263         * @return {@link BaseTableManager}实例,转换失败时抛出异常
264         */
265        @SuppressWarnings({ "unchecked", "rawtypes" })
266        static <B extends BaseBean>BaseTableManager<B> baseManagerOf(TableManager<B> manager){
267                checkArgument(manager != null,"manager is null");
268                if(manager instanceof BaseTableManager){
269                        return (BaseTableManager<B>) manager;
270                }else if(Proxy.isProxyClass(manager.getClass())){
271                        InvocationHandler handler = Proxy.getInvocationHandler(manager);
272                        checkArgument(handler instanceof TableManagerDecorator,"UNKNOW HANDLER TYPE %s",manager.getClass());
273                        return ((TableManagerDecorator)handler).delegate;
274                }else {
275                        throw new IllegalArgumentException(logString("UNKNOW TableManager instance type %s",manager.getClass()));
276                }
277        }
278
279        /**
280         * 根据表名返回对应的{@link BaseTableManager}实例
281         * 优先返回支持缓存的数据库操作实例(cache)
282         * @param tablename table name
283         * @return {@link BaseTableManager}实例,找不到时抛出异常
284         */
285        @SuppressWarnings("unchecked")
286        static <B extends BaseBean>BaseTableManager<B> baseManagerOf(String tablename) {
287            return baseManagerOf((TableManager<B>)managerOf(tablename)); 
288        }
289
290        /**
291         * 根据数据表名(驼峰命名格式)返回对应的{@link TableManager}实例<br>
292         * 优先返回支持缓存的数据库操作实例(cache)
293         * @param coreClass
294         * @return {@link TableManager}实例,找不到时抛出异常
295         */
296        public static  <B extends BaseBean>TableManager<B> managerOfCoreClass(String coreClass) {
297                String tablename = RowMetaData.getRowMetaDataByCoreClassName(coreClass).tablename;
298                try {
299                return getCacheManager(tablename);
300            } catch (Exception e) {
301                return getTableManager(tablename);
302            } 
303        }
304
305        /**
306         * 根据表记录类型返回数据库操作实例(非cache)<br>
307         * 优先返回支持缓存的数据库操作实例
308         * @param beanType java bean type
309         * @return {@link TableManager}实例,找不到时抛出异常
310         */
311        public static <B extends BaseBean>TableManager<B> managerOf(Class<B> beanType) {
312            try {
313                return getCacheManagerByBeanType(beanType);
314            } catch (Exception e) {
315                return getTableManagerByBeanType(beanType);
316            } 
317        }
318        public enum Module{
319                MANAGER,CACHE,DECORATOR,MANAGERS,BASETABLEMANAGER
320        }
321        /**
322         * set debug flag that determine if output log message,default : false
323         * @param debug flag for debug message output
324         * @param modules modules array to be set debug flag,all modules used if be null or empty
325         */
326        public static void setDebug(boolean debug,Module... modules){
327                if(modules == null || modules.length == 0){
328                        modules = Module.values();
329                } 
330                for(Module module:modules){
331                        if(null != module){
332                                switch (module) {
333                                case MANAGER:
334                                        Manager.getInstance().setDebug(debug);
335                                        break;
336                                case CACHE:
337                                        ColumnCache.setDebug(debug);
338                                        break;
339                                case DECORATOR:
340                                        TableManagerDecorator.setDebug(debug);
341                                        break;
342                                case MANAGERS:
343                                        Managers.debug = debug;
344                                        break;
345                                case BASETABLEMANAGER:
346                                        BaseTableManager.setDebug(debug);
347                                        break;
348                                default:
349                                        break;
350                                }
351                        }
352                }
353        }
354
355        /**
356         * inject properties to {@link Manager#databaseProperties}<br>
357         * be effected only while called before initializing singleton instance 
358         * @param properties
359         * @param prefix the prefix of properties,ignore if {@code null}
360         * @see JdbcProperty
361         */
362        public static final void injectProperties(Map<String,String> properties,String prefix){
363            if(null != properties){
364                EnumMap<JdbcProperty, String> enumMap = new EnumMap<JdbcProperty,String>(JdbcProperty.class);
365                JdbcProperty property;
366                for(Entry<String, String> entry:properties.entrySet()){
367                    if(null != (property = JdbcProperty.fromKey(entry.getKey(),prefix))){
368                        enumMap.put(property, entry.getValue());
369                    }
370                }
371                Manager.injectProperties(enumMap);
372            }
373        }
374        /**
375         * inject properties to {@link Manager#databaseProperties}<br>
376         * be effected only while called before initializing singleton instance 
377         * @param properties
378         * @see JdbcProperty
379         */
380        public static final void injectProperties(Map<String,String> properties){
381            injectProperties(properties,null);
382        }
383        /**
384         * inject properties to {@link Manager#databaseProperties}<br>
385         * be effected only while called before initializing singleton instance 
386         * @param properties
387         * @param prefix the prefix of properties,ignore if {@code null}
388         * @see JdbcProperty
389         */
390        public static final void injectProperties(Properties properties,String prefix){
391                HashMap<String, String> m = null;
392                if(null != properties){
393                        m =new HashMap<>();
394                        for(String name : properties.stringPropertyNames()){
395                                m.put(name, properties.getProperty(name));              
396                        }
397                }
398                injectProperties(m,prefix);
399        }
400        
401        /**
402         * @return singleton instance of {@link Manager} as {@link SqlRunner} instance
403         */
404        public static SqlRunner getSqlRunner(){
405                return Manager.getInstance();
406        }
407}