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

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

HIGHLIGHTS:

  • add <include/> to configuration
  • add side notes to tree
    • used to store arbitrary information alongside the tree structure
  • migrate to log4j2
    • supports lazy string evaluation of passed arguments
  • improve GUI tree
    • it stays in synchronization with actual state (even in high load test scenario)
  • improve panel management in GUI
  • make loading objects in GUI more lazy
  • offload parsing to connection receiver thread
    • info parsing
    • first step of objects parsing
  • fix connection parsing bug (eof in long values)
  • support zero-arguments procedure in table view

CHANGELOG:
Implement procedure calls from table view.

Refactorization around procedures in tables.

Add table editor for buttons.

Render buttons in the the list view.

Further improve Columns.

Add Column class for TableModel?.

Accept also non-arguments ProcedureParams? in tableView.

Increase maximal TextAreaControl? size.

Add tooltip to ProcedureControl?.

Fix bug of interpreting eofs in long values by connection reader.

Further rework connection parsing.

Simplify client connection processing.

Test ListChange? modification.

Test ListChange? events with java server.

Add TestChild?.

Fix bug with fast deregistering when connecting to running server.

Another minor refactorization in TreeOperations?.

Fix bug in SimpleAbstractAccess? loading routine.

Another minor improvement.

Minor change.

Make reading of List objects two-phase.

Another minor change.

Dispatch parsing into receiver thread.

Another step.

Enclose passing value in ObjectParam? case in closure.

Minor step.

Minor change on way to offload parsing.

Temporarily comment out single ValueParam? get.

It will be generalized to multi ValueParam?.

Process info in receiver thread.

Add DispatchingExceptionHandler?.

Make waits in browser test longer.

Use FETCHED_MARK.

It is honored in GUI, where it used to decide whether to get values

after user action.

It is set in standard algorithm for processing fetched values.

Add remove operation to side notes.

Make loading more lazy.

Improve loading policy.

On node choose load itself, on node expansion, load children.

Minor improvement.

Fix bug with panel interleaving.

Minor improvements.

Improve panel management.

More cleaning around panels.

Reorganize panels.

Further improve tree.

Fix bug in TreeModel?.

Remove children from TreeNode?.

Implement TreeNode? hashCode and equals.

Make TreeNode? delegate equals and hashcode to internal reference.

Move listeners from TreeNode? to side notes.

Store path.textual as a side note.

Side note params instead of accesses for objects.

More refactorizations.

In TreeNode? bindAccess based on side notes.

Minor step.

Hide createAccess.

Rename AccessInterface? to Access.

Minor changes.

Several improvements in high load scenarios.

Change semantics of ArrayListAccess?.set(index, null);

It now removes the element, making list shorter
(it was set to null before).

Add path remove handler.

Handle exceptions in Connection.

Update .gitignore

Configure logging to file.

Move registration to TreeModel?.

Further refactorization.

Minor refactorization.

Minor improvements.

Use specialized event also for Modify action of ListChange?.

Use remove events.

Use the insertion events for tree.

Further improve tree refreshing.

Further improve reacting on events in GUI.

Fix problem with not adding objects on addition list change.

Migrate to log4j lazy String construction interface.

Migrate imports to log4j2.

Drop dependency on adapter to version 1.2.

Switch log4j implementation to log4j2.

Add dirty mark to the NodeAtFrame?.

Make selecting in AccessInterfaces? type safe.

Ignore containers size settings in Model and Genotype.

Use tree side notes to remember local changes and panels.

Add sideNotes to tree.

They will be used to store various accompanying information
right in the tree.

Use ReferenceIdentityMap? from apache in TreeNode?.

It suits the need perfectly (weak semantics on both key and value).

Make ArrayListParam? do not react size changes.

Guard in TableModel? before not yet loaded objects.

Add <include/> clause and AutoInjector?.

Extract common columns configuration to separate xml,
that can be included by other configurations.

File size: 11.1 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.Path;
10import com.framsticks.params.*;
11import com.framsticks.params.EventListener;
12import com.framsticks.params.annotations.AutoAppendAnnotation;
13import com.framsticks.params.annotations.FramsClassAnnotation;
14import com.framsticks.params.annotations.ParamAnnotation;
15import com.framsticks.params.types.EventParam;
16import com.framsticks.params.types.ProcedureParam;
17import com.framsticks.parsers.Loaders;
18import com.framsticks.parsers.MultiParamLoader;
19import com.framsticks.core.Tree;
20import com.framsticks.util.*;
21import com.framsticks.util.dispatching.AtOnceDispatcher;
22import com.framsticks.util.dispatching.Dispatching;
23import com.framsticks.util.dispatching.Dispatching.DispatcherWaiter;
24import com.framsticks.util.dispatching.DispatchingFuture;
25import com.framsticks.util.dispatching.ExceptionResultHandler;
26import com.framsticks.util.dispatching.Future;
27import com.framsticks.util.dispatching.FutureHandler;
28import com.framsticks.util.dispatching.Joinable;
29import com.framsticks.util.dispatching.JoinableParent;
30import com.framsticks.util.dispatching.JoinableState;
31import com.framsticks.util.dispatching.ThrowExceptionHandler;
32import com.framsticks.util.lang.Casting;
33import com.framsticks.util.dispatching.RunAt;
34import static com.framsticks.core.TreeOperations.*;
35
36import java.util.*;
37
38import javax.annotation.Nonnull;
39
40import org.apache.logging.log4j.Logger;
41import org.apache.logging.log4j.LogManager;
42
43/**
44 * @author Piotr Sniegowski
45 */
46@FramsClassAnnotation
47public final class RemoteTree extends AbstractTree implements JoinableParent {
48
49        private final static Logger log = LogManager.getLogger(RemoteTree.class);
50
51        protected ClientSideManagedConnection connection;
52
53        public RemoteTree() {
54        }
55
56        @ParamAnnotation
57        public void setAddress(String address) {
58                setConnection(Connection.to(new ClientSideManagedConnection(), new Address(address)));
59        }
60
61        @ParamAnnotation
62        public String getAddress() {
63                return connection == null ? "<disconnected>" : connection.getAddress().toString();
64        }
65
66        @AutoAppendAnnotation
67        public void setConnection(final ClientSideManagedConnection connection) {
68                this.connection = connection;
69                this.connection.setExceptionHandler(this);
70        }
71
72        public final ClientSideManagedConnection getConnection() {
73                return connection;
74        }
75
76        @Override
77        public String toString() {
78                assert Dispatching.isThreadSafe();
79                return "remote tree " + getName() + "(" + getAddress() + ")";
80        }
81
82        protected ExceptionResultHandler pathRemoveHandler(final Path path, final ExceptionResultHandler handler) {
83                return new ExceptionResultHandler() {
84
85                        @Override
86                        public void handle(final FramsticksException exception) {
87                                Dispatching.dispatchIfNotActive(RemoteTree.this, new RunAt<RemoteTree>(RemoteTree.this) {
88
89                                        @Override
90                                        protected void runAt() {
91                                                assert path.getTree().isActive();
92                                                log.info("path is invalid (removing): {}", path);
93                                                bindAccess(path.getUnder()).set(path.getTop().getParam(), null);
94                                                handler.handle(exception);
95                                        }
96                                });
97                        }
98                };
99        }
100
101        // @Override
102        // public void get(final Path path, final ValueParam param, final Future<Object> future) {
103        //      assert isActive();
104        //      assert param != null;
105        //      // assert path.isResolved();
106        //      //TODO only do that if needed
107        //      connection.send(new GetRequest().field(param.getId()).path(path.getTextual()), this, new ClientSideResponseFuture(pathRemoveHandler(path, future)) {
108        //              @Override
109        //              protected void processOk(Response response) {
110        //                      assert isActive();
111        //                      processFetchedValues(path, response.getFiles());
112        //                      future.pass(bindAccess(path.tryResolveIfNeeded()).get(param, Object.class));
113        //              }
114        //      });
115        // }
116
117        protected final Map<String, Set<Future<FramsClass>>> infoRequests = new HashMap<String, Set<Future<FramsClass>>>();
118
119
120        @Override
121        public void info(final Path path, final Future<FramsClass> future) {
122
123                final String name = path.getTop().getParam().getContainedTypeName();
124
125                if (infoRequests.containsKey(name)) {
126                        infoRequests.get(name).add(future);
127                        return;
128                }
129
130                log.debug("issuing info request for {}", name);
131                final Set<Future<FramsClass>> futures = new HashSet<Future<FramsClass>>();
132                futures.add(future);
133                infoRequests.put(name, futures);
134
135                final Future<FramsClass> compositeFuture = DispatchingFuture.create(this, new Future<FramsClass>() {
136
137                        @Override
138                        public void handle(FramsticksException exception) {
139                                assert isActive();
140                                infoRequests.remove(name);
141                                for (Future<FramsClass> f : futures) {
142                                        f.handle(exception);
143                                }
144                        }
145
146                        @Override
147                        protected void result(FramsClass framsClass) {
148                                assert isActive();
149                                putInfoIntoCache(framsClass);
150                                infoRequests.remove(name);
151                                for (Future<FramsClass> f : futures) {
152                                        f.pass(framsClass);
153                                }
154                        }
155                });
156
157                //TODO: if the info is in the cache, then don't communicate
158                connection.send(new InfoRequest().path(path.getTextual()), AtOnceDispatcher.getInstance(), new ClientSideResponseFuture(compositeFuture) {
159                        @Override
160                        protected void processOk(Response response) {
161                                assert connection.getReceiverDispatcher().isActive();
162
163                                if (response.getFiles().size() != 1) {
164                                        throw new FramsticksException().msg("invalid number of files in response").arg("files", response.getFiles().size());
165                                }
166                                if (!path.isTheSame(response.getFiles().get(0).getPath())) {
167                                        throw new FramsticksException().msg("path mismatch").arg("returned path", response.getFiles().get(0).getPath());
168                                }
169                                FramsClass framsClass = Loaders.loadFramsClass(response.getFiles().get(0).getContent());
170
171                                CompositeParam thisParam = path.getTop().getParam();
172                                if (!thisParam.isMatchingContainedName(framsClass.getId())) {
173                                        throw new FramsticksException().msg("framsclass id mismatch").arg("request", thisParam.getContainedTypeName()).arg("fetched", framsClass.getId());
174                                }
175                                compositeFuture.pass(framsClass);
176                        }
177                });
178        }
179
180        @Override
181        public void get(final Path path, final Future<Path> future) {
182                assert isActive();
183                final ExceptionResultHandler remover = pathRemoveHandler(path, future);
184
185                log.trace("fetching values for {}", path);
186                findInfo(path, new FutureHandler<FramsClass>(remover) {
187                        @Override
188                        protected void result(FramsClass result) {
189
190                                final Access access = registry.prepareAccess(path.getTop().getParam());
191                                connection.send(new GetRequest().path(path.getTextual()), AtOnceDispatcher.getInstance(), new ClientSideResponseFuture(remover) {
192                                        @Override
193                                        protected void processOk(Response response) {
194                                                processFetchedValues(path, response.getFiles(), access, future);
195                                        }
196                                });
197                        }
198                });
199        }
200
201        @Override
202        public void set(final Path path, final PrimitiveParam<?> param, final Object value, final Future<Integer> future) {
203                assert isActive();
204                final Integer flag = bindAccess(path).set(param, value);
205
206                log.trace("storing value {} for {}", param, path);
207                //TODO break in passing exception handler is here
208                connection.send(new SetRequest().value(value.toString()).field(param.getId()).path(path.getTextual()), this, new ClientSideResponseFuture(future) {
209                        @Override
210                        protected void processOk(Response response) {
211                                future.pass(flag);
212                        }
213                });
214        }
215
216        @Override
217        protected void joinableStart() {
218                Dispatching.use(connection, this);
219                super.joinableStart();
220
221                dispatch(new RunAt<RemoteTree>(ThrowExceptionHandler.getInstance()) {
222                        @Override
223                        protected void runAt() {
224                                final DispatcherWaiter<Tree, Tree> waiter = new DispatcherWaiter<Tree, Tree>(RemoteTree.this);
225
226                                connection.send(new InfoRequest().path("/"), waiter, new ClientSideResponseFuture(this) {
227                                        @Override
228                                        protected void processOk(Response response) {
229                                                FramsClass framsClass = processFetchedInfo(RemoteTree.this, response.getFiles().get(0));
230                                                assignRootParam(Param.build().name("Tree").id(RemoteTree.this.getName()).type("o " + framsClass.getId()).finish(CompositeParam.class));
231                                        }
232                                });
233
234                                waiter.waitFor();
235                        }
236                });
237        }
238
239        @Override
240        protected void joinableInterrupt() {
241                Dispatching.drop(connection, this);
242                super.joinableInterrupt();
243        }
244
245        @Override
246        protected void joinableFinish() {
247                super.joinableFinish();
248
249        }
250
251        @Override
252        public void joinableJoin() throws InterruptedException {
253                Dispatching.join(connection);
254                super.joinableJoin();
255        }
256
257        @Override
258        public void childChangedState(Joinable joinable, JoinableState state) {
259                proceedToState(state);
260        }
261
262        @Override
263        public void call(@Nonnull final Path path, @Nonnull final ProcedureParam procedure, @Nonnull Object[] arguments, final Future<Object> future) {
264                assert isActive();
265                assert path.isResolved();
266
267                //TODO validate arguments type using params
268                connection.send(new CallRequest().procedure(procedure.getId()).arguments(Arrays.asList(arguments)).path(path.getTextual()), this, new ClientSideResponseFuture(future) {
269                        @Override
270                        protected void processOk(Response response) {
271                                assert isActive();
272                                // InstanceUtils.processFetchedValues(path, response.getFiles());
273                                future.pass(null);
274                        }
275                });
276
277        }
278
279        protected final Map<EventListener<?>, EventListener<File>> proxyListeners = new IdentityHashMap<>();
280
281        public <A> void addListener(Path path, EventParam param, final EventListener<A> listener, final Class<A> argumentType, final Future<Void> future) {
282                assert isActive();
283                assert path.isResolved();
284                if ((!argumentType.equals(Object.class)) && (null == registry.getFramsClassForJavaClass(argumentType))) {
285                        registry.registerAndBuild(argumentType);
286                }
287
288                final EventListener<File> proxyListener = new EventListener<File>() {
289
290                        @Override
291                        public void action(final File file) {
292                                Dispatching.dispatchIfNotActive(RemoteTree.this, new RunAt<RemoteTree>(RemoteTree.this) {
293
294                                        @Override
295                                        protected void runAt() {
296                                                assert isActive();
297                                                if (argumentType.equals(Object.class)) {
298                                                        listener.action(Casting.tryCast(argumentType, file));
299                                                        return;
300                                                }
301                                                Access access = registry.createAccess(argumentType);
302                                                Object argument = access.createAccessee();
303                                                access.select(argument);
304                                                if (!argumentType.isInstance(argument)) {
305                                                        throw new FramsticksException().msg("created argument is of wrond type").arg("expected", argumentType).arg("created", argument.getClass());
306                                                }
307                                                A typedArgument = argumentType.cast(argument);
308
309                                                // log.info("executing event with argument {}", argumentType);
310                                                MultiParamLoader loader = new MultiParamLoader();
311                                                loader.setNewSource(file.getContent());
312                                                loader.addBreakCondition(MultiParamLoader.Status.AfterObject);
313                                                loader.addAccess(access);
314                                                loader.go();
315
316                                                listener.action(typedArgument);
317                                        }
318                                });
319                        }
320                };
321
322                proxyListeners.put(listener, proxyListener);
323
324                connection.addListener(Path.appendString(path.getTextual(), param.getId()), proxyListener, this, future);
325        }
326
327        public void removeListener(Path path, EventParam param, EventListener<?> listener, Future<Void> future) {
328                assert isActive();
329                EventListener<File> proxyListener = proxyListeners.get(listener);
330                connection.removeListener(proxyListener, this, future);
331        }
332
333}
Note: See TracBrowser for help on using the repository browser.