1 /*******************************************************************************
\r
2 * Copyright (c) 2010- Association for Decentralized Information Management in
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.databoard.accessor.wire;
\r
14 import java.util.ArrayList;
\r
15 import java.util.Collection;
\r
16 import java.util.Collections;
\r
17 import java.util.HashMap;
\r
18 import java.util.LinkedList;
\r
19 import java.util.List;
\r
20 import java.util.Map;
\r
22 import org.simantics.databoard.Bindings;
\r
23 import org.simantics.databoard.Methods;
\r
24 import org.simantics.databoard.accessor.Accessor;
\r
25 import org.simantics.databoard.accessor.Accessor.Listener;
\r
26 import org.simantics.databoard.accessor.ArrayAccessor;
\r
27 import org.simantics.databoard.accessor.MapAccessor;
\r
28 import org.simantics.databoard.accessor.OptionalAccessor;
\r
29 import org.simantics.databoard.accessor.RecordAccessor;
\r
30 import org.simantics.databoard.accessor.UnionAccessor;
\r
31 import org.simantics.databoard.accessor.error.AccessorConstructionException;
\r
32 import org.simantics.databoard.accessor.error.AccessorException;
\r
33 import org.simantics.databoard.accessor.event.Event;
\r
34 import org.simantics.databoard.accessor.impl.AccessorParams;
\r
35 import org.simantics.databoard.accessor.interestset.InterestSet;
\r
36 import org.simantics.databoard.accessor.reference.ChildReference;
\r
37 import org.simantics.databoard.adapter.AdaptException;
\r
38 import org.simantics.databoard.annotations.Optional;
\r
39 import org.simantics.databoard.binding.ArrayBinding;
\r
40 import org.simantics.databoard.binding.Binding;
\r
41 import org.simantics.databoard.binding.MapBinding;
\r
42 import org.simantics.databoard.binding.RecordBinding;
\r
43 import org.simantics.databoard.binding.UnionBinding;
\r
44 import org.simantics.databoard.binding.error.BindingConstructionException;
\r
45 import org.simantics.databoard.binding.impl.ObjectArrayBinding;
\r
46 import org.simantics.databoard.binding.mutable.MutableVariant;
\r
47 import org.simantics.databoard.method.MethodInterface;
\r
48 import org.simantics.databoard.method.MethodInterface.Method;
\r
49 import org.simantics.databoard.method.MethodNotSupportedException;
\r
50 import org.simantics.databoard.method.MethodTypeBinding;
\r
51 import org.simantics.databoard.method.TcpConnection;
\r
52 import org.simantics.databoard.method.TcpConnection.ConnectionListener;
\r
53 import org.simantics.databoard.type.ArrayType;
\r
54 import org.simantics.databoard.type.Component;
\r
55 import org.simantics.databoard.type.Datatype;
\r
56 import org.simantics.databoard.type.MapType;
\r
57 import org.simantics.databoard.type.UnionType;
\r
58 import org.simantics.databoard.util.BijectionMap;
\r
61 * WireServer exposes an accessor over TCP/IP socket.
\r
63 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
\r
65 public class WireServer implements IWireServer {
\r
68 Map<TcpConnection, ClientRecord> clients = Collections.synchronizedMap( new HashMap<TcpConnection, ClientRecord>() );
\r
70 AccessorParams params = AccessorParams.DEFAULT;
\r
72 public WireServer(Accessor accessor) {
\r
73 this.accessor = accessor;
\r
75 this.mi = Methods.bindInterface(IWireServer.class, this);
\r
76 } catch (BindingConstructionException e) {
\r
77 throw new RuntimeException(e);
\r
81 public MethodInterface getMethodInterface() {
\r
85 static class ClientRecord {
\r
86 TcpConnection connection;
\r
87 MethodInterface clientMethodInterface;
\r
88 Method onEventsMethod;
\r
89 IWireClient clientMethods;
\r
90 int accIdCounter = 0;
\r
91 int listIdCounter = 0;
\r
92 BijectionMap<Integer, AccRecord> accessorTable = new BijectionMap<Integer, AccRecord>();
\r
93 Map<Integer, LisRecord> listenerTable = new HashMap<Integer, LisRecord>();
\r
95 public void dispose() {
\r
99 static class AccRecord {
\r
102 ChildReference reference;
\r
107 static class LisRecord {
\r
114 static class OnEventsRequest {
\r
115 public @Optional Integer arg1;
\r
116 public @Optional Event[] arg2;
\r
120 * Get or create new client handler associated with current thread.
\r
121 * The result value depends on the current thread.
\r
123 * @return client handler
\r
124 * @throws WireException
\r
126 public ClientRecord getClient() throws WireException {
\r
127 TcpConnection connection = TcpConnection.getCurrentConnection();
\r
128 if (connection == null) throw new WireException("Internal Error. This method must be invoked in ConnectionThread");
\r
130 synchronized(clients) {
\r
131 ClientRecord handler = clients.get( connection );
\r
132 if (handler == null) {
\r
134 RecordBinding requestBinding = (RecordBinding) Bindings.getBindingUnchecked( OnEventsRequest.class );
\r
135 Binding responseBinding = Bindings.INTEGER;
\r
136 UnionType ut = new UnionType();
\r
137 ut.components = new Component[0];
\r
138 UnionBinding errorBinding = (UnionBinding) params.bindingScheme.getBinding( ut );
\r
139 MethodTypeBinding onEventsBinding = new MethodTypeBinding("onEvents", requestBinding, responseBinding, errorBinding);
\r
142 handler = new ClientRecord();
\r
143 handler.clientMethodInterface = connection.getRemoteMethodInterface();
\r
144 handler.onEventsMethod = handler.clientMethodInterface.getMethod( onEventsBinding );
\r
145 handler.connection = connection;
\r
146 handler.clientMethods = Methods.createProxy(IWireClient.class, handler.connection.getRemoteMethodInterface());
\r
147 } catch (MethodNotSupportedException e) {
\r
148 throw new WireException(e);
\r
151 connection.addConnectionListener( new ConnectionListener() {
\r
153 public void onError(Exception error) {
\r
154 TcpConnection connection = TcpConnection.getCurrentConnection();
\r
155 ClientRecord handler = clients.remove(connection);
\r
156 if (handler != null) handler.dispose();
\r
159 public void onClosed() {
\r
160 TcpConnection connection = TcpConnection.getCurrentConnection();
\r
161 ClientRecord handler = clients.remove(connection);
\r
162 if (handler != null) handler.dispose();
\r
164 clients.put(connection, handler);
\r
168 } catch (BindingConstructionException e) {
\r
169 throw new WireException( e );
\r
173 AccRecord getAccessor(int accId) throws WireException {
\r
174 ClientRecord client = getClient();
\r
175 AccRecord acc = client.accessorTable.getRight(accId);
\r
176 if (acc==null) throw new WireException("Invalid accessor id");
\r
180 LisRecord getListener(int accId, int lisId) throws WireException {
\r
181 ClientRecord client = getClient();
\r
182 LisRecord lis = client.listenerTable.get(lisId);
\r
186 // Assumption is that each method is invoked in a connection thread.
\r
187 // Therefore object synchronization is unnecessary.
\r
190 public AccessorInfo openAccessor(ChildReference ref) throws WireException {
\r
192 ClientRecord client = getClient();
\r
193 AccRecord acc = new AccRecord();
\r
194 acc.accessor = accessor.getComponent(ref);
\r
195 acc.accId = client.accIdCounter++;
\r
196 acc.reference = ref;
\r
197 acc.type = accessor.type();
\r
198 acc.binding = params.bindingScheme.getBinding(acc.type);
\r
199 client.accessorTable.map(acc.accId, acc);
\r
200 AccessorInfo ai = new AccessorInfo();
\r
201 ai.accessorId = acc.accId;
\r
202 ai.type = acc.type;
\r
204 } catch (AccessorConstructionException e) {
\r
205 throw new WireException( e );
\r
206 } catch (BindingConstructionException e) {
\r
207 throw new WireException( e );
\r
212 public int closeAccessors(Integer[] accIds) throws WireException {
\r
213 ClientRecord client = getClient();
\r
214 for (Integer accId : accIds) {
\r
215 client.accessorTable.removeWithLeft(accId);
\r
220 public MutableVariant getValue(int accId) throws WireException {
\r
222 AccRecord acc = getAccessor(accId);
\r
223 Object value = acc.accessor.getValue(acc.binding);
\r
224 return new MutableVariant(acc.binding, value);
\r
225 } catch (AccessorException e) {
\r
226 throw new WireException(e);
\r
231 public ApplyResult apply(int accId, Event[] changeSet, boolean rollback) {
\r
232 ApplyResult result = new ApplyResult();
\r
233 result.rollbackLog = rollback ? new LinkedList<Event>() : null;
\r
235 AccRecord acc = getAccessor(accId);
\r
236 List<Event> changeSetList = new ArrayList<Event>( changeSet.length );
\r
237 for (Event e : changeSet) changeSetList.add( e );
\r
238 acc.accessor.apply(changeSetList, result.rollbackLog);
\r
239 } catch (WireException e) {
\r
241 } catch (AccessorException e) {
\r
242 result.error = new WireException(e);
\r
248 public int addListener(int accId, InterestSet interestSet,
\r
249 ChildReference path) throws WireException {
\r
252 ClientRecord client = getClient();
\r
253 AccRecord acc = getAccessor(accId);
\r
254 final LisRecord lis = new LisRecord();
\r
256 lis.is = interestSet;
\r
257 lis.lisId = client.listIdCounter++;
\r
258 lis.listener = new Listener() {
\r
260 public void onEvents(Collection<Event> events) {
\r
262 // Push event to client
\r
263 ClientRecord client = getClient();
\r
264 Event[] eventArray = events.toArray( new Event[events.size()] );
\r
266 // client.clientMethods.onEvents(lis.lisId, eventArray);
\r
269 OnEventsRequest req = new OnEventsRequest();
\r
270 req.arg1 = lis.lisId;
\r
271 req.arg2 = eventArray;
\r
272 client.onEventsMethod.invoke( req );
\r
273 } catch (WireException e) {
\r
276 acc.accessor.addListener(lis.listener, interestSet, path, null /*TODO Dispatch thread*/);
\r
277 client.listenerTable.put(lis.lisId, lis);
\r
279 } catch (AccessorException e) {
\r
280 throw new WireException(e.getClass().getName()+": "+e.getMessage());
\r
285 public int removeListener(int lisId) throws WireException {
\r
287 ClientRecord client = getClient();
\r
288 LisRecord lis = client.listenerTable.remove(lisId);
\r
289 AccRecord acc = getAccessor(lis.accId);
\r
290 acc.accessor.removeListener(lis.listener);
\r
292 } catch (AccessorException e) {
\r
293 throw new WireException( e );
\r
298 public int size(int accId) throws WireException {
\r
300 Accessor acc = getAccessor(accId).accessor;
\r
301 if (acc instanceof ArrayAccessor) return ((ArrayAccessor)acc).size();
\r
302 if (acc instanceof RecordAccessor) return ((RecordAccessor)acc).count();
\r
303 if (acc instanceof MapAccessor) return ((MapAccessor)acc).size();
\r
304 if (acc instanceof UnionAccessor) return ((UnionAccessor)acc).count();
\r
305 throw new WireException("Cannot get size for "+acc.getClass().getName());
\r
306 } catch (AccessorException e) {
\r
307 throw new WireException( e );
\r
312 public int clear(int accId) throws WireException {
\r
314 Accessor acc = getAccessor(accId).accessor;
\r
315 if (acc instanceof ArrayAccessor) {
\r
316 ArrayAccessor aa = ((ArrayAccessor)acc);
\r
317 aa.remove(0, aa.size());
\r
320 if (acc instanceof MapAccessor) {
\r
321 ((MapAccessor)acc).clear();
\r
324 throw new WireException("Cannot clear "+acc.getClass().getName());
\r
325 } catch (AccessorException e) {
\r
326 throw new WireException( e );
\r
331 public void add(int accId, int index, Variant[] values) throws WireException {
\r
333 Accessor acc = getAccessor(accId).accessor;
\r
334 if (acc instanceof ArrayAccessor) {
\r
335 ArrayAccessor aa = (ArrayAccessor) acc;
\r
336 if (values.length==0) return;
\r
338 Binding b = values[0].getBinding();
\r
339 boolean sameBinding = true;
\r
340 for (Variant v : values) sameBinding &= v.getBinding()==b;
\r
343 Object[] objs = new Object[ values.length ];
\r
344 for (int i=0; i<values.length; i++) {
\r
345 objs[i] = values[i].getValue();
\r
347 aa.add(index, b, objs);
\r
349 // Add entry one by one
\r
350 for (Variant v : values) {
\r
351 aa.add(index, v.getClass(), v.getValue());
\r
357 throw new WireException("Cannot add to "+acc.getClass().getName());
\r
358 } catch (AccessorException e) {
\r
359 throw new WireException( e );
\r
365 public boolean containsKey(int accId, MutableVariant key) throws WireException {
\r
367 Accessor acc = getAccessor(accId).accessor;
\r
368 if (acc instanceof MapAccessor == false)
\r
369 throw new WireException("Not a map");
\r
370 MapAccessor ma = (MapAccessor) acc;
\r
371 return ma.containsKey(key.getBinding(), key.getValue());
\r
372 } catch (AccessorException e) {
\r
373 throw new WireException( e );
\r
378 public boolean containsValue(int accId, MutableVariant value) throws WireException {
\r
380 Accessor acc = getAccessor(accId).accessor;
\r
381 if (acc instanceof MapAccessor == false)
\r
382 throw new WireException("Not a map");
\r
383 MapAccessor ma = (MapAccessor) acc;
\r
384 return ma.containsValue(value.getBinding(), value.getValue());
\r
385 } catch (AccessorException e) {
\r
386 throw new WireException( e );
\r
391 public MutableVariant getFirstKey(int accId) throws WireException {
\r
393 Accessor acc = getAccessor(accId).accessor;
\r
394 if (acc instanceof MapAccessor == false)
\r
395 throw new WireException("Not a map");
\r
396 MapAccessor ma = (MapAccessor) acc;
\r
397 Binding keyBinding = params.bindingScheme.getBinding( ( (MapType) acc.type() ).keyType );
\r
398 Object key = ma.getFirstKey( keyBinding );
\r
399 return new MutableVariant(keyBinding, key);
\r
400 } catch (AccessorException e) {
\r
401 throw new WireException( e );
\r
402 } catch (BindingConstructionException e) {
\r
403 throw new WireException( e );
\r
408 public MutableVariant getLastKey(int accId) throws WireException {
\r
410 Accessor acc = getAccessor(accId).accessor;
\r
411 if (acc instanceof MapAccessor == false)
\r
412 throw new WireException("Not a map");
\r
413 MapAccessor ma = (MapAccessor) acc;
\r
414 Binding keyBinding = params.bindingScheme.getBinding( ( (MapType) acc.type() ).keyType );
\r
415 Object key = ma.getLastKey( keyBinding );
\r
416 return new MutableVariant(keyBinding, key);
\r
417 } catch (AccessorException e) {
\r
418 throw new WireException( e );
\r
419 } catch (BindingConstructionException e) {
\r
420 throw new WireException( e );
\r
425 public MutableVariant getLowerKey(int accId, MutableVariant key) throws WireException {
\r
427 Accessor acc = getAccessor(accId).accessor;
\r
428 if (acc instanceof MapAccessor == false)
\r
429 throw new WireException("Not a map");
\r
430 MapAccessor ma = (MapAccessor) acc;
\r
431 Binding keyBinding = key.getBinding();
\r
432 Object lowerKey = ma.getLowerKey(keyBinding, key.getValue());
\r
433 if (lowerKey==null) return new MutableVariant(Bindings.VOID, null);
\r
434 return new MutableVariant(keyBinding, lowerKey);
\r
435 } catch (AccessorException e) {
\r
436 throw new WireException( e );
\r
441 public MutableVariant getFloorKey(int accId, MutableVariant key) throws WireException {
\r
443 Accessor acc = getAccessor(accId).accessor;
\r
444 if (acc instanceof MapAccessor == false)
\r
445 throw new WireException("Not a map");
\r
446 MapAccessor ma = (MapAccessor) acc;
\r
447 Binding keyBinding = key.getBinding();
\r
448 Object floorKey = ma.getFloorKey(keyBinding, key.getValue());
\r
449 if (floorKey==null) return new MutableVariant(Bindings.VOID, null);;
\r
450 return new MutableVariant(keyBinding, floorKey);
\r
451 } catch (AccessorException e) {
\r
452 throw new WireException( e );
\r
457 public MutableVariant getCeilingKey(int accId, MutableVariant key) throws WireException {
\r
459 Accessor acc = getAccessor(accId).accessor;
\r
460 if (acc instanceof MapAccessor == false)
\r
461 throw new WireException("Not a map");
\r
462 MapAccessor ma = (MapAccessor) acc;
\r
463 Binding keyBinding = key.getBinding();
\r
464 Object ceilingKey = ma.getCeilingKey(keyBinding, key.getValue());
\r
465 if (ceilingKey==null) return new MutableVariant(Bindings.VOID, null);;
\r
466 return new MutableVariant(keyBinding, ceilingKey);
\r
467 } catch (AccessorException e) {
\r
468 throw new WireException( e );
\r
473 public MutableVariant getHigherKey(int accId, MutableVariant key) throws WireException {
\r
475 Accessor acc = getAccessor(accId).accessor;
\r
476 if (acc instanceof MapAccessor == false)
\r
477 throw new WireException("Not a map");
\r
478 MapAccessor ma = (MapAccessor) acc;
\r
479 Binding keyBinding = key.getBinding();
\r
480 Object higherKey = ma.getHigherKey(keyBinding, key.getValue());
\r
481 if (higherKey==null) return new MutableVariant(Bindings.VOID, null);;
\r
482 return new MutableVariant(keyBinding, higherKey);
\r
483 } catch (AccessorException e) {
\r
484 throw new WireException( e );
\r
489 public boolean hasValue(int accId) throws WireException {
\r
491 Accessor acc = getAccessor(accId).accessor;
\r
492 if (acc instanceof OptionalAccessor == false)
\r
493 throw new WireException("Not an option");
\r
494 OptionalAccessor ma = (OptionalAccessor) acc;
\r
495 return ma.hasValue();
\r
496 } catch (AccessorException e) {
\r
497 throw new WireException( e );
\r
502 public int getTag(int accId) throws WireException {
\r
504 Accessor acc = getAccessor(accId).accessor;
\r
505 if (acc instanceof UnionAccessor == false)
\r
506 throw new WireException("Not an union");
\r
507 UnionAccessor ma = (UnionAccessor) acc;
\r
508 return ma.getTag();
\r
509 } catch (AccessorException e) {
\r
510 throw new WireException( e );
\r
515 public int addAll(int accId, int index, MutableVariant array) throws WireException {
\r
517 ArrayAccessor acc = (ArrayAccessor) getAccessor(accId).accessor;
\r
518 Datatype type = array.type();
\r
519 if (type instanceof ArrayType == false) {
\r
520 throw new WireException("addAll() array expepected, got "+array.type());
\r
523 Binding componentBinding = ( (ArrayBinding) array.getBinding() ).getComponentBinding();
\r
524 ArrayBinding binding = new ObjectArrayBinding((ArrayType) type, componentBinding);
\r
525 Object values[] = (Object[]) array.getValue(binding);
\r
527 int size = acc.size();
\r
528 acc.addAll(binding, values);
\r
531 acc.addAll(index, binding, values);
\r
535 } catch (AccessorException e) {
\r
536 throw new WireException( e );
\r
537 } catch (AdaptException e) {
\r
538 throw new WireException( e );
\r
543 public int add(int accId, int index, MutableVariant value) throws WireException {
\r
545 ArrayAccessor acc = (ArrayAccessor) getAccessor(accId).accessor;
\r
548 int size = acc.size();
\r
549 acc.add(value.getBinding(), value.getValue());
\r
552 acc.add(index, value.getBinding(), value.getValue());
\r
555 } catch (AccessorException e) {
\r
556 throw new WireException( e );
\r
561 public MutableVariant getArrayElement(int accId, int index) throws WireException {
\r
563 AccRecord ar = getAccessor(accId);
\r
564 ArrayAccessor acc = (ArrayAccessor) ar.accessor;
\r
565 Binding valueBinding = ((ArrayBinding)ar.binding).getComponentBinding();
\r
566 Object value = acc.get(index, valueBinding );
\r
567 MutableVariant v = new MutableVariant(valueBinding, value);
\r
569 } catch (AccessorException e) {
\r
570 throw new WireException( e );
\r
575 public MutableVariant getMapValue(int accId, MutableVariant key) throws WireException {
\r
577 AccRecord ar = getAccessor(accId);
\r
578 MapAccessor acc = (MapAccessor) ar.accessor;
\r
579 MapBinding mapBinding = (MapBinding)ar.binding;
\r
580 Object value = acc.get(key.getBinding(), key.getValue(), mapBinding.getValueBinding());
\r
581 if (value == null) return new MutableVariant();
\r
582 return new MutableVariant(mapBinding.getValueBinding(), value);
\r
583 } catch (AccessorException e) {
\r
584 throw new WireException( e );
\r
589 public MutableVariant getMapValues(int accId) throws WireException {
\r
591 AccRecord ar = getAccessor(accId);
\r
592 MapAccessor acc = (MapAccessor) ar.accessor;
\r
593 MapBinding mapBinding = (MapBinding)ar.binding;
\r
594 Binding valueBinding = mapBinding.getValueBinding();
\r
595 Object[] values = acc.getValues( valueBinding );
\r
596 ArrayBinding arrayBinding = new ObjectArrayBinding(new ArrayType(valueBinding.type()), valueBinding);
\r
597 return new MutableVariant(arrayBinding, values);
\r
598 } catch (AccessorException e) {
\r
599 throw new WireException( e );
\r
604 public MutableVariant getMapKeys(int accId) throws WireException {
\r
606 AccRecord ar = getAccessor(accId);
\r
607 MapAccessor acc = (MapAccessor) ar.accessor;
\r
608 MapBinding mapBinding = (MapBinding)ar.binding;
\r
609 Binding keyBinding = mapBinding.getKeyBinding();
\r
610 Object[] keys = acc.getKeys( keyBinding );
\r
611 ArrayBinding arrayBinding = new ObjectArrayBinding(new ArrayType(keyBinding.type()), keyBinding);
\r
612 return new MutableVariant(arrayBinding, keys);
\r
613 } catch (AccessorException e) {
\r
614 throw new WireException( e );
\r