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 }