001 
002 /*
003  *  JScripter Standard 1.0 - To Script In Java
004  *  Copyright (C) 2008-2011  J.J.Liu<jianjunliu@126.com> <http://www.jscripter.org>
005  *  
006  *  This program is free software: you can redistribute it and/or modify
007  *  it under the terms of the GNU Affero General Public License as published by
008  *  the Free Software Foundation, either version 3 of the License, or
009  *  (at your option) any later version.
010  *  
011  *  This program 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
014  *  GNU Affero General Public License for more details.
015  *  
016  *  You should have received a copy of the GNU Affero General Public License
017  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
018  */
019 
020 package jsx.http.rpc.server;
021 
022 import java.lang.reflect.Field;
023 import java.lang.reflect.InvocationTargetException;
024 import java.lang.reflect.Method;
025 import java.util.concurrent.atomic.AtomicInteger;
026 
027 import js.ArrayLike;
028 import js.ObjectLike;
029 import jsx.http.rpc.remote.Remote;
030 import jsx.http.rpc.remote.RemoteReflector;
031 
032 /**
033  * <p>A base class of the server-side providers for remote reflect services.</p>
034  * <p>Note that a server-side provider for remote reflect services is also a 
035  * {@link Remote} object.</p>
036  * 
037  * @author <a href="mailto:jianjunliu@126.com">J.J.Liu (Jianjun Liu)</a> at <a href="http://www.jscripter.org" target="_blank">http://www.jscripter.org</a>
038  */
039 public class ServerReflector extends RemoteReflector
040 {
041     /**
042      * <p>A typical constructor for a {@link ServerReflector} object.</p>
043      * @param ini An initializer for configuration.
044      * @since 1.0
045      */
046     public ServerReflector(ObjectLike ini) {
047         super(ini);
048     }
049 
050     /**
051      * <p>Checks if the servicing remote object IS a reflector.</p>
052      * @return <tt>true</tt> if it is a reflector; <tt>false</tt>, otherwise.
053      * @since 1.0
054      */
055     public boolean isReflector() {
056         return get(this, TID) == TID_REFLECTOR;
057     }
058 
059     private final static IdentityMap cache = new IdentityMap();
060     private final static AtomicInteger nextvid = new AtomicInteger(0);
061 
062     private static final String getCacheKey(Object o) {
063         if (cache.containsValue(o)) {
064             return cache.getKey(o);
065         } else {
066             String k = new Integer(nextvid.incrementAndGet()).toString();
067             cache.put(k, o);
068             return k;
069         }
070     }
071 
072     /**
073      * <p>Locally registers the current reflector.</p>
074      * @return A registered version of this reflector.
075      * @since 1.0
076      */
077     @Override
078     public RemoteReflector register() {
079         string(this, VID, new Integer(nextvid.incrementAndGet()).toString());
080         return this;
081     }
082 
083     /**
084      * <p>Locally increases remote reference count for a remote object.</p>
085      * @param args An array of arguments. Only the first one is used here.
086      * @since 1.0
087      */
088     @Override
089     public void increase(ArrayLike<Remote> args) {
090         for (int i = 0; i < args.length(); i++) {
091             Remote o = args.get(i);
092             int tid = get(o, TID);
093             switch(tid) {
094                 case TID_NULL:
095                 case TID_NUMBER:
096                 case TID_STRING:
097                 case TID_CLASS:
098                 case TID_REFLECTOR:
099                     break;
100                 default:
101                     cache.increase(string(o, VID));
102             }
103         }
104     }
105 
106     /**
107      * <p>Locally decreases remote reference count for a remote object.</p>
108      * @param args An array of arguments. Only the first one is used here.
109      * @since 1.0
110      */
111     @Override
112     public void decrease(ArrayLike<Remote> args) {
113         for (int i = 0; i < args.length(); i++) {
114             Remote o = args.get(i);
115             int tid = get(o, TID);
116             switch(tid) {
117                 case TID_NULL:
118                 case TID_NUMBER:
119                 case TID_STRING:
120                 case TID_CLASS:
121                 case TID_REFLECTOR:
122                     break;
123                 default:
124                     cache.decrease(string(o, VID));
125             }
126         }
127     }
128 
129     /**
130      * <p>Locally loads a remote class by the class name.</p>
131      * @param name A Java class name.
132      * @return The newly created remote class with the class name.
133      * @since 1.0
134      */
135     @Override
136     public Remote forName(String name) {
137         try {
138             Class.forName(name);
139             return new Remote(Remote.TID_CLASS, name);
140         } catch (ClassNotFoundException e) {
141             e.printStackTrace();
142             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
143         }
144     }
145 
146     private static final Object checkIn(Remote v, int tid) {
147         if (!(v instanceof Remote) || tid != get(v, TID)) return null;
148         switch(tid) {
149             case TID_NULL:
150                 return null;
151             case TID_NUMBER:
152             case TID_STRING:
153                 return v;
154             case TID_CLASS:
155                 return string(v, VID);
156             default:
157                 return cache.get(string(v, VID));
158         }
159     }
160 
161     /**
162      * <p>Locally gets the remote class for a remote object.</p>
163      * @param o A remote object.
164      * @return The remote class for the remote object.
165      * @since 1.0
166      */
167     @Override
168     public Remote getRemoteClass(Remote o) {
169         return checkOut(checkIn(
170                 o,
171                 get(o, TID)
172         ).getClass());
173     }
174 
175     /**
176      * <p>Locally creates a remote instance of a remote class.</p>
177      * @param o The remote class.
178      * @return The newly created remote object.
179      * @since 1.0
180      */
181     @Override
182     public Remote newInstance(Remote o) {
183         try {
184             return checkOut(Class.forName((String)checkIn(
185                     o,
186                     TID_CLASS
187             )).newInstance());
188         } catch (ClassNotFoundException e) {
189             e.printStackTrace();
190             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
191         } catch (InstantiationException e) {
192             e.printStackTrace();
193             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
194         } catch (IllegalAccessException e) {
195             e.printStackTrace();
196             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
197         }
198     }
199 
200     private static final int tid(Object o) {
201         if (o == null) {
202             return TID_NULL;
203         } else if (o instanceof Number) {
204             return TID_NUMBER;
205         } else if (o instanceof String) {
206             return TID_STRING;
207         } else if (o instanceof Class) {
208             return TID_CLASS;
209         } else if (o instanceof Exception) {
210             return TID_EXCEPTION;
211         } else if (o instanceof Field) {
212             return TID_FIELD;
213         } else if (o instanceof Method) {
214             return TID_METHOD;
215         } else {
216             return TID_OBJECT;
217         }
218     }
219 
220     private static final Remote checkOut(Object o) {
221         int tid = tid(o);
222         switch (tid) {
223             case TID_NULL:
224                 return new Remote(tid, "null");
225             case TID_NUMBER:
226             case TID_STRING:
227                 return new Remote(tid, o.toString());
228             case TID_CLASS:
229                 return new Remote(tid, ((Class<?>)o).getName());
230             default:
231                 return new Remote(tid, getCacheKey(o));
232         }
233     }
234 
235     /**
236      * <p>Locally gets an element of a remote array.</p>
237      * @param o The remote array.
238      * @param i The array index for the element.
239      * @return A remote value or object.
240      * @since 1.0
241      */
242     @Override
243     public Remote getArrayElement(Remote o, int i) {
244         try {
245             return checkOut(((Object[])checkIn(o, TID_ARRAY))[i]);
246         } catch (Exception e) {
247             e.printStackTrace();
248             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
249         }
250     }
251 
252     /**
253      * <p>Locally gets the current length of a remote array.</p>
254      * @param o The remote array.
255      * @return A remote value for the length.
256      * @since 1.0
257      */
258     @Override
259     public Remote getArrayLength(Remote o) {
260         try {
261             return new Remote(
262                     Remote.TID_NUMBER,
263                     new Integer(((Object[])checkIn(o, TID_ARRAY)).length).toString()
264             );
265         } catch (Exception e) {
266             e.printStackTrace();
267             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
268         }
269     }
270 
271     /**
272      * <p>Locally sets an element of a remote array.</p>
273      * @param o The remote array.
274      * @param i The array index for the element.
275      * @param v The new value for the element.
276      * @return A remote value or object.
277      * @since 1.0
278      */
279     @Override
280     public Remote setArrayElement(Remote o, int i, Remote v) {
281         try {
282             getCacheKey(v);
283             ((Object[])checkIn(o, TID_ARRAY))[i] = checkIn(v, get(v, TID));
284             return v;
285         } catch (Exception e) {
286             e.printStackTrace();
287             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
288         }
289     }
290 
291     /**
292      * <p>Locally gets a remote field.</p>
293      * @param o The remote object for the base.
294      * @param name The name for the field.
295      * @return A remote field.
296      * @since 1.0
297      */
298     @Override
299     public Remote getField(Remote o, String name) {
300         Class<?> c;
301         try {
302             c = Class.forName(string(o, VID));
303         } catch (ClassNotFoundException e) {
304             e.printStackTrace();
305             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
306         }
307         try {
308             return checkOut(
309                     c.getField(name)
310             );
311         } catch (SecurityException e) {
312             e.printStackTrace();
313             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
314         } catch (NoSuchFieldException e) {
315             e.printStackTrace();
316             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
317         }
318     }
319 
320     /**
321      * <p>Locally evaluates a remote static field.</p>
322      * @param o The remote static field.
323      * @return A remote value or object.
324      * @since 1.0
325      */
326     @Override
327     public Remote get(Remote o) {
328         Field f;
329         try {
330             f = (Field)checkIn(o, TID_FIELD);
331         } catch (Exception e) {
332             e.printStackTrace();
333             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
334         }
335         try {
336             Object v = f.get(null);
337             return checkOut(v);
338         } catch (IllegalArgumentException e) {
339             e.printStackTrace();
340             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
341         } catch (IllegalAccessException e) {
342             e.printStackTrace();
343             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
344         }
345     }
346 
347     /**
348      * <p>Locally evaluates a remote instance field.</p>
349      * @param o The remote instance field.
350      * @param base The remote object for the base.
351      * @return A remote value or object.
352      * @since 1.0
353      */
354     @Override
355     public Remote get(Remote o, Remote base) {
356         Field f;
357         try {
358             f = (Field)checkIn(o, TID_FIELD);
359         } catch (Exception e) {
360             e.printStackTrace();
361             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
362         }
363         try {
364             Object v = f.get(checkIn(base, get(base, TID)));
365             return checkOut(v);
366         } catch (IllegalArgumentException e) {
367             e.printStackTrace();
368             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
369         } catch (IllegalAccessException e) {
370             e.printStackTrace();
371             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
372         }
373     }
374 
375     /**
376      * <p>Locally gets a remote method.</p>
377      * @param o The remote object for the base.
378      * @param name The name for the method.
379      * @return A remote method.
380      * @since 1.0
381      */
382     @Override
383     public Remote getMethod(Remote o, String name) {
384         Class<?> c;
385         try {
386             c = Class.forName(string(o, VID));
387         } catch (ClassNotFoundException e) {
388             e.printStackTrace();
389             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
390         }
391         try {
392             return checkOut(
393                     c.getMethod(name)
394             );
395         } catch (SecurityException e) {
396             e.printStackTrace();
397             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
398         } catch (NoSuchMethodException e) {
399             e.printStackTrace();
400             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
401         }
402     }
403 
404     /**
405      * <p>Locally gets a remote method.</p>
406      * @param o The remote object for the base.
407      * @param name The name for the method.
408      * @param types An array of argument types for the remote method.
409      * @return A remote method.
410      * @since 1.0
411      */
412     @Override
413     public Remote getMethod(Remote o, String name, ArrayLike<String> types) {
414         Class<?> c; Class<?>[] atypes;
415         try {
416             c = Class.forName(string(o, VID));
417             atypes = new Class<?>[types.length()];
418             for (int i = 0; i < types.length(); i++) {
419                 atypes[i] = Class.forName(types.get(i));
420             }
421         } catch (ClassNotFoundException e) {
422             e.printStackTrace();
423             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
424         }
425         try {
426             return checkOut(c.getMethod(name, atypes));
427         } catch (SecurityException e) {
428             e.printStackTrace();
429             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
430         } catch (NoSuchMethodException e) {
431             e.printStackTrace();
432             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
433         }
434     }
435 
436     /**
437      * <p>Locally invokes a remote method without arguments.</p>
438      * @param o The remote method.
439      * @return A remote value or object returned from the remote method invocation.
440      * @since 1.0
441      */
442     @Override
443     public Remote invoke(Remote o) {
444         Method m;
445         try {
446             m = (Method)checkIn(o, TID_METHOD);
447         } catch (Exception e) {
448             e.printStackTrace();
449             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
450         }
451         try {
452             Object v = m.invoke(null);
453             return checkOut(v);
454         } catch (IllegalArgumentException e) {
455             e.printStackTrace();
456             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
457         } catch (IllegalAccessException e) {
458             e.printStackTrace();
459             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
460         } catch (InvocationTargetException e) {
461             e.printStackTrace();
462             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
463         }
464     }
465 
466     /**
467      * <p>Locally invokes a remote method with arguments.</p>
468      * @param o The remote method.
469      * @param args An array of arguments.
470      * @return A remote value or object returned from the remote method invocation.
471      * @since 1.0
472      */
473     @Override
474     public Remote invoke(Remote o, ArrayLike<Remote> args) {
475         Method m; Object b; Object[] a;
476         try {
477             m = (Method)checkIn(o, TID_METHOD);
478             b = checkIn(args.get(0), get(args.get(0), TID));
479             a = new Object[args.length() - 1];
480             for (int i = 1; i < args.length(); i++) {
481                 a[i - 1] = checkIn(args.get(i), get(args.get(i), TID));
482             }
483         } catch (Exception e) {
484             e.printStackTrace();
485             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
486         }
487         try {
488             return checkOut(m.invoke(b, a));
489         } catch (IllegalArgumentException e) {
490             e.printStackTrace();
491             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
492         } catch (IllegalAccessException e) {
493             e.printStackTrace();
494             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
495         } catch (InvocationTargetException e) {
496             e.printStackTrace();
497             return new Remote(Remote.TID_EXCEPTION, getCacheKey(e));
498         }
499     }
500 }