source: java/main/src/main/java/com/framsticks/params/ReflectionAccess.java @ 96

Last change on this file since 96 was 96, checked in by psniegowski, 11 years ago

HIGHLIGHTS:

  • cleanup Instance management
    • extract Instance interface
    • extract Instance common algorithms to InstanceUtils?
  • fix closing issues: Ctrl+C or window close button

properly shutdown whole program

by Java Framsticks framework

  • fix parsing and printing of all request types
  • hide exception passing in special handle method of closures
    • substantially improve readability of closures
    • basically enable use of exception in asynchronous closures

(thrown exception is transported back to the caller)

  • implement call request on both sides

CHANGELOG:
Further improve calling.

Improve instance calling.

Calling is working on both sides.

Improve exception handling in testing.

Waiters do not supercede other apllication exception being thrown.

Finished parsing and printing of all request types (with tests).

Move implementation and tests of request parsing to Request.

Add tests for Requests.

Improve waits in asynchronours tests.

Extract more algorithms to InstanceUtils?.

Extract Instance.resolve to InstanceUtils?.

Improve naming.

Improve passing exception in InstanceClient?.

Hide calling of passed functor in StateCallback?.

Hide Exception passing in asynchronous closures.

Hide exception passing in Future.

Make ResponseCallback? an abstract class.

Make Future an abstract class.

Minor change.

Move getPath to Path.to()

Move bindAccess to InstanceUtils?.

Extract common things to InstanceUtils?.

Fix synchronization bug in Connection.

Move resolve to InstanceUtils?.

Allow names of Joinable to be dynamic.

Add support for set request server side.

More fixes in communication.

Fix issues with parsing in connection.

Cut new line characters when reading.

More improvements.

Migrate closures to FramsticksException?.

Several changes.

Extract resolveAndFetch to InstanceUtils? algorithms.

Test resolving and fetching.

More fixes with function signature deduction.

Do not print default values in SimpleAbstractAccess?.

Add test of FramsClass? printing.

Improve FramsticksException? messages.

Add explicit dispatcher synchronization feature.

Rework assertions in tests.

Previous solution was not generic enough.

Allow addition of joinables to collection after start.

Extract SimulatorInstance? from RemoteInstance?.

Remove PrivateJoinableCollection?.

Improve connections.

Move shutdown hook to inside the Monitor.

It should work in TestNG tests, but it seems that
hooks are not called.

In ServerTest? client connects to testing server.

Move socket initialization to receiver thread.

Add proper closing on Ctrl+C (don't use signals).

Fix bugs with server accepting connections.

Merge Entity into Joinable.

Reworking ServerInstance?.

Extract more algorithm to InstanceUtils?.

Extract some common functionality from AbstractInstance?.

Functions were placed in InstanceUtils?.

Hide registry of Instance.

Use ValueParam? in Instance interface.

Minor change.

Extract Instance interface.

Old Instance is now AbstractInstance?.

File size: 11.9 KB
Line 
1package com.framsticks.params;
2
3import java.lang.reflect.Field;
4import java.lang.reflect.InvocationTargetException;
5import java.lang.reflect.Method;
6import java.util.ArrayList;
7import java.util.Collections;
8import java.util.Comparator;
9import java.util.HashMap;
10import java.util.List;
11import java.util.Map;
12
13import javax.annotation.concurrent.Immutable;
14
15import org.apache.log4j.Logger;
16
17import com.framsticks.params.annotations.AutoAppendAnnotation;
18import com.framsticks.params.types.ProcedureParam;
19import com.framsticks.util.FramsticksException;
20import com.framsticks.util.lang.Pair;
21
22import static com.framsticks.util.lang.Containers.*;
23
24/**
25 * The Class ReflectionAccess. Stores data in provided object using reflection.
26 *
27 * @author Mateusz Jarus <name.surname@gmail.com> (please replace name and
28 *         surname with my personal data)
29 *
30 * @author Piotr Sniegowski
31 */
32public class ReflectionAccess extends SimpleAbstractAccess {
33        private final static Logger log = Logger.getLogger(ReflectionAccess.class.getName());
34
35        protected final Class<?> javaClass;
36        protected final Backend backend;
37
38        private Object object;
39
40        @Immutable
41        public static class Backend {
42
43                protected static final Map<Pair<Class<?>, FramsClass>, Backend> synchronizedCache = Collections.synchronizedMap(new HashMap<Pair<Class<?>, FramsClass>, Backend>());
44
45
46                public interface ReflectedGetter {
47                        public <T> T get(Object object, Class<T> type) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
48                }
49
50                public interface ReflectedSetter {
51                        public <T> void set(Object object, T value) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
52                }
53
54                public interface ReflectedCaller {
55                        public Object call(Object object, Object[] arguments) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException;
56                }
57
58                protected final Map<ValueParam, ReflectedSetter> setters = new HashMap<>();
59                protected final Map<ValueParam, ReflectedGetter> getters = new HashMap<>();
60                protected final Map<ProcedureParam, ReflectedCaller> callers = new HashMap<>();
61                protected final List<Method> autoAppendMethods = new ArrayList<>();
62
63                /**
64                 * @param params
65                 */
66                public Backend() {
67                }
68
69                public static Backend getOrCreateFor(Class<?> reflectedClass, FramsClass framsClass) {
70
71                        Pair<Class<?>, FramsClass> id = new Pair<Class<?>, FramsClass>(reflectedClass, framsClass);
72                        Backend backend = synchronizedCache.get(id);
73                        if (backend != null) {
74                                return backend;
75                        }
76
77                        log.debug("constructing backend for " + id);
78                        backend = new Backend();
79
80                        Map<String, ParamCandidate> candidates = ParamCandidate.getAllCandidates(reflectedClass).getCandidates();
81
82                        try {
83                                for (final ProcedureParam pp : filterInstanceof(framsClass.getParamEntries(), ProcedureParam.class)) {
84                                        if (!candidates.containsKey(pp.getId())) {
85                                                log.trace("java class does implement method " + pp);
86                                                continue;
87                                        }
88                                        ParamCandidate pc = candidates.get(pp.getId());
89                                        final Method method = pc.getCaller();
90
91                                        backend.callers.put(pp, new ReflectedCaller() {
92
93                                                @Override
94                                                public Object call(Object object, Object[] arguments) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
95                                                        return method.invoke(object, arguments);
96                                                }
97                                        });
98
99                                }
100
101                                for (final ValueParam vp : filterInstanceof(framsClass.getParamEntries(), ValueParam.class)) {
102                                        if (!candidates.containsKey(vp.getId())) {
103                                                throw new ConstructionException().msg("missing candidate for param").arg("param", vp);
104                                        }
105                                        ParamCandidate pc = candidates.get(vp.getId());
106                                        if (pc.isReadOnly() && !vp.hasFlag(Flags.READONLY)) {
107                                                throw new ConstructionException().msg("readonly state conflict").arg("param", vp);
108                                        }
109                                        if (!typeMatch(pc.getRawType(), vp.getStorageType())) {
110                                                throw new ConstructionException().msg("types mismatch for param").arg("param", vp).arg("candidate", pc.getType()).arg("storage", vp.getStorageType());
111                                        }
112
113                                        final boolean primitive = pc.isPrimitive();
114                                        if (pc.getField() != null) {
115                                                final Field f = pc.getField();
116                                                backend.getters.put(vp, new ReflectedGetter() {
117                                                        @Override
118                                                        public <T> T get(Object object, Class<T> type) throws IllegalArgumentException, IllegalAccessException {
119                                                                return type.cast(f.get(object));
120                                                        }
121                                                });
122                                                if (!pc.isFinal()) {
123                                                        backend.setters.put(vp, new ReflectedSetter() {
124                                                                @Override
125                                                                public <T> void set(Object object, T value) throws IllegalArgumentException, IllegalAccessException {
126                                                                        if (value == null && primitive) {
127                                                                                throw new FramsticksException().msg("setting null to primitive value");
128                                                                        }
129                                                                        f.set(object, value);
130                                                                }
131                                                        });
132                                                }
133                                        } else {
134                                                final Method g = pc.getGetter();
135
136                                                backend.getters.put(vp, new ReflectedGetter() {
137                                                        @Override
138                                                        public <T> T get(Object object, Class<T> type) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
139                                                                return type.cast(g.invoke(object));
140                                                        }
141                                                });
142
143                                                if (!pc.isFinal()) {
144                                                        final Method s = pc.getSetter();
145                                                        backend.setters.put(vp, new ReflectedSetter() {
146                                                                @Override
147                                                                public <T> void set(Object object, T value) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
148                                                                        if (value == null && primitive) {
149                                                                                throw new FramsticksException().msg("setting null to primitive value");
150                                                                        }
151                                                                        s.invoke(object, value);
152                                                                }
153                                                        });
154                                                }
155                                        }
156                                }
157                        } catch (ConstructionException e) {
158                                throw e.arg("java class", reflectedClass).arg("framsClass", framsClass);
159                        }
160
161                        Class<?> javaClass = reflectedClass;
162                        while (javaClass != null) {
163
164                                for (Method m : javaClass.getDeclaredMethods()) {
165                                        AutoAppendAnnotation a = m.getAnnotation(AutoAppendAnnotation.class);
166                                        if (a == null) {
167                                                continue;
168                                        }
169                                        Class<?>[] args = m.getParameterTypes();
170                                        if (args.length != 1) {
171                                                throw new ConstructionException().msg("invalid number of arguments in AutoAppend marked method").arg("method", m).arg("arguments", args.length);
172                                        }
173                                        backend.autoAppendMethods.add(m);
174                                }
175
176                                javaClass = javaClass.getSuperclass();
177                        }
178
179                        Collections.sort(backend.autoAppendMethods, new Comparator<Method>() {
180
181                                @Override
182                                public int compare(Method m0, Method m1) {
183                                        Class<?> arg0 = m0.getParameterTypes()[0];
184                                        Class<?> arg1 = m1.getParameterTypes()[0];
185                                        if (arg0.isAssignableFrom(arg1)) {
186                                                return 1;
187                                        }
188                                        if (arg1.isAssignableFrom(arg0)) {
189                                                return -1;
190                                        }
191                                        return 0;
192                                }
193                        });
194
195                        synchronizedCache.put(id, backend);
196                        return backend;
197                }
198
199        }
200
201        public ReflectionAccess(Class<?> reflectedClass) throws ConstructionException {
202                this(reflectedClass, FramsClass.build().forClass(reflectedClass));
203        }
204
205        public static boolean typeMatch(Class<?> a, Class<?> b) {
206                assert !b.isPrimitive();
207                if (!a.isPrimitive()) {
208                        return a.equals(b);
209                }
210
211                if (a.equals(int.class)) {
212                        return b.equals(Integer.class);
213                }
214                if (a.equals(double.class)) {
215                        return b.equals(Double.class);
216                }
217                if (a.equals(boolean.class)) {
218                        return b.equals(Boolean.class);
219                }
220                assert false;
221                return false;
222        }
223
224        public ReflectionAccess(Class<?> reflectedClass, FramsClass framsClass) throws ConstructionException {
225                super(framsClass);
226                this.javaClass = reflectedClass;
227                this.backend = Backend.getOrCreateFor(reflectedClass, framsClass);
228        }
229
230
231        @Override
232        public <T> T get(ValueParam param, Class<T> type) {
233                try {
234                        try {
235                                if (object == null) {
236                                        throw new FramsticksException().msg("no object set");
237                                }
238
239                                return backend.getters.get(param).get(object, type);
240                        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
241                                throw new FramsticksException().msg("failed to get").cause(e);
242                        }
243                } catch (FramsticksException e) {
244                        throw e.arg("param", param).arg("type", type).arg("access", this);
245                }
246        }
247
248        @Override
249        protected <T> void internalSet(ValueParam param, T value) {
250                setValue(param, value);
251        }
252
253        private <T> void setValue(ValueParam param, T value) {
254                try {
255                        try {
256                                if (object == null) {
257                                        throw new FramsticksException().msg("no object set");
258                                }
259                                Backend.ReflectedSetter s = backend.setters.get(param);
260                                if (s == null) {
261                                        throw new FramsticksException().msg("trying to set unsettable");
262                                }
263                                s.set(object, value);
264                        } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
265                                throw new FramsticksException().msg("failed to set").cause(e);
266                        }
267                } catch (FramsticksException e) {
268                        throw e.arg("param", param).arg("value", value).arg("access", this);
269                }
270        }
271
272        @Override
273        public Object call(String id, Object[] arguments) {
274                return call(framsClass.getParamEntry(id, ProcedureParam.class), arguments);
275        }
276
277        @Override
278        public Object call(ProcedureParam param, Object[] arguments) {
279                try {
280                        try {
281                                if (object == null) {
282                                        throw new FramsticksException().msg("no object set");
283                                }
284                                Backend.ReflectedCaller c = backend.callers.get(param);
285                                if (c == null) {
286                                        throw new FramsticksException().msg("method is not bound");
287                                }
288                                return c.call(object, arguments);
289                        } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
290                                throw new FramsticksException().msg("failed to call").cause(e);
291                        }
292                } catch (FramsticksException e) {
293                        throw e.arg("param", param).arg("access", this);
294                }
295        }
296
297        void resetErrors() {
298                //TODO this replaces returnedObject.resetErrors();
299        }
300
301        @Override
302        public void clearValues() {
303                if (object == null) {
304                        return;
305                }
306
307                resetErrors();
308
309                try {
310                        for (ValueParam p : filterInstanceof(framsClass.getParamEntries(), ValueParam.class)) {
311                                setValue(p, p.getDef(Object.class));
312                        }
313                } catch (IllegalArgumentException ex) {
314                        ex.printStackTrace();
315                }
316        }
317
318        /**
319         * Sets the new object to operate on.
320         *
321         * @param object
322         *            new object to operate on
323         */
324        @Override
325        public ReflectionAccess select(Object object) {
326                assert object == null || javaClass.isInstance(object);
327                this.object = object;
328                return this;
329        }
330
331        @Override
332        public Object getSelected() {
333                return object;
334        }
335
336        // TODO: find a better place for it
337        public static String objectToString(Object object) {
338                StringBuilder b = new StringBuilder();
339                for (Field f : object.getClass().getFields()) {
340                        b.append(f.getName());
341                        b.append(":");
342                        try {
343                                Object value = f.get(object);
344                                b.append((value != null) ? value.toString() : "<null>");
345                        } catch (IllegalAccessException e) {
346                                e.printStackTrace();
347                        }
348                        b.append("\n");
349                }
350                return b.toString();
351        }
352
353        @Override
354        public ReflectionAccess cloneAccess() throws ConstructionException {
355                return new ReflectionAccess(javaClass, framsClass);
356        }
357
358        @Override
359        public Object createAccessee() {
360                try {
361                        return javaClass.newInstance();
362                } catch (InstantiationException | IllegalAccessException e) {
363                        e.printStackTrace();
364                }
365                log.fatal("failed to create reflected object of class " + javaClass.getCanonicalName() + " for frams type " + framsClass.getId());
366                return null;
367        }
368
369        @Override
370        public String toString() {
371                StringBuilder b = new StringBuilder();
372                b.append(framsClass);
373                if (object != null) {
374                        b.append("(").append(object).append(")");
375                }
376                return b.toString();
377        }
378
379        @Override
380        public void tryAutoAppend(Object value) {
381                assert object != null;
382                try {
383                        for (Method m : backend.autoAppendMethods) {
384                                if (m.getParameterTypes()[0].isAssignableFrom(value.getClass())) {
385                                        try {
386                                                log.trace("auto appending with value " + value + " with method " + m);
387                                                m.invoke(object, value);
388                                                return;
389                                        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | FramsticksException e) {
390                                                throw new FramsticksException().msg("failed to auto append").cause(e).arg("with method", m);
391                                        }
392                                }
393                        }
394                        throw new FramsticksException().msg("no method found to append");
395                } catch (FramsticksException e) {
396                        throw e.arg("value", value).arg("into object", object);
397                }
398
399        }
400
401}
402
Note: See TracBrowser for help on using the repository browser.