001/* 
002 * JKNIV, whinstone one contract to access your database.
003 * 
004 * Copyright (C) 2017, the original author or authors.
005 *
006 * This library is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 2.1 of the License.
010 * 
011 * This library is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 * Lesser General Public License for more details.
015 * 
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with this library; if not, write to the Free Software Foundation, Inc., 
018 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
019 */
020package net.sf.jkniv.whinstone.commands;
021
022import java.lang.reflect.Method;
023import java.sql.Statement;
024import java.util.HashMap;
025import java.util.Map;
026import java.util.Set;
027
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030
031import net.sf.jkniv.asserts.Assertable;
032import net.sf.jkniv.asserts.AssertsFactory;
033import net.sf.jkniv.exception.HandleableException;
034import net.sf.jkniv.reflect.beans.ObjectProxy;
035import net.sf.jkniv.reflect.beans.ObjectProxyFactory;
036import net.sf.jkniv.sqlegance.Sql;
037import net.sf.jkniv.sqlegance.SqlType;
038import net.sf.jkniv.sqlegance.dialect.SqlFeatureSupport;
039import net.sf.jkniv.whinstone.Queryable;
040import net.sf.jkniv.whinstone.ResultRow;
041
042/**
043 * 
044 * @author Alisson Gomes
045 * @since 0.6.0
046 */
047public abstract class DefaultCommandHandler implements CommandHandler
048{
049    final static Logger                              LOG               = LoggerFactory
050            .getLogger(DefaultCommandHandler.class);
051    static final Assertable                          NOT_NULL          = AssertsFactory.getNotNull();
052    private final static Map<String, ObjectCallback> OBJECTS_CALLBACKS = new HashMap<String, ObjectCallback>();
053    CommandAdapter                                   cmdAdapter;
054    Command                                          command;
055    ObjectProxy<?>                                   proxyParams;
056    protected Queryable                              queryable;
057    protected Sql                                    sql;
058    //protected RepositoryConfig                       config;
059    protected ResultRow<?, ?>                        overloadResultRow;
060    protected HandleableException                    handleableException;
061    
062    public DefaultCommandHandler(CommandAdapter cmdAdapter)
063    {
064        this.cmdAdapter = cmdAdapter;
065    }
066    
067    @Override
068    public CommandHandler with(ResultRow<?, ?> overloadResultRow)
069    {
070        this.overloadResultRow = overloadResultRow;
071        return this;
072    }
073    
074    @Override
075    public CommandHandler with(Queryable queryable)
076    {
077        this.queryable = queryable;
078        if (queryable.getParams() != null)
079        {
080            this.proxyParams = ObjectProxyFactory.of(queryable.getParams());
081            CallbackProcessor processor = new CallbackProcessor(this.proxyParams);
082            if (!OBJECTS_CALLBACKS.containsKey(proxyParams.getTargetClass().getName()))
083            {
084                ObjectCallback objectCallback = processor.loadCallbackEvents();
085                OBJECTS_CALLBACKS.put(proxyParams.getTargetClass().getName(), objectCallback);
086            }
087        }
088        //loadCallbackEvents();
089        return this;
090    }
091    
092    @Override
093    public CommandHandler with(Sql sql)
094    {
095        this.sql = sql;
096        return this;
097    }
098    
099    public CommandHandler with(HandleableException handlerException)
100    {
101        this.handleableException = handlerException;
102        return this;
103    }
104    
105    @Override
106    public <T> T run()
107    {
108        NOT_NULL.verify(this.cmdAdapter, this.queryable, this.sql);
109        T t = null;
110        Number rows = 0;
111        if (LOG.isTraceEnabled())
112            LOG.trace("Executing [{}] as {} command", queryable, sql.getSqlType());
113        try
114        {
115            // JPA with a entity doesn't assert validate
116            // how can fix/improve this?
117            if(!queryable.isBoundSql())
118                queryable.bind(sql);
119            try
120            {
121                preCallback();
122                sql.getValidateType().assertValidate(queryable.getParams());
123                this.command = asCommand();
124                t = this.command.execute();
125                if (t instanceof Number)
126                    rows = (Number) t;
127                if (queryable.getDynamicSql().getSqlDialect().supportsFeature(SqlFeatureSupport.PAGING_ROUNDTRIP))
128                    queryable.setTotal(rows.longValue());
129                else
130                    queryable.setTotal(Statement.SUCCESS_NO_INFO);
131                if (LOG.isDebugEnabled())
132                    LOG.debug("{} records was affected by {} [{}] query", rows, sql.getSqlType(), queryable.getName());
133                postCallback();
134            }
135            catch (Exception e)
136            {
137                queryable.setTotal(Statement.EXECUTE_FAILED);
138                postException();
139                handleableException.handle(e);
140            }
141        }
142        finally
143        {
144            this.cmdAdapter.close();
145        }
146        return t;
147    }
148    
149    protected CommandAdapter getCommandAdapter()
150    {
151        return this.cmdAdapter;
152    }
153    
154    @Override
155    public CommandHandler preCallback()
156    {
157        if (proxyParams != null)
158        {
159            ObjectCallback objectCallback = OBJECTS_CALLBACKS.get(proxyParams.getTargetClass().getName());
160            if (objectCallback != null)
161            {
162                Set<Method> methods = objectCallback.getPreMethods(sql.getSqlType());
163                for (Method m : methods)
164                    proxyParams.invoke(m);
165            }
166        }
167        return this;
168    }
169    
170    @Override
171    public CommandHandler postCallback()
172    {
173        if (proxyParams != null)
174        {
175            ObjectCallback objectCallback = OBJECTS_CALLBACKS.get(proxyParams.getTargetClass().getName());
176            if (objectCallback != null)
177            {
178                Set<Method> methods = objectCallback.getPostMethods(sql.getSqlType());
179                for (Method m : methods)
180                    proxyParams.invoke(m);
181            }
182        }
183        return this;
184    }
185    
186    @Override
187    public CommandHandler postCommit()// TODO Callback implmements POST_COMMIT
188    {
189        //        if (proxyParams != null)
190        //        {
191        //            CallbackMethods preCallbackMethods = CacheCallback.get(proxyParams.getTargetClass(), sql.getSqlType());
192        //            for (Method m : preCallbackMethods.getCallbacks())
193        //                proxyParams.invoke(m);
194        //        }
195        return this;
196    }
197    
198    @Override
199    public CommandHandler postException()// TODO Callback implmements POST_EXCEPTION
200    {
201        //        if (proxyParams != null)
202        //        {
203        //            CallbackMethods preCallbackMethods = CacheCallback.get(proxyParams.getTargetClass(), sql.getSqlType());
204        //            for (Method m : preCallbackMethods.getCallbacks())
205        //                proxyParams.invoke(m);
206        //        }
207        return this;
208    }
209    
210    
211    @Override
212    public CommandHandler checkSqlType(SqlType expected)
213    {
214        if (sql == null)
215            throw new IllegalArgumentException("Null Sql reference wasn't expected");
216        
217        if (sql.getSqlType() != expected)
218            throw new IllegalArgumentException("Cannot execute sql [" + sql.getName() + "] as " + sql.getSqlType()
219                    + ", " + expected + " was expect");
220        
221        return this;
222    }
223    
224}