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