source: java/main/src/main/java/com/framsticks/remote/RemoteInstance.java @ 90

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

HIGHLIGHTS:

CHANGELOG:
Make ProcedureParam? hold only ValueParams?.

Use id instead of names when naming gui components internally.

Basic procedure calling in GUI.

The actual procedure call is currently only backed
by the ObjectInstance?.

Add UnimplementedException?.

Improve naming of various gui elements.

Allow easy navigating in FEST Swing testing.

Add optional explicit order attribute to FramsClassAnnotation?.

That's because java reflection does return declared members
in any specific order. That ordering is needed only for
classes that have no representation in framsticks and need
a deterministic ordering of params.

Add ControlOwner? interface.

Add test for procedure calling in Browser.

First version of ParamAnnotation? for procedures.

Development of ProcedureParam?.

Add draft version of ProcedureParam? implementation in ReflectionAccess?.

Allow viewing FramsClasses? in gui Browser.

Extract ResourceBuilder? from ModelBuilder?.

Remove internalId from Param.

It was currently completely not utilised. Whether it is still needed
after introduction of ParamAnnotation? is arguable.

Add remaining param attributes to ParamAnnotation?.

Change AutoBuilder? semantics.

AutoBuilder? returns list of objects that are to be appended
with methods @AutoAppendAnnotation?.

This allows to omit explicit addition of ModelPackage? to instance
if the instance uses ModelBuilder? (registration of ModelPackage? comes
from schema).

Fix params ordering problem in auto created FramsClasses?.

Improve ObjectInstance?.

Several fixes to ModelBuilder?.

Improve test for ObjectInstance? in Browser.

Make initialization of robot static.

With robot recreated for second browser test, the test hanged
deep in AWT.

Add base convenience base test for Browser tests.

More tests to ObjectInstance?.

Rename Dispatcher.invokeLater() to dispatch().

Add assertDispatch.

It allows assertions in other threads, than TestNGInvoker.
Assertions are gathered after each method invocation and rethrown.

Use timeOut annotation attribute for tests involving some waiting.

Remove firstTask method (merge with joinableStart).

Clean up leftovers.

Remove unused FavouritesXMLFactory (the reading part is already
completely done with generic XmlLoader?, and writing part will be done
based on the same approach if needed).
Move UserFavourite? to the com.framsticks.gui.configuration package.

Remove GenotypeBrowser? as to specific.

This functionality will be available in ObjectInstance?.

Add interface ParamsPackage?.

Package containing registration of Java classes meant to use with
ReflectionAccess? may be in Instance using configuration.

Minor changes.

Make Group immutable.

Add AutoBuilder? interface extending Builder - only those would
be used to automatically build from XML.

Fix groups in FramsClass?.

Minor naming cleanup in Registry.

Add ModelComponent? interface.

All class creating the Model are implementing that interface.

Extract Model.build into ModelBuilder?.

ModelBuilder? will be compatible with other builders
and allow using it from configuration.

Fix NeuroConnection?.

Add synchronous get operation for dispatchers.

Rename JoinableMonitor? to Monitor.

Add ObjectInstance?.

This class is mainly for demonstration
and testing purposes.

Improve FramsServer? runner.

  • improve ExternalProcess? runner,
  • runner can kill the server but also react properly, when the server exists on it's own,
  • set default path to search for framsticks server installation,
  • add LoggingOutputListener?.
File size: 14.7 KB
Line 
1package com.framsticks.remote;
2
3import com.framsticks.communication.*;
4import com.framsticks.communication.queries.GetRequest;
5import com.framsticks.communication.queries.InfoRequest;
6import com.framsticks.communication.queries.SetRequest;
7import com.framsticks.communication.util.LoggingSubscriptionCallback;
8import com.framsticks.core.ListChange;
9import com.framsticks.core.Path;
10import com.framsticks.params.*;
11import com.framsticks.params.annotations.FramsClassAnnotation;
12import com.framsticks.params.annotations.ParamAnnotation;
13import com.framsticks.params.types.EventParam;
14import com.framsticks.params.types.ProcedureParam;
15import com.framsticks.parsers.MultiParamLoader;
16import com.framsticks.core.Instance;
17import com.framsticks.util.*;
18import com.framsticks.util.dispatching.Dispatching;
19import com.framsticks.util.dispatching.Future;
20import com.framsticks.util.dispatching.Joinable;
21import com.framsticks.util.dispatching.JoinableParent;
22import com.framsticks.util.dispatching.JoinableState;
23import com.framsticks.util.lang.Casting;
24import com.framsticks.util.lang.Pair;
25import com.framsticks.util.dispatching.RunAt;
26
27import java.util.*;
28
29import org.apache.log4j.Logger;
30
31/**
32 * @author Piotr Sniegowski
33 */
34@FramsClassAnnotation
35public class RemoteInstance extends Instance implements JoinableParent {
36
37        private final static Logger log = Logger.getLogger(RemoteInstance.class.getName());
38
39        protected Path simulator;
40        protected ClientConnection connection;
41
42        protected final Set<Pair<Path, Subscription<?>>> subscriptions = new HashSet<>();
43
44        public Pair<Path, Subscription<?>> getSubscription(Path path) {
45                for (Pair<Path, Subscription<?>> s : subscriptions) {
46                        if (s.first.matches(path)) {
47                                return s;
48                        }
49                }
50                return null;
51        }
52
53        public RemoteInstance() {
54        }
55
56        @ParamAnnotation
57        public void setAddress(String address) {
58                setConnection(new ClientConnection(address));
59        }
60
61        @ParamAnnotation
62        public String getAddress() {
63                return connection == null ? "<disconnected>" : connection.getAddress();
64        }
65
66        public void setConnection(final ClientConnection connection) {
67                this.connection = connection;
68                this.connection.setConnectedFunctor(new StateFunctor() {
69                        @Override
70                        public void call(Exception e) {
71                                if (e != null) {
72                                        fireRun(e);
73                                        return;
74                                }
75                                connection.negotiateProtocolVersion(new StateFunctor() {
76                                        @Override
77                                        public void call(Exception e) {
78                                                if (e != null) {
79                                                        log.fatal("unsupported protocol version!\n minimal version is: " + "\nmanager protocol is: " + connection.getProtocolVersion());
80                                                        Dispatching.drop(connection, RemoteInstance.this);
81                                                        fireRun(e);
82                                                        return;
83                                                }
84
85                                                dispatch(new RunAt<Instance>() {
86                                                        @Override
87                                                        public void run() {
88                                                                resolveAndFetch("/simulator", new Future<Path>() {
89                                                                        @Override
90                                                                        public void result(Path path, Exception e) {
91                                                                                if (e != null) {
92                                                                                        log.fatal("failed to resolve simulator node");
93                                                                                        fireRun(e);
94                                                                                        return;
95                                                                                }
96                                                                                assert isActive();
97                                                                                simulator = path;
98                                                                                fireRun(null);
99                                                                                log.info("resolved simulator node");
100
101                                                                                EventParam param = getParam(simulator, "running_changed", EventParam.class);
102                                                                                assert param != null;
103                                                                                connection.subscribe(simulator.getTextual() + "/" + param.getId(), RemoteInstance.this, new LoggingSubscriptionCallback<Instance>(log, "server running state change", new EventCallback() {
104                                                                                        @Override
105                                                                                        public void call(List<File> files) {
106                                                                                                dispatch(new RunAt<Instance>() {
107                                                                                                        @Override
108                                                                                                        public void run() {
109                                                                                                                updateSimulationRunning();
110                                                                                                        }
111                                                                                                });
112                                                                                        }
113                                                                                }));
114                                                                                new PeriodicTask<Instance>(RemoteInstance.this, 1000) {
115                                                                                        @Override
116                                                                                        public void run() {
117                                                                                                updateSimulationRunning();
118                                                                                                again();
119                                                                                        }
120                                                                                };
121                                                                        }
122                                                                });
123                                                        }
124                                                });
125                                        }
126                                });
127                        }
128                });
129
130        }
131
132        @Override
133        public String toString() {
134                assert Dispatching.isThreadSafe();
135                return "remote instance " + getName() + "(" + getAddress() + ")";
136        }
137
138        public void setRunning(final boolean running) {
139                assert isActive();
140                //simulator.call(simulator.getParam(running ? "start" : "stop", ProcedureParam.class), new LoggingStateCallback(log, (running ? "starting" : "stopping") + " server"));
141        }
142
143        protected final UnaryListenersSet<Boolean> simulationRunningListeners = new UnaryListenersSet<Boolean>();
144
145        protected void updateSimulationRunning() {
146                assert isActive();
147                /*
148                fetchValue(simulator, getParam(simulator, "running", Param.class), new StateFunctor() {
149                        @Override
150                        public void call(Exception e) {
151                                if (e != null) {
152                                        log.fatal("failed to query simulator running status: " + e);
153                                        return;
154                                }
155
156                                invokeLater(new Runnable() {
157                                        @Override
158                                        public void run() {
159                                                boolean value = bindAccess(simulator).get("running", Boolean.class);
160                                                log.trace("server running: " + value);
161                                                simulationRunningListeners.call(value);
162                                        }
163                                });
164
165                        }
166                });
167                 */
168        }
169
170        public void addRunningStateListener(UnaryFunctor<Boolean, Boolean> listener) {
171                assert isActive();
172                simulationRunningListeners.add(listener);
173        }
174
175        // public void disconnect() {
176        //      assert isActive();
177        //      if (connection.isConnected()) {
178        //              Dispatching.stop(connection, this);
179        //      }
180        // }
181
182        public final ClientConnection getConnection() {
183                return connection;
184        }
185
186        @Override
187        public void fetchValue(final Path path, final Param param, final StateFunctor stateFunctor) {
188                assert isActive();
189                assert param != null;
190                assert path.isResolved();
191                connection.send(new GetRequest().field(param.getId()).path(path.getTextual()), this, new ResponseCallback<Instance>() {
192                        @Override
193                        public void process(Response response) {
194                                assert isActive();
195                                if (!response.getOk()) {
196                                        stateFunctor.call(new Exception(response.getComment()));
197                                        return;
198                                }
199                                try {
200                                        processFetchedValues(path, response.getFiles());
201                                        stateFunctor.call(null);
202                                } catch (Exception ex) {
203                                        stateFunctor.call(ex);
204                                }
205                        }
206                });
207        }
208
209        protected final Map<String, Set<Future<FramsClass>>> infoRequests = new HashMap<String, Set<Future<FramsClass>>>();
210
211        protected void finishInfoRequest(String id, FramsClass result, Exception e) {
212                assert isActive();
213                Set<Future<FramsClass>> futures = infoRequests.get(id);
214                infoRequests.remove(id);
215                for (Future<FramsClass> f : futures) {
216                        f.result(result, e);
217                }
218        }
219
220        @Override
221        protected void fetchInfo(final Path path, final Future<FramsClass> future) {
222
223                final String name = path.getTop().getParam().getContainedTypeName();
224
225                if (infoRequests.containsKey(name)) {
226                        infoRequests.get(name).add(future);
227                        return;
228                }
229
230                log.debug("issuing info request for " + name);
231                Set<Future<FramsClass>> futures = new HashSet<Future<FramsClass>>();
232                futures.add(future);
233                infoRequests.put(name, futures);
234
235                //TODO: if the info is in the cache, then don't communicate
236                connection.send(new InfoRequest().path(path.getTextual()), this, new ResponseCallback<Instance>() {
237                        @Override
238                        public void process(Response response) {
239                                assert isActive();
240                                if (!response.getOk()) {
241                                        finishInfoRequest(name, null, new Exception(response.getComment()));
242                                        return;
243                                }
244
245                                assert response.getFiles().size() == 1;
246                                assert path.isTheSame(response.getFiles().get(0).getPath());
247                                FramsClass framsClass;
248                                try {
249                                        framsClass = processFetchedInfo(response.getFiles().get(0));
250                                } catch (ConstructionException e) {
251                                        log.fatal("could not read class info");
252                                        finishInfoRequest(name, null, new Exception("could not read class info"));
253                                        return;
254                                }
255
256                                CompositeParam thisParam = path.getTop().getParam();
257                                if (!thisParam.isMatchingContainedName(framsClass.getId())) {
258                                        String mismatch = "class name mismatch: param=" + thisParam.getContainedTypeName() + " differs from fetched=" + framsClass.getId();
259                                        log.error(mismatch);
260                                        finishInfoRequest(name, null, new Exception(mismatch));
261                                        return;
262                                }
263                                finishInfoRequest(name, framsClass, null);
264                        }
265                });
266        }
267
268        @Override
269        public void fetchValues(final Path path, final StateFunctor stateFunctor) {
270                assert isActive();
271                assert path.getTop().getObject() != null;
272
273                log.trace("fetching values for " + path);
274                connection.send(new GetRequest().path(path.getTextual()), this, new ResponseCallback<Instance>() {
275                        @Override
276                        public void process(Response response) {
277                                assert isActive();
278                                if (!response.getOk()) {
279                                        stateFunctor.call(new Exception(response.getComment()));
280                                        return;
281                                }
282                                try {
283                                        processFetchedValues(path, response.getFiles());
284                                        stateFunctor.call(null);
285                                } catch (Exception ex) {
286                                        log.error("an exception occurred while loading: " + ex);
287                                        ex.printStackTrace();
288                                        stateFunctor.call(ex);
289                                }
290                        }
291                });
292        }
293
294        @Override
295        public void resolve(final Path path, final Future<Path> future) {
296                assert isActive();
297                if (path.getTop().getObject() != null) {
298                        if (getInfoFromCache(path) != null) {
299                                future.result(path, null);
300                                return;
301                        }
302                        findInfo(path, new Future<FramsClass>() {
303                                @Override
304                                public void result(FramsClass result, Exception e) {
305                                        if (e != null) {
306                                                future.result(null, e);
307                                                return;
308                                        }
309                                        future.result(path, null);
310                                }
311                        });
312                        return;
313                }
314                findInfo(path, new Future<FramsClass>() {
315                        @Override
316                        public void result(FramsClass result, Exception e) {
317                                assert isActive();
318                                if (e != null) {
319                                        future.result(null, e);
320                                        return;
321                                }
322                                assert path.getTop().getParam().isMatchingContainedName(result.getId());
323                                Path p = (path.getTop().getParam().getContainedTypeName() != null ? path : path.tryFindResolution());
324                                future.result(createIfNeeded(p), null);
325                        }
326                });
327        }
328
329        @Override
330        protected void tryRegisterOnChangeEvents(final Path path) {
331                assert isActive();
332                AccessInterface access = bindAccess(path);
333                if (!(access instanceof ListAccess)) {
334                        return;
335                }
336
337                assert path.size() >= 2;
338                FramsClass underFramsClass = getInfoFromCache(path.getUnder().getParam().getContainedTypeName());
339
340                EventParam changedEvent;
341                try {
342                        changedEvent = underFramsClass.getParamEntry(path.getTop().getParam().getId() + "_changed", EventParam.class);
343                } catch (FramsticksException e) {
344                        return;
345                }
346
347                log.debug("registering for " + changedEvent);
348                if (getSubscription(path) != null) {
349                        return;
350                }
351
352                final Pair<Path, Subscription<?>> temporary = new Pair<>(path, null);
353                subscriptions.add(temporary);
354
355                connection.subscribe(path.getTextual() + "_changed", this, new SubscriptionCallback<Instance>() {
356                        @Override
357                        public EventCallback subscribed(final Subscription<? super Instance> subscription) {
358                                if (subscription == null) {
359                                        log.error("failed to subscribe for change event for " + path);
360                                        return null;
361                                }
362                                log.debug("subscribed for change event for " + path);
363                                // subscription.setDispatcher(RemoteInstance.this);
364                                RemoteInstance.this.dispatch(new RunAt<Instance>() {
365                                        @Override
366                                        public void run() {
367                                                subscriptions.remove(temporary);
368                                                subscriptions.add(new Pair<Path, Subscription<?>>(path, subscription));
369                                        }
370                                });
371                                return new EventCallback() {
372                                        @Override
373                                        public void call(List<File> files) {
374                                                assert isActive();
375                                                assert files.size() == 1;
376                                                MultiParamLoader loader = new MultiParamLoader();
377                                                loader.setNewSource(files.get(0).getContent());
378                                                loader.addBreakCondition(MultiParamLoader.Status.AfterObject);
379                                                ReflectionAccess access = new ReflectionAccess(ListChange.class, FramsClass.build().forClass(ListChange.class));
380                                                loader.addAccessInterface(access);
381                                                MultiParamLoader.Status status;
382                                                try {
383                                                        while ((status = loader.go()) != MultiParamLoader.Status.Finished) {
384                                                                if (status == MultiParamLoader.Status.AfterObject) {
385                                                                        AccessInterface accessInterface = loader.getLastAccessInterface();
386                                                                        reactToChange(path, (ListChange) accessInterface.getSelected());
387                                                                }
388                                                        }
389                                                } catch (Exception e) {
390                                                        e.printStackTrace();
391                                                }
392                                        }
393                                };
394                        }
395                });
396        }
397
398        protected void reactToChange(final Path path, final ListChange listChange) {
399                assert isActive();
400                log.debug("reacting to change " + listChange + " in " + path);
401                AccessInterface access = bindAccess(path);
402                assert access != null;
403
404                if ((listChange.getAction() == ListChange.Action.Modify) && (listChange.getPosition() == -1)) {
405                        final String p = path.getTextual();
406                        resolveAndFetch(p, new Future<Path>() {
407                                @Override
408                                public void result(Path result, Exception e) {
409                                        if (e != null) {
410                                                log.error("failed to modify " + p + ": " + e);
411                                                return;
412                                        }
413                                        fireListChange(path, listChange);
414                                }
415                        });
416                        return;
417                }
418
419                CompositeParam childParam = Casting.tryCast(CompositeParam.class, access.getParam(listChange.getBestIdentifier()));
420                assert childParam != null;
421                switch (listChange.getAction()) {
422                case Add: {
423                        final String p = path.getTextual() + "/" + childParam.getId();
424                        resolveAndFetch(p, new Future<Path>() {
425                                @Override
426                                public void result(Path result, Exception e) {
427                                        if (e != null) {
428                                                log.error("failed to add " + p + ": " + e);
429                                                return;
430                                        }
431                                        log.debug("added: " + result);
432                                        fireListChange(path, listChange);
433                                }
434                        });
435                        break;
436                }
437                case Remove: {
438                        access.set(childParam, null);
439                        fireListChange(path, listChange);
440                        break;
441                }
442                case Modify: {
443                        final String p = path.getTextual() + "/" + childParam.getId();
444                        resolveAndFetch(p, new Future<Path>() {
445                                @Override
446                                public void result(Path result, Exception e) {
447                                        if (e != null) {
448                                                log.error("failed to modify " + p + ": " + e);
449                                                return;
450                                        }
451                                        fireListChange(path, listChange);
452                                }
453                        });
454                        break;
455                }
456                }
457        }
458
459        //TODO ValueParam
460        @Override
461        public void storeValue(final Path path, final Param param, final Object value, final StateFunctor stateFunctor) {
462                assert isActive();
463
464                log.trace("storing value " + param + " for " + path);
465                connection.send(new SetRequest().value(value.toString()).field(param.getId()).path(path.getTextual()), this, new StateCallback<Instance>() {
466                        @Override
467                        public void call(Exception e) {
468                                if (e == null) {
469                                        bindAccess(path).set((ValueParam) param, value);
470                                }
471                                stateFunctor.call(e);
472                        }
473                });
474        }
475
476        @Override
477        protected void joinableStart() {
478                Dispatching.use(connection, this);
479                super.joinableStart();
480        }
481
482        @Override
483        protected void joinableInterrupt() {
484                Dispatching.drop(connection, this);
485                super.joinableInterrupt();
486        }
487
488        @Override
489        protected void joinableFinish() {
490                super.joinableFinish();
491
492        }
493
494        @Override
495        public void joinableJoin() throws InterruptedException {
496                Dispatching.join(connection);
497                super.joinableJoin();
498        }
499
500        @Override
501        public void childChangedState(Joinable joinable, JoinableState state) {
502                proceedToState(state);
503        }
504
505        @Override
506        public void call(Path path, ProcedureParam param, Object[] arguments, StateFunctor stateFunctor) {
507                throw new UnimplementedException();
508        }
509
510}
Note: See TracBrowser for help on using the repository browser.