001    /*
002     * Copyright © 2008, 2009 Pedro Agulló Soliveres.
003     * 
004     * This file is part of DirectJNgine.
005     *
006     * DirectJNgine is free software: you can redistribute it and/or modify
007     * it under the terms of the GNU General Public License as published by
008     * the Free Software Foundation, either version 3 of the License.
009     *
010     * Commercial use is permitted to the extent that the code/component(s)
011     * do NOT become part of another Open Source or Commercially developed
012     * licensed development library or toolkit without explicit permission.
013     *
014     * DirectJNgine is distributed in the hope that it will be useful,
015     * but WITHOUT ANY WARRANTY; without even the implied warranty of
016     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017     * GNU General Public License for more details.
018     *
019     * You should have received a copy of the GNU General Public License
020     * along with DirectJNgine.  If not, see <http://www.gnu.org/licenses/>.
021     * 
022     * This software uses the ExtJs library (http://extjs.com), which is 
023     * distributed under the GPL v3 license (see http://extjs.com/license).
024     */
025    
026    package com.softwarementors.extjs.djn.router.processor;
027    
028    import java.io.PrintWriter;
029    import java.io.StringWriter;
030    import java.io.UnsupportedEncodingException;
031    import java.io.Writer;
032    import java.lang.reflect.InvocationTargetException;
033    import java.lang.reflect.Method;
034    import java.net.URLDecoder;
035    import java.util.HashMap;
036    import java.util.Map;
037    
038    import org.apache.log4j.Logger;
039    
040    import com.google.gson.Gson;
041    import com.google.gson.GsonBuilder;
042    import com.google.gson.JsonParseException;
043    import com.google.gson.JsonParser;
044    import com.softwarementors.extjs.djn.ClassUtils;
045    import com.softwarementors.extjs.djn.EncodingUtils;
046    import com.softwarementors.extjs.djn.StringUtils;
047    import com.softwarementors.extjs.djn.UnexpectedException;
048    import com.softwarementors.extjs.djn.api.RegisteredAction;
049    import com.softwarementors.extjs.djn.api.RegisteredMethod;
050    import com.softwarementors.extjs.djn.api.Registry;
051    import com.softwarementors.extjs.djn.config.GlobalConfiguration;
052    import com.softwarementors.extjs.djn.gson.GsonBuilderConfigurator;
053    import com.softwarementors.extjs.djn.gson.GsonBuilderConfiguratorException;
054    import com.softwarementors.extjs.djn.gson.JsonException;
055    import com.softwarementors.extjs.djn.router.dispatcher.Dispatcher;
056    import com.softwarementors.extjs.djn.router.dispatcher.MethodExecutionException;
057    import com.softwarementors.extjs.djn.router.processor.standard.JsonErrorResponse;
058    
059    public abstract class RequestProcessorBase {
060      
061      private static Logger logger = Logger.getLogger(RequestProcessorBase.class);
062      
063      private Dispatcher dispatcher;
064      private Registry registry;
065      private GlobalConfiguration globalConfiguration;
066      private Gson gson;
067      private JsonParser parser;
068      
069      protected GlobalConfiguration getGlobalConfiguration() {
070        return this.globalConfiguration;
071      }
072        
073      protected RequestProcessorBase( Registry registry, Dispatcher dispatcher, GlobalConfiguration globalConfiguration ) {
074        assert registry != null;
075        assert dispatcher != null;
076        assert globalConfiguration != null;
077          
078        this.dispatcher = dispatcher;
079        this.globalConfiguration = globalConfiguration;
080        this.registry = registry;
081      }
082        
083      protected Gson getGson() {
084        if( this.gson == null ) {
085          GsonBuilder builder = new GsonBuilder();
086          createGsonBuilderConfigurator().configure( builder, getGlobalConfiguration());
087          this.gson = builder.create();
088        }
089        
090        return this.gson;
091      }
092    
093      private GsonBuilderConfigurator createGsonBuilderConfigurator() {
094        Class<? extends GsonBuilderConfigurator> configuratorClass = getGlobalConfiguration().getGsonBuilderConfiguratorClass(); 
095        try {
096          return configuratorClass.newInstance();
097        }
098        catch (InstantiationException e) {
099          GsonBuilderConfiguratorException ex = GsonBuilderConfiguratorException.forUnableToInstantiateGsonBuilder(configuratorClass, e);
100          logger.fatal( ex.getMessage(), ex);
101          throw ex;
102        }
103        catch (IllegalAccessException e) {
104          GsonBuilderConfiguratorException ex = GsonBuilderConfiguratorException.forUnableToInstantiateGsonBuilder(configuratorClass, e);
105          logger.fatal( ex.getMessage(), ex);
106          throw ex;
107        }     
108      }
109      
110      protected JsonParser getJsonParser() {
111        if( this.parser == null ) {
112          this.parser = new JsonParser();
113        }
114        return this.parser;
115      }
116      
117      protected void appendIndividualResponseJsonString(ResponseBase response, StringBuilder result) {
118        assert response != null;
119        assert result != null;
120        
121        try {
122          getGson().toJson( response, result );
123        }
124        catch( JsonParseException ex ) {
125          JsonException.forFailedConversionFromResponseToJson(response, ex);
126        }
127      }    
128      
129      protected Registry getRegistry() {
130        return this.registry;
131      }
132      
133      protected Dispatcher getDispatcher() {
134        return this.dispatcher;
135      }
136        
137      protected boolean getDebug() {
138        return this.globalConfiguration.getDebug();
139      }
140      
141      // ************************************************************
142      // * Error handling support
143      // ************************************************************
144      // TODO: this is NOT something PollRequestProcessor will use. Handle somewhere else!
145      protected JsonErrorResponse createJsonServerErrorResponse(RequestBase request, Throwable t) {
146        assert request != null;
147        assert t != null;
148        
149        Throwable reportedException = getExceptionToReport(t);
150        String message = getExceptionMessage(reportedException);
151        String where = getExceptionWhere(reportedException);
152        
153        JsonErrorResponse response = new JsonErrorResponse( request.getTid(), request.getAction(), request.getMethod(), message, where);
154        return response;
155      }
156      
157      protected static Throwable getExceptionToReport(Throwable t) {
158        assert t != null;
159        
160        Throwable reportedException = t;
161        if( reportedException.getClass().equals( MethodExecutionException.class)) {
162          assert reportedException.getCause() != null;
163          reportedException = reportedException.getCause();
164          
165        }
166        if( reportedException.getClass().equals(InvocationTargetException.class )) {
167          assert reportedException.getCause() != null;
168          reportedException = reportedException.getCause();
169        }
170        return reportedException;
171      }
172    
173      protected static String getExceptionMessage(Throwable t) {
174        assert t!= null;
175    
176        String result = ClassUtils.getSimpleName( t.getClass() );
177        if( t.getMessage() != null ) {
178          result += ": " + t.getMessage();
179        }
180        return result;
181      }
182    
183      protected String getExceptionWhere(Throwable t) {
184        assert t != null;
185        
186        if( getDebug() ) {
187          Writer where = new StringWriter();
188          PrintWriter printWriter = new PrintWriter( where );
189          t.printStackTrace(printWriter);
190          return where.toString();
191        }
192        return "";
193      }
194    
195      // ************************************************************
196      // * JSon support
197      // ************************************************************
198      // ************************************************************
199      // * Dispatching support
200      // ************************************************************
201      // TODO: this is NOT something PollRequestProcessor will use. Handle somewhere else!
202      protected RegisteredMethod getMethod(String actionName, String methodName) {
203        assert !StringUtils.isEmpty(actionName);
204        assert !StringUtils.isEmpty(methodName);
205    
206        RegisteredAction action = getRegistry().getAction(actionName); 
207        if( action == null ) {
208          throw RequestException.forActionNotFound( actionName );
209        }
210    
211        RegisteredMethod method = action.getMethod(methodName);
212        if( method == null ) {
213          throw RequestException.forActionMethodNotFound( action.getName(), methodName );
214        }
215        return method;
216      }
217    
218      protected Object dispatch(Class<?> instanceClass, Method method, Object[] parameters) {
219        return getDispatcher().dispatch( instanceClass, method, parameters );
220      }
221      
222      // TODO: this is NOT something PollRequestProcessor will use. Handle somewhere else!
223      protected Object dispatch( String actionName, String methodName, Object[] parameters ) {
224        assert !StringUtils.isEmpty(actionName);  
225        assert !StringUtils.isEmpty(methodName);
226        assert parameters != null;
227       
228        
229        RegisteredMethod method = getMethod( actionName, methodName);
230        Object result = dispatch(method.getActionClass(), method.getMethod(), parameters);
231        return result;
232      }
233    
234      protected static Map<String, String> getDecodedRequestParameters(String requestString) {
235        assert requestString != null;
236        Map<String,String> result = new HashMap<String,String>();
237        if( !requestString.equals("")) {
238          String[] entries = requestString.split( "&");
239          for( String entry : entries ) {
240            String[] keyValue = entry.split("=");
241            assert keyValue.length >= 1 && keyValue.length <= 2;
242            String key = keyValue[0];
243            assert !StringUtils.isEmpty(key);
244    
245            String value = "";
246            if( keyValue.length == 2 )
247              value = keyValue[1];
248            try {
249              key = URLDecoder.decode( key, EncodingUtils.UTF8);
250              value = URLDecoder.decode( value, EncodingUtils.UTF8);
251            }
252            catch (UnsupportedEncodingException e) {
253              UnexpectedException.forExpectingUTF8UrlEncodingIsAlwaysSupportedByURLEncoder(e);
254            }
255            result.put( key, value);
256          }
257        }
258        return result;
259      }
260      
261    }