2828public abstract class WebService {
2929
3030 private ConcurrentHashMap <String , DomainClass > classes = new ConcurrentHashMap <>();
31-
31+ private LinkedHashMap <String , ArrayList <Method >> serviceMethods = new LinkedHashMap <>();
32+ private boolean strictLookup = false ;
3233
3334 public static Console console = new Console (); //do not replace with static import!
3435
@@ -48,6 +49,66 @@ public String toString(){
4849 }
4950
5051
52+ //**************************************************************************
53+ //** Constructor
54+ //**************************************************************************
55+ public WebService (){
56+
57+ //Generate a list of all the service methods in the subclass. Service
58+ //methods are public methods that accept a ServiceRequest parameter and
59+ //return a ServiceResponse object. Implementation note:
60+ //The getDeclaredMethod() method will only find methods declared in the
61+ //current Class, not inherited from supertypes. So we may need to
62+ //traverse up the concrete class hierarchy if this becomes a requirement.
63+ for (Method m : this .getClass ().getDeclaredMethods ()){
64+ if (Modifier .isPrivate (m .getModifiers ())) continue ;
65+
66+ if (m .getReturnType ().equals (ServiceResponse .class )){
67+
68+ Class <?>[] params = m .getParameterTypes ();
69+ if (params .length >0 ){
70+ if (ServiceRequest .class .isAssignableFrom (params [0 ])){
71+ String key = m .getName ();
72+ if (!strictLookup ) key = key .toLowerCase ();
73+ ArrayList <Method > methods = serviceMethods .get (key );
74+ if (methods ==null ){
75+ methods = new ArrayList <>();
76+ serviceMethods .put (key , methods );
77+ }
78+ methods .add (m );
79+ }
80+ }
81+ }
82+ }
83+
84+
85+
86+ //Experimental special case for anonymous classes
87+ if (this .getClass ().isAnonymousClass ()){
88+ for (Method m : this .getClass ().getMethods ()){
89+ if (Modifier .isPrivate (m .getModifiers ())) continue ;
90+
91+ if (m .getReturnType ().equals (ServiceResponse .class )){
92+
93+ Class <?>[] params = m .getParameterTypes ();
94+ if (params .length >0 ){
95+ if (ServiceRequest .class .isAssignableFrom (params [0 ])){
96+ String key = m .getName ();
97+ if (!strictLookup ) key = key .toLowerCase ();
98+ ArrayList <Method > methods = serviceMethods .get (key );
99+ if (methods ==null ){
100+ methods = new ArrayList <>();
101+ serviceMethods .put (key , methods );
102+ methods .add (m );
103+ }
104+ }
105+ }
106+ }
107+ }
108+ }
109+ }
110+
111+
51112 //**************************************************************************
52113 //** addModel
53114 //**************************************************************************
@@ -132,145 +193,80 @@ public ServiceResponse getServiceResponse(ServiceRequest request, Database datab
132193 String methodName = request .getMethod ();
133194
134195
135- //Find a concrete implementation of the requested method in the subclass
136- {
137-
138- boolean strictLookup = false ;
139- if (!strictLookup ) methodName = methodName .toLowerCase ();
140-
141-
142-
143- //Generate a list of all the service methods in the subclass. Service
144- //methods are public methods that accept a ServiceRequest parameter
145- //and return a ServiceResponse object. Implementation note: the
146- //getDeclaredMethod() method will only find methods declared in the
147- //current Class, not inherited from supertypes. So we may need to
148- //traverse up the concrete class hierarchy if this becomes a requirement.
149- LinkedHashMap <String , ArrayList <Method >> serviceMethods = new LinkedHashMap <>();
150- for (Method m : this .getClass ().getDeclaredMethods ()){
151- if (Modifier .isPrivate (m .getModifiers ())) continue ;
152-
153- if (m .getReturnType ().equals (ServiceResponse .class )){
154-
155- Class <?>[] params = m .getParameterTypes ();
156- if (params .length >0 ){
157- if (ServiceRequest .class .isAssignableFrom (params [0 ])){
158- String key = m .getName ();
159- if (!strictLookup ) key = key .toLowerCase ();
160- ArrayList <Method > methods = serviceMethods .get (key );
161- if (methods ==null ){
162- methods = new ArrayList <>();
163- serviceMethods .put (key , methods );
164- }
165- methods .add (m );
166- }
167- }
168- }
169- }
170196
197+ //Find a concrete implementation of the requested method in the subclass
198+ if (!strictLookup ) methodName = methodName .toLowerCase ();
199+ ArrayList <Method > methods = null ;
200+ if (serviceMethods .containsKey (methodName )){
201+ methods = serviceMethods .get (methodName );
202+ }
203+ else {
204+ int i = 0 ;
205+ if (methodName .startsWith ("get" )) i = 4 ;
206+ if (methodName .startsWith ("save" )) i = 5 ;
207+ if (methodName .startsWith ("delete" )) i = 6 ;
171208
209+ if (i >0 ){
172210
173- //Experimental special case for anonymous classes
174- if ( this . getClass (). isAnonymousClass ()){
175- for ( Method m : this . getClass (). getMethods ()){
176- if ( Modifier . isPrivate ( m . getModifiers ())) continue ;
211+ //Check for a simple method like search() or update() without a
212+ //"get" or "save" or "delete" prefix
213+ methodName = methodName . substring ( i - 1 , i ). toLowerCase () + methodName . substring ( i );
214+ methods = serviceMethods . get ( methodName ) ;
177215
178- if (m .getReturnType ().equals (ServiceResponse .class )){
179216
180- Class <?>[] params = m .getParameterTypes ();
181- if (params .length >0 ){
182- if (ServiceRequest .class .isAssignableFrom (params [0 ])){
183- String key = m .getName ();
184- if (!strictLookup ) key = key .toLowerCase ();
185- ArrayList <Method > methods = serviceMethods .get (key );
186- if (methods ==null ){
187- methods = new ArrayList <>();
188- serviceMethods .put (key , methods );
189- methods .add (m );
190- }
191- //methods.add(m);
192- }
193- }
194- }
217+ //Special case for POST requests (e.g. "POST /companies") that
218+ //map to a "save" method (e.g. saveCompanies) that does not exist.
219+ //Let's check if there's a suitable "get" method instead (e.g. getCompanies).
220+ if (methods ==null && i ==5 ){ // && methodName.endsWith("s")
221+ methods = serviceMethods .get ("get" + methodName );
195222 }
196- }
197-
198223
199-
200- //Find service methods that implement the requested method
201- ArrayList <Method > methods = null ;
202- if (serviceMethods .containsKey (methodName )){
203- methods = serviceMethods .get (methodName );
204224 }
205- else {
206- int i = 0 ;
207- if (methodName .startsWith ("get" )) i = 4 ;
208- if (methodName .startsWith ("save" )) i = 5 ;
209- if (methodName .startsWith ("delete" )) i = 6 ;
225+ }
210226
211- if (i >0 ){
212227
213- //Check for a simple method like search() or update() without
214- //a "get" or "save" or "delete" prefix
215- methodName = methodName . substring ( i - 1 , i ). toLowerCase () + methodName . substring ( i );
216- methods = serviceMethods . get ( methodName );
228+ //Return ServiceResponse
229+ if ( methods != null ){
230+ for ( Method m : methods ){
231+ Class <?>[] params = m . getParameterTypes ( );
217232
218233
219- //Special case for POST requests (e.g. "POST /companies")
220- //that map to a "save" method (e.g. saveCompanies) that does
221- //not exist. Let's check if there's a suitable "get" method
222- //instead (e.g. getCompanies).
223- if (methods ==null && i ==5 ){ // && methodName.endsWith("s")
224- methods = serviceMethods .get ("get" + methodName );
234+ //Check whether the method accepts a ServiceRequest
235+ //or ServiceRequest + Database as inputs
236+ Object [] inputs = null ;
237+ if (params .length ==1 ){
238+ inputs = new Object []{request };
239+ }
240+ else if (params .length ==2 ){
241+ if (Database .class .isAssignableFrom (params [1 ])){
242+ inputs = new Object []{request , database };
225243 }
226-
227244 }
228- }
229245
246+ if (inputs !=null ){
230247
231- //Return ServiceResponse
232- if (methods !=null ){
233- for (Method m : methods ){
234- Class <?>[] params = m .getParameterTypes ();
235248
236-
237- //Check whether the method accepts a ServiceRequest
238- //or ServiceRequest + Database as inputs
239- Object [] inputs = null ;
240- if (params .length ==1 ){
241- inputs = new Object []{request };
242- }
243- else if (params .length ==2 ){
244- if (Database .class .isAssignableFrom (params [1 ])){
245- inputs = new Object []{request , database };
246- }
249+ //Ensure that we don't want to invoke this function!
250+ //For example, the caller might want to call
251+ //super.getServiceResponse(request, database);
252+ //If so, we would end up in a recursion causing a stack
253+ //overflow. Instead of calling getServiceResponse()
254+ //let's just flow down to the CRUD handlers below.
255+ StackTraceElement [] stackTrace = new Exception ().getStackTrace ();
256+ StackTraceElement el = stackTrace [1 ];
257+ if (m .getName ().equals (el .getMethodName ())){
258+ break ;
247259 }
248260
249- if (inputs !=null ){
250-
251-
252- //Ensure that we don't want to invoke this function!
253- //For example, the caller might want to call
254- //super.getServiceResponse(request, database);
255- //If so, we would end up in a recursion causing a
256- //stack overflow. Instead of calling getServiceResponse()
257- //let's just flow down to the CRUD handlers below.
258- StackTraceElement [] stackTrace = new Exception ().getStackTrace ();
259- StackTraceElement el = stackTrace [1 ];
260- if (m .getName ().equals (el .getMethodName ())){
261- break ;
262- }
263-
264261
265- //If we're still here, call the requested method
266- //and return the response
267- try {
268- m .setAccessible (true );
269- return (ServiceResponse ) m .invoke (this , inputs );
270- }
271- catch (Exception e ){
272- return getServiceResponse (e );
273- }
262+ //If we're still here, call the requested method and return
263+ //the response
264+ try {
265+ m .setAccessible (true );
266+ return (ServiceResponse ) m .invoke (this , inputs );
267+ }
268+ catch (Exception e ){
269+ return getServiceResponse (e );
274270 }
275271 }
276272 }
0 commit comments