source: java/main/src/main/java/com/framsticks/remote/RemoteTree.java @ 98

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

HIGHLIGHTS:

CHANGELOG:
Get data also on tree expansion.

Use nice framstick icon for empty nodes.

Update panel after reload if it is current.

Add shallow reload procedure.

Cut Gui prefix from several tree classes.

Bring back counter of GuiTreeNode?.

Use IdentityHashMap? were it is more appriopriate.

Remove TreeListener?.

Do not use TreeListener? in GUI.

Minor change.

Done migration to GuiTreeModel?.

BrowserTest? in that version always crashes frams.linux.

Move rendering implementation into GuiAbstractNode?.

Use hand-crafted list in GuiTreeNode?.

Generally, it would be a great place for WeakIdentityHashMap?
(but there is none in Java Collection Framework).

Remove superfluous logging.

Fix bug in GuiTreeNode?.

Use IdentityHashMap? instead of HashMap?.

Improve structure update.

Filter out invalid uids in UniqueListAccess?.

Improve TreeCellRenderer?.

Add filtering in TrackConsole?.

Improve TreeModel?.

More changes.

More improvements.

More changes.

Remove TreeNode?.

Support MetaNode? in the GuiTreeModel?.

Implement more in GuiTreeModel?.

Add CompositeParam? interface to FramsClass? and AccessInterface?.

Allow access by number to UniqueList?.

Add UidComparator?.

Use TreeMap? as a default accessee in unique list.

It keeps order of keys.

Introduce classes to use with new TreeModel?.

Another step.

Migrate from TreeNode? to Node in many places.

Remove some uses of TreeNode? as DefaultMutableTreeNode?.

Remove Path from TreeNode? interface.

Remove Path from TreeNode?.

Add Path recration from node feature.

Reworking TreeCellRenderer?.

Minor change of TreeOperations? interface.

Remove last methods from TreeNode?.

Another minor step.

Do not store reference to TreeAtFrame? in TreeNode?.

Add proxy exceptionHandler to StatusBar?.

Move panels management to TreeAtFrame?.

Store localChanges in the NodeAtFrame?.

More cleanup.

Move name computing to TreeCellRenderer?.

Move tooltip and icon computations to TreeCellRenderer?.

More dispatches removed.

Remove most dispatching from TreeNode?.

TreeNode? does not actually redispatch tasks.

Make Tree embedded in Browser use SwingDispatcher?.

Make lazy binding of Tree with Dispatcher.

Minor changes.

Organizational change in AbstractTree?.

Make AbstractTree? compose from Thread instead of inherit from it.

Make SwingDispatcher? and AtOnceDispatcher? Joinable compatible.

Add ListPanelProvider?.

Improve Controls readonly and enabled handling.

Properly pass ExceptionHandlers? in more places.

Make Tree.get accept ValueParam?.

  • This is to allow access number of list elements.

Remove not needed get redirection in ClientAtServer?.

Rename tryResolve to tryGet.

Unify tryResolveAndGet into tryResolve.

Remove resolveTop from Tree interface.

Make Tree.get accept Future<Path>.

Use get to implement resolveTop also in ObjectTree?.

Unify resolveTop and get in RemoteTree?.

Another minor step.

More minor changes in tree operations.

Minor organizational changes.

In RemoteTree? first fetch info for root.

Reworking resolving.

Minor changes.

Make ListAccess? return proxy iterators (instead of creating temporary collection).

Let AccessInterface? return Iterable<Param>.

Improve resolving.

More improvements.

First working completion in ManagedConsole?.

Rename resolve to resolveTop.

This reflects the actuall functionality.

Change semantic of tryResolve and tryResolveAndGet.

File size: 12.8 KB
Line 
1package com.framsticks.remote;
2
3import com.framsticks.communication.*;
4import com.framsticks.communication.queries.CallRequest;
5import com.framsticks.communication.queries.GetRequest;
6import com.framsticks.communication.queries.InfoRequest;
7import com.framsticks.communication.queries.SetRequest;
8import com.framsticks.core.AbstractTree;
9import com.framsticks.core.Mode;
10import com.framsticks.core.ListChange;
11import com.framsticks.core.Path;
12import com.framsticks.params.*;
13import com.framsticks.params.annotations.AutoAppendAnnotation;
14import com.framsticks.params.annotations.FramsClassAnnotation;
15import com.framsticks.params.annotations.ParamAnnotation;
16import com.framsticks.params.types.EventParam;
17import com.framsticks.params.types.ProcedureParam;
18import com.framsticks.parsers.MultiParamLoader;
19import com.framsticks.core.Tree;
20import com.framsticks.util.*;
21import com.framsticks.util.dispatching.Dispatching;
22import com.framsticks.util.dispatching.Dispatching.DispatcherWaiter;
23import com.framsticks.util.dispatching.Future;
24import com.framsticks.util.dispatching.FutureHandler;
25import com.framsticks.util.dispatching.Joinable;
26import com.framsticks.util.dispatching.JoinableParent;
27import com.framsticks.util.dispatching.JoinableState;
28import com.framsticks.util.dispatching.ThrowExceptionHandler;
29import com.framsticks.util.lang.Casting;
30import com.framsticks.util.lang.Pair;
31import com.framsticks.util.dispatching.RunAt;
32import static com.framsticks.core.TreeOperations.*;
33
34import java.util.*;
35
36import javax.annotation.Nonnull;
37
38import org.apache.log4j.Logger;
39
40/**
41 * @author Piotr Sniegowski
42 */
43@FramsClassAnnotation
44public class RemoteTree extends AbstractTree implements JoinableParent {
45
46        private final static Logger log = Logger.getLogger(RemoteTree.class);
47
48        protected ClientSideManagedConnection connection;
49
50        protected final Set<Pair<Path, Subscription<?>>> subscriptions = new HashSet<>();
51
52        public Pair<Path, Subscription<?>> getSubscription(Path path) {
53                for (Pair<Path, Subscription<?>> s : subscriptions) {
54                        if (s.first.isTheSameObjects(path)) {
55                                return s;
56                        }
57                }
58                return null;
59        }
60
61        public RemoteTree() {
62        }
63
64        @ParamAnnotation
65        public void setAddress(String address) {
66                setConnection(Connection.to(new ClientSideManagedConnection(), new Address(address)));
67        }
68
69        @ParamAnnotation
70        public String getAddress() {
71                return connection == null ? "<disconnected>" : connection.getAddress().toString();
72        }
73
74        @AutoAppendAnnotation
75        public void setConnection(final ClientSideManagedConnection connection) {
76                this.connection = connection;
77        }
78
79        public final ClientSideManagedConnection getConnection() {
80                return connection;
81        }
82
83        @Override
84        public String toString() {
85                assert Dispatching.isThreadSafe();
86                return "remote tree " + getName() + "(" + getAddress() + ")";
87        }
88
89        @Override
90        public void get(final Path path, final ValueParam param, Mode mode, final Future<Object> future) {
91                assert isActive();
92                assert param != null;
93                // assert path.isResolved();
94                //TODO only do that if needed
95                connection.send(new GetRequest().field(param.getId()).path(path.getTextual()), this, new ClientSideResponseFuture(future) {
96                        @Override
97                        protected void processOk(Response response) {
98                                assert isActive();
99                                processFetchedValues(path, response.getFiles());
100                                future.pass(bindAccess(path.tryResolveIfNeeded()).get(param, Object.class));
101                        }
102                });
103        }
104
105        protected final Map<String, Set<Future<FramsClass>>> infoRequests = new HashMap<String, Set<Future<FramsClass>>>();
106
107
108        @Override
109        public void info(final Path path, final Future<FramsClass> future) {
110
111                final String name = path.getTop().getParam().getContainedTypeName();
112
113                if (infoRequests.containsKey(name)) {
114                        infoRequests.get(name).add(future);
115                        return;
116                }
117
118                log.debug("issuing info request for " + name);
119                final Set<Future<FramsClass>> futures = new HashSet<Future<FramsClass>>();
120                futures.add(future);
121                infoRequests.put(name, futures);
122
123                final Future<FramsClass> compositeFuture = new Future<FramsClass>() {
124
125                        @Override
126                        public void handle(FramsticksException exception) {
127                                assert isActive();
128                                infoRequests.remove(name);
129                                for (Future<FramsClass> f : futures) {
130                                        f.handle(exception);
131                                }
132                        }
133
134                        @Override
135                        protected void result(FramsClass framsClass) {
136                                assert isActive();
137                                infoRequests.remove(name);
138                                for (Future<FramsClass> f : futures) {
139                                        f.pass(framsClass);
140                                }
141                        }
142                };
143
144                //TODO: if the info is in the cache, then don't communicate
145                connection.send(new InfoRequest().path(path.getTextual()), this, new ClientSideResponseFuture(compositeFuture) {
146                        @Override
147                        protected void processOk(Response response) {
148                                assert isActive();
149
150                                if (response.getFiles().size() != 1) {
151                                        throw new FramsticksException().msg("invalid number of files in response").arg("files", response.getFiles().size());
152                                }
153                                if (!path.isTheSame(response.getFiles().get(0).getPath())) {
154                                        throw new FramsticksException().msg("path mismatch").arg("returned path", response.getFiles().get(0).getPath());
155                                }
156                                FramsClass framsClass = processFetchedInfo(RemoteTree.this, response.getFiles().get(0));
157
158                                CompositeParam thisParam = path.getTop().getParam();
159                                if (!thisParam.isMatchingContainedName(framsClass.getId())) {
160                                        throw new FramsticksException().msg("framsclass id mismatch").arg("request", thisParam.getContainedTypeName()).arg("fetched", framsClass.getId());
161                                }
162                                compositeFuture.pass(framsClass);
163                        }
164                });
165        }
166
167        @Override
168        public void get(final Path path, final Mode mode, final Future<Path> future) {
169                assert isActive();
170
171                log.trace("fetching values for " + path);
172                findInfo(path, new FutureHandler<FramsClass>(future) {
173                        @Override
174                        protected void result(FramsClass result) {
175
176                                connection.send(new GetRequest().path(path.getTextual()), RemoteTree.this, new ClientSideResponseFuture(future) {
177                                        @Override
178                                        protected void processOk(Response response) {
179                                                assert isActive();
180                                                Path p = path.tryResolveIfNeeded();
181                                                processFetchedValues(p, response.getFiles());
182                                                future.pass(p.tryResolveIfNeeded().assureResolved());
183                                        }
184                                });
185                        }
186                });
187        }
188
189
190        @Override
191        protected void tryRegisterOnChangeEvents(final Path path) {
192                assert isActive();
193                AccessInterface access = bindAccess(path);
194                if (!(access instanceof ListAccess)) {
195                        return;
196                }
197
198                assert path.size() >= 2;
199                FramsClass underFramsClass = getInfoFromCache(path.getUnder().getParam().getContainedTypeName());
200
201                EventParam changedEvent;
202                try {
203                        changedEvent = underFramsClass.getParamEntry(path.getTop().getParam().getId() + "_changed", EventParam.class);
204                } catch (FramsticksException e) {
205                        return;
206                }
207
208                log.debug("registering for " + changedEvent);
209                if (getSubscription(path) != null) {
210                        return;
211                }
212
213                final Pair<Path, Subscription<?>> temporary = new Pair<>(path, null);
214                subscriptions.add(temporary);
215
216                connection.subscribe(path.getTextual() + "_changed", this, new SubscriptionCallback<Tree>() {
217                        @Override
218                        public EventCallback subscribed(final Subscription<? super Tree> subscription) {
219                                if (subscription == null) {
220                                        log.error("failed to subscribe for change event for " + path);
221                                        return null;
222                                }
223                                log.debug("subscribed for change event for " + path);
224                                // subscription.setDispatcher(RemoteInstance.this);
225                                RemoteTree.this.dispatch(new RunAt<Tree>(this) {
226                                        @Override
227                                        protected void runAt() {
228                                                subscriptions.remove(temporary);
229                                                subscriptions.add(new Pair<Path, Subscription<?>>(path, subscription));
230                                        }
231                                });
232                                return new EventCallback() {
233                                        @Override
234                                        public void call(List<File> files) {
235                                                assert isActive();
236                                                assert files.size() == 1;
237                                                final MultiParamLoader loader = new MultiParamLoader();
238                                                loader.setNewSource(files.get(0).getContent());
239                                                loader.addBreakCondition(MultiParamLoader.Status.AfterObject);
240                                                loader.addListener(MultiParamLoader.Status.OnComment, new MultiParamLoader.StatusListener() {
241
242                                                        @Override
243                                                        public void onStatusChange() {
244                                                                throw new FramsticksException().msg("multi param loader error").arg("line", loader.getCurrentLine());
245                                                        }
246                                                });
247                                                ReflectionAccess access = new ReflectionAccess(ListChange.class, FramsClass.build().forClass(ListChange.class));
248                                                loader.addAccessInterface(access);
249
250                                                MultiParamLoader.Status status;
251                                                while ((status = loader.go()) != MultiParamLoader.Status.Finished) {
252                                                        if (status == MultiParamLoader.Status.AfterObject) {
253                                                                AccessInterface accessInterface = loader.getLastAccessInterface();
254                                                                reactToChange(path, (ListChange) accessInterface.getSelected());
255                                                        }
256                                                }
257                                        }
258                                };
259                        }
260                });
261        }
262
263        protected Future<Path> futureListChanger(final ListChange listChange, final String path) {
264                return new FutureHandler<Path>(Logging.logger(log, "failed to " + listChange, path)) {
265                        @Override
266                        protected void result(Path result) {
267                                log.debug(listChange + ": " + result);
268                        }
269                };
270        }
271
272        protected void reactToChange(final Path path, final ListChange listChange) {
273                assert isActive();
274                log.debug("reacting to change " + listChange + " in " + path);
275                AccessInterface access = bindAccess(path);
276                assert access != null;
277
278                if ((listChange.getAction() == ListChange.Action.Modify) && (listChange.getPosition() == -1)) {
279                        final String p = path.getTextual();
280                        tryGet(this, p, futureListChanger(listChange, p));
281                        return;
282                }
283
284                CompositeParam childParam = Casting.tryCast(CompositeParam.class, access.getParam(listChange.getBestIdentifier()));
285                assert childParam != null;
286                switch (listChange.getAction()) {
287                        case Add: {
288                                final String p = path.getTextual() + "/" + childParam.getId();
289                                tryGet(this, p, futureListChanger(listChange, p));
290                                break;
291                        }
292                        case Remove: {
293                                access.set(childParam, null);
294                                break;
295                        }
296                        case Modify: {
297                                final String p = path.getTextual() + "/" + childParam.getId();
298                                tryGet(this, p, futureListChanger(listChange, p));
299                                break;
300                        }
301                }
302        }
303
304        @Override
305        public void set(final Path path, final PrimitiveParam<?> param, final Object value, final Future<Integer> future) {
306                assert isActive();
307                final Integer flag = bindAccess(path).set(param, value);
308
309                log.trace("storing value " + param + " for " + path);
310                //TODO break in passing exception handler is here
311                connection.send(new SetRequest().value(value.toString()).field(param.getId()).path(path.getTextual()), this, new ClientSideResponseFuture(future) {
312                        @Override
313                        protected void processOk(Response response) {
314                                future.pass(flag);
315                        }
316                });
317        }
318
319        @Override
320        protected void joinableStart() {
321                Dispatching.use(connection, this);
322                super.joinableStart();
323
324                dispatch(new RunAt<RemoteTree>(ThrowExceptionHandler.getInstance()) {
325                        @Override
326                        protected void runAt() {
327                                final DispatcherWaiter<Tree, Tree> waiter = new DispatcherWaiter<Tree, Tree>(RemoteTree.this);
328
329                                connection.send(new InfoRequest().path("/"), waiter, new ClientSideResponseFuture(this) {
330                                        @Override
331                                        protected void processOk(Response response) {
332                                                FramsClass framsClass = processFetchedInfo(RemoteTree.this, response.getFiles().get(0));
333                                                assignRootParam(Param.build().name("Tree").id(RemoteTree.this.getName()).type("o " + framsClass.getId()).finish(CompositeParam.class));
334                                        }
335                                });
336
337                                waiter.waitFor();
338                        }
339                });
340        }
341
342        @Override
343        protected void joinableInterrupt() {
344                Dispatching.drop(connection, this);
345                super.joinableInterrupt();
346        }
347
348        @Override
349        protected void joinableFinish() {
350                super.joinableFinish();
351
352        }
353
354        @Override
355        public void joinableJoin() throws InterruptedException {
356                Dispatching.join(connection);
357                super.joinableJoin();
358        }
359
360        @Override
361        public void childChangedState(Joinable joinable, JoinableState state) {
362                proceedToState(state);
363        }
364
365        @Override
366        public void call(@Nonnull final Path path, @Nonnull final ProcedureParam procedure, @Nonnull Object[] arguments, final Future<Object> future) {
367                assert isActive();
368                assert path.isResolved();
369
370                //TODO validate arguments type using params
371                connection.send(new CallRequest().procedure(procedure.getId()).arguments(Arrays.asList(arguments)).path(path.getTextual()), this, new ClientSideResponseFuture(future) {
372                        @Override
373                        protected void processOk(Response response) {
374                                assert isActive();
375                                // InstanceUtils.processFetchedValues(path, response.getFiles());
376                                future.pass(null);
377                        }
378                });
379
380        }
381
382        // @Override
383        // public Path create(Path path) {
384        //      assert isActive();
385        //      assert !path.isResolved();
386        //      Path resolved = path.tryFindResolution();
387        //      if (!resolved.isResolved()) {
388        //              log.debug("creating: " + path);
389        //              //TODO: access parent here, check if it is valid, only then create
390        //              AccessInterface access = registry.prepareAccess(path.getTop().getParam());
391        //              assert access != null;
392        //              Object child = access.createAccessee();
393        //              assert child != null;
394        //              if (path.size() == 1) {
395        //                      setRoot(new Node(getRoot().getParam(), child));
396        //              } else {
397        //                      bindAccess(this, path.getUnder()).set(path.getTop().getParam(), child);
398        //              }
399        //              resolved = path.appendResolution(child);
400        //      }
401        //      tryRegisterOnChangeEvents(resolved);
402        //      return resolved;
403        // }
404
405}
Note: See TracBrowser for help on using the repository browser.