-/*******************************************************************************\r
- * Copyright (c) 2010- Association for Decentralized Information Management in\r
- * Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- * \r
- * Contributors:\r
- * VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.databoard.accessor.wire;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.HashMap;\r
-import java.util.LinkedList;\r
-import java.util.List;\r
-import java.util.Map;\r
-\r
-import org.simantics.databoard.Bindings;\r
-import org.simantics.databoard.Methods;\r
-import org.simantics.databoard.accessor.Accessor;\r
-import org.simantics.databoard.accessor.Accessor.Listener;\r
-import org.simantics.databoard.accessor.ArrayAccessor;\r
-import org.simantics.databoard.accessor.MapAccessor;\r
-import org.simantics.databoard.accessor.OptionalAccessor;\r
-import org.simantics.databoard.accessor.RecordAccessor;\r
-import org.simantics.databoard.accessor.UnionAccessor;\r
-import org.simantics.databoard.accessor.error.AccessorConstructionException;\r
-import org.simantics.databoard.accessor.error.AccessorException;\r
-import org.simantics.databoard.accessor.event.Event;\r
-import org.simantics.databoard.accessor.impl.AccessorParams;\r
-import org.simantics.databoard.accessor.interestset.InterestSet;\r
-import org.simantics.databoard.accessor.reference.ChildReference;\r
-import org.simantics.databoard.adapter.AdaptException;\r
-import org.simantics.databoard.annotations.Optional;\r
-import org.simantics.databoard.binding.ArrayBinding;\r
-import org.simantics.databoard.binding.Binding;\r
-import org.simantics.databoard.binding.MapBinding;\r
-import org.simantics.databoard.binding.RecordBinding;\r
-import org.simantics.databoard.binding.UnionBinding;\r
-import org.simantics.databoard.binding.error.BindingConstructionException;\r
-import org.simantics.databoard.binding.impl.ObjectArrayBinding;\r
-import org.simantics.databoard.binding.mutable.MutableVariant;\r
-import org.simantics.databoard.method.MethodInterface;\r
-import org.simantics.databoard.method.MethodInterface.Method;\r
-import org.simantics.databoard.method.MethodNotSupportedException;\r
-import org.simantics.databoard.method.MethodTypeBinding;\r
-import org.simantics.databoard.method.TcpConnection;\r
-import org.simantics.databoard.method.TcpConnection.ConnectionListener;\r
-import org.simantics.databoard.type.ArrayType;\r
-import org.simantics.databoard.type.Component;\r
-import org.simantics.databoard.type.Datatype;\r
-import org.simantics.databoard.type.MapType;\r
-import org.simantics.databoard.type.UnionType;\r
-import org.simantics.databoard.util.BijectionMap;\r
-\r
-/**\r
- * WireServer exposes an accessor over TCP/IP socket. \r
- *\r
- * @author Toni Kalajainen <toni.kalajainen@vtt.fi>\r
- */\r
-public class WireServer implements IWireServer {\r
- \r
- Accessor accessor;\r
- Map<TcpConnection, ClientRecord> clients = Collections.synchronizedMap( new HashMap<TcpConnection, ClientRecord>() );\r
- MethodInterface mi;\r
- AccessorParams params = AccessorParams.DEFAULT;\r
- \r
- public WireServer(Accessor accessor) {\r
- this.accessor = accessor;\r
- try {\r
- this.mi = Methods.bindInterface(IWireServer.class, this);\r
- } catch (BindingConstructionException e) {\r
- throw new RuntimeException(e);\r
- }\r
- }\r
- \r
- public MethodInterface getMethodInterface() {\r
- return mi;\r
- }\r
- \r
- static class ClientRecord {\r
- TcpConnection connection;\r
- MethodInterface clientMethodInterface;\r
- Method onEventsMethod;\r
- IWireClient clientMethods;\r
- int accIdCounter = 0;\r
- int listIdCounter = 0; \r
- BijectionMap<Integer, AccRecord> accessorTable = new BijectionMap<Integer, AccRecord>();\r
- Map<Integer, LisRecord> listenerTable = new HashMap<Integer, LisRecord>();\r
- \r
- public void dispose() { \r
- } \r
- }\r
- \r
- static class AccRecord {\r
- int accId;\r
- Accessor accessor;\r
- ChildReference reference;\r
- Datatype type;\r
- Binding binding;\r
- } \r
- \r
- static class LisRecord {\r
- int lisId;\r
- int accId;\r
- InterestSet is;\r
- Listener listener;\r
- }\r
- \r
- static class OnEventsRequest {\r
- public @Optional Integer arg1;\r
- public @Optional Event[] arg2;\r
- }\r
- \r
- /**\r
- * Get or create new client handler associated with current thread.\r
- * The result value depends on the current thread. \r
- * \r
- * @return client handler\r
- * @throws WireException\r
- */\r
- public ClientRecord getClient() throws WireException {\r
- TcpConnection connection = TcpConnection.getCurrentConnection();\r
- if (connection == null) throw new WireException("Internal Error. This method must be invoked in ConnectionThread");\r
- try {\r
- synchronized(clients) {\r
- ClientRecord handler = clients.get( connection );\r
- if (handler == null) {\r
-\r
- RecordBinding requestBinding = (RecordBinding) Bindings.getBindingUnchecked( OnEventsRequest.class );\r
- Binding responseBinding = Bindings.INTEGER;\r
- UnionType ut = new UnionType();\r
- ut.components = new Component[0];\r
- UnionBinding errorBinding = (UnionBinding) params.bindingScheme.getBinding( ut );\r
- MethodTypeBinding onEventsBinding = new MethodTypeBinding("onEvents", requestBinding, responseBinding, errorBinding);\r
- \r
- try {\r
- handler = new ClientRecord();\r
- handler.clientMethodInterface = connection.getRemoteMethodInterface();\r
- handler.onEventsMethod = handler.clientMethodInterface.getMethod( onEventsBinding );\r
- handler.connection = connection;\r
- handler.clientMethods = Methods.createProxy(IWireClient.class, handler.connection.getRemoteMethodInterface());\r
- } catch (MethodNotSupportedException e) {\r
- throw new WireException(e);\r
- }\r
- \r
- connection.addConnectionListener( new ConnectionListener() {\r
- @Override\r
- public void onError(Exception error) {\r
- TcpConnection connection = TcpConnection.getCurrentConnection();\r
- ClientRecord handler = clients.remove(connection);\r
- if (handler != null) handler.dispose();\r
- }\r
- @Override\r
- public void onClosed() {\r
- TcpConnection connection = TcpConnection.getCurrentConnection();\r
- ClientRecord handler = clients.remove(connection);\r
- if (handler != null) handler.dispose();\r
- }});\r
- clients.put(connection, handler);\r
- }\r
- return handler;\r
- }\r
- } catch (BindingConstructionException e) {\r
- throw new WireException( e );\r
- }\r
- }\r
-\r
- AccRecord getAccessor(int accId) throws WireException {\r
- ClientRecord client = getClient();\r
- AccRecord acc = client.accessorTable.getRight(accId);\r
- if (acc==null) throw new WireException("Invalid accessor id");\r
- return acc;\r
- }\r
- \r
- LisRecord getListener(int accId, int lisId) throws WireException {\r
- ClientRecord client = getClient();\r
- LisRecord lis = client.listenerTable.get(lisId);\r
- return lis;\r
- }\r
- \r
- // Assumption is that each method is invoked in a connection thread.\r
- // Therefore object synchronization is unnecessary.\r
- \r
- @Override\r
- public AccessorInfo openAccessor(ChildReference ref) throws WireException {\r
- try {\r
- ClientRecord client = getClient();\r
- AccRecord acc = new AccRecord();\r
- acc.accessor = accessor.getComponent(ref);\r
- acc.accId = client.accIdCounter++;\r
- acc.reference = ref;\r
- acc.type = accessor.type();\r
- acc.binding = params.bindingScheme.getBinding(acc.type);\r
- client.accessorTable.map(acc.accId, acc);\r
- AccessorInfo ai = new AccessorInfo();\r
- ai.accessorId = acc.accId;\r
- ai.type = acc.type;\r
- return ai;\r
- } catch (AccessorConstructionException e) {\r
- throw new WireException( e );\r
- } catch (BindingConstructionException e) {\r
- throw new WireException( e );\r
- }\r
- }\r
-\r
- @Override\r
- public int closeAccessors(Integer[] accIds) throws WireException {\r
- ClientRecord client = getClient();\r
- for (Integer accId : accIds) {\r
- client.accessorTable.removeWithLeft(accId);\r
- }\r
- return 0;\r
- }\r
- @Override\r
- public MutableVariant getValue(int accId) throws WireException {\r
- try {\r
- AccRecord acc = getAccessor(accId);\r
- Object value = acc.accessor.getValue(acc.binding);\r
- return new MutableVariant(acc.binding, value);\r
- } catch (AccessorException e) {\r
- throw new WireException(e);\r
- }\r
- }\r
-\r
- @Override\r
- public ApplyResult apply(int accId, Event[] changeSet, boolean rollback) {\r
- ApplyResult result = new ApplyResult();\r
- result.rollbackLog = rollback ? new LinkedList<Event>() : null; \r
- try {\r
- AccRecord acc = getAccessor(accId);\r
- List<Event> changeSetList = new ArrayList<Event>( changeSet.length );\r
- for (Event e : changeSet) changeSetList.add( e );\r
- acc.accessor.apply(changeSetList, result.rollbackLog);\r
- } catch (WireException e) {\r
- result.error = e;\r
- } catch (AccessorException e) {\r
- result.error = new WireException(e);\r
- } \r
- return result;\r
- }\r
-\r
- @Override\r
- public int addListener(int accId, InterestSet interestSet,\r
- ChildReference path) throws WireException {\r
- try {\r
- \r
- ClientRecord client = getClient();\r
- AccRecord acc = getAccessor(accId);\r
- final LisRecord lis = new LisRecord();\r
- lis.accId = accId;\r
- lis.is = interestSet;\r
- lis.lisId = client.listIdCounter++;\r
- lis.listener = new Listener() {\r
- @Override\r
- public void onEvents(Collection<Event> events) {\r
- try {\r
- // Push event to client\r
- ClientRecord client = getClient();\r
- Event[] eventArray = events.toArray( new Event[events.size()] );\r
-\r
-// client.clientMethods.onEvents(lis.lisId, eventArray);\r
-\r
- // Invoke async.\r
- OnEventsRequest req = new OnEventsRequest();\r
- req.arg1 = lis.lisId;\r
- req.arg2 = eventArray;\r
- client.onEventsMethod.invoke( req );\r
- } catch (WireException e) {\r
- }\r
- }};\r
- acc.accessor.addListener(lis.listener, interestSet, path, null /*TODO Dispatch thread*/);\r
- client.listenerTable.put(lis.lisId, lis);\r
- return lis.lisId;\r
- } catch (AccessorException e) {\r
- throw new WireException(e.getClass().getName()+": "+e.getMessage());\r
- } \r
- }\r
-\r
- @Override\r
- public int removeListener(int lisId) throws WireException {\r
- try {\r
- ClientRecord client = getClient(); \r
- LisRecord lis = client.listenerTable.remove(lisId);\r
- AccRecord acc = getAccessor(lis.accId);\r
- acc.accessor.removeListener(lis.listener);\r
- return 0;\r
- } catch (AccessorException e) {\r
- throw new WireException( e ); \r
- } \r
- }\r
-\r
- @Override\r
- public int size(int accId) throws WireException {\r
- try {\r
- Accessor acc = getAccessor(accId).accessor;\r
- if (acc instanceof ArrayAccessor) return ((ArrayAccessor)acc).size();\r
- if (acc instanceof RecordAccessor) return ((RecordAccessor)acc).count();\r
- if (acc instanceof MapAccessor) return ((MapAccessor)acc).size();\r
- if (acc instanceof UnionAccessor) return ((UnionAccessor)acc).count();\r
- throw new WireException("Cannot get size for "+acc.getClass().getName());\r
- } catch (AccessorException e) {\r
- throw new WireException( e );\r
- }\r
- }\r
-\r
- @Override\r
- public int clear(int accId) throws WireException {\r
- try {\r
- Accessor acc = getAccessor(accId).accessor;\r
- if (acc instanceof ArrayAccessor) {\r
- ArrayAccessor aa = ((ArrayAccessor)acc); \r
- aa.remove(0, aa.size());\r
- return 0;\r
- }\r
- if (acc instanceof MapAccessor) {\r
- ((MapAccessor)acc).clear();\r
- return 0;\r
- } \r
- throw new WireException("Cannot clear "+acc.getClass().getName());\r
- } catch (AccessorException e) {\r
- throw new WireException( e );\r
- }\r
- }\r
-/*\r
- @Override\r
- public void add(int accId, int index, Variant[] values) throws WireException {\r
- try {\r
- Accessor acc = getAccessor(accId).accessor;\r
- if (acc instanceof ArrayAccessor) {\r
- ArrayAccessor aa = (ArrayAccessor) acc;\r
- if (values.length==0) return;\r
- \r
- Binding b = values[0].getBinding();\r
- boolean sameBinding = true;\r
- for (Variant v : values) sameBinding &= v.getBinding()==b;\r
- \r
- if (sameBinding) {\r
- Object[] objs = new Object[ values.length ];\r
- for (int i=0; i<values.length; i++) {\r
- objs[i] = values[i].getValue();\r
- }\r
- aa.add(index, b, objs); \r
- } else {\r
- // Add entry one by one\r
- for (Variant v : values) {\r
- aa.add(index, v.getClass(), v.getValue());\r
- index++;\r
- }\r
- }\r
- \r
- } else \r
- throw new WireException("Cannot add to "+acc.getClass().getName());\r
- } catch (AccessorException e) {\r
- throw new WireException( e );\r
- }\r
- }\r
-*/\r
-\r
- @Override\r
- public boolean containsKey(int accId, MutableVariant key) throws WireException {\r
- try {\r
- Accessor acc = getAccessor(accId).accessor;\r
- if (acc instanceof MapAccessor == false) \r
- throw new WireException("Not a map");\r
- MapAccessor ma = (MapAccessor) acc;\r
- return ma.containsKey(key.getBinding(), key.getValue());\r
- } catch (AccessorException e) {\r
- throw new WireException( e );\r
- }\r
- }\r
-\r
- @Override\r
- public boolean containsValue(int accId, MutableVariant value) throws WireException {\r
- try {\r
- Accessor acc = getAccessor(accId).accessor;\r
- if (acc instanceof MapAccessor == false) \r
- throw new WireException("Not a map");\r
- MapAccessor ma = (MapAccessor) acc;\r
- return ma.containsValue(value.getBinding(), value.getValue());\r
- } catch (AccessorException e) {\r
- throw new WireException( e );\r
- }\r
- }\r
-\r
- @Override\r
- public MutableVariant getFirstKey(int accId) throws WireException {\r
- try {\r
- Accessor acc = getAccessor(accId).accessor;\r
- if (acc instanceof MapAccessor == false) \r
- throw new WireException("Not a map");\r
- MapAccessor ma = (MapAccessor) acc;\r
- Binding keyBinding = params.bindingScheme.getBinding( ( (MapType) acc.type() ).keyType );\r
- Object key = ma.getFirstKey( keyBinding );\r
- return new MutableVariant(keyBinding, key);\r
- } catch (AccessorException e) {\r
- throw new WireException( e );\r
- } catch (BindingConstructionException e) {\r
- throw new WireException( e );\r
- }\r
- }\r
-\r
- @Override\r
- public MutableVariant getLastKey(int accId) throws WireException {\r
- try {\r
- Accessor acc = getAccessor(accId).accessor;\r
- if (acc instanceof MapAccessor == false) \r
- throw new WireException("Not a map");\r
- MapAccessor ma = (MapAccessor) acc;\r
- Binding keyBinding = params.bindingScheme.getBinding( ( (MapType) acc.type() ).keyType );\r
- Object key = ma.getLastKey( keyBinding );\r
- return new MutableVariant(keyBinding, key);\r
- } catch (AccessorException e) {\r
- throw new WireException( e );\r
- } catch (BindingConstructionException e) {\r
- throw new WireException( e );\r
- }\r
- }\r
-\r
- @Override\r
- public MutableVariant getLowerKey(int accId, MutableVariant key) throws WireException {\r
- try {\r
- Accessor acc = getAccessor(accId).accessor;\r
- if (acc instanceof MapAccessor == false) \r
- throw new WireException("Not a map");\r
- MapAccessor ma = (MapAccessor) acc;\r
- Binding keyBinding = key.getBinding();\r
- Object lowerKey = ma.getLowerKey(keyBinding, key.getValue());\r
- if (lowerKey==null) return new MutableVariant(Bindings.VOID, null);\r
- return new MutableVariant(keyBinding, lowerKey);\r
- } catch (AccessorException e) {\r
- throw new WireException( e );\r
- } \r
- }\r
-\r
- @Override\r
- public MutableVariant getFloorKey(int accId, MutableVariant key) throws WireException {\r
- try {\r
- Accessor acc = getAccessor(accId).accessor;\r
- if (acc instanceof MapAccessor == false) \r
- throw new WireException("Not a map");\r
- MapAccessor ma = (MapAccessor) acc;\r
- Binding keyBinding = key.getBinding();\r
- Object floorKey = ma.getFloorKey(keyBinding, key.getValue());\r
- if (floorKey==null) return new MutableVariant(Bindings.VOID, null);; \r
- return new MutableVariant(keyBinding, floorKey);\r
- } catch (AccessorException e) {\r
- throw new WireException( e );\r
- } \r
- }\r
-\r
- @Override\r
- public MutableVariant getCeilingKey(int accId, MutableVariant key) throws WireException {\r
- try {\r
- Accessor acc = getAccessor(accId).accessor;\r
- if (acc instanceof MapAccessor == false) \r
- throw new WireException("Not a map");\r
- MapAccessor ma = (MapAccessor) acc;\r
- Binding keyBinding = key.getBinding();\r
- Object ceilingKey = ma.getCeilingKey(keyBinding, key.getValue());\r
- if (ceilingKey==null) return new MutableVariant(Bindings.VOID, null);; \r
- return new MutableVariant(keyBinding, ceilingKey);\r
- } catch (AccessorException e) {\r
- throw new WireException( e );\r
- } \r
- }\r
-\r
- @Override\r
- public MutableVariant getHigherKey(int accId, MutableVariant key) throws WireException {\r
- try {\r
- Accessor acc = getAccessor(accId).accessor;\r
- if (acc instanceof MapAccessor == false) \r
- throw new WireException("Not a map");\r
- MapAccessor ma = (MapAccessor) acc;\r
- Binding keyBinding = key.getBinding();\r
- Object higherKey = ma.getHigherKey(keyBinding, key.getValue());\r
- if (higherKey==null) return new MutableVariant(Bindings.VOID, null);; \r
- return new MutableVariant(keyBinding, higherKey);\r
- } catch (AccessorException e) {\r
- throw new WireException( e );\r
- } \r
- }\r
-\r
- @Override\r
- public boolean hasValue(int accId) throws WireException {\r
- try {\r
- Accessor acc = getAccessor(accId).accessor;\r
- if (acc instanceof OptionalAccessor == false) \r
- throw new WireException("Not an option");\r
- OptionalAccessor ma = (OptionalAccessor) acc;\r
- return ma.hasValue();\r
- } catch (AccessorException e) {\r
- throw new WireException( e );\r
- } \r
- }\r
-\r
- @Override\r
- public int getTag(int accId) throws WireException {\r
- try {\r
- Accessor acc = getAccessor(accId).accessor;\r
- if (acc instanceof UnionAccessor == false) \r
- throw new WireException("Not an union");\r
- UnionAccessor ma = (UnionAccessor) acc;\r
- return ma.getTag();\r
- } catch (AccessorException e) {\r
- throw new WireException( e );\r
- } \r
- }\r
- \r
- @Override\r
- public int addAll(int accId, int index, MutableVariant array) throws WireException {\r
- try {\r
- ArrayAccessor acc = (ArrayAccessor) getAccessor(accId).accessor;\r
- Datatype type = array.type();\r
- if (type instanceof ArrayType == false) {\r
- throw new WireException("addAll() array expepected, got "+array.type());\r
- }\r
- \r
- Binding componentBinding = ( (ArrayBinding) array.getBinding() ).getComponentBinding();\r
- ArrayBinding binding = new ObjectArrayBinding((ArrayType) type, componentBinding);\r
- Object values[] = (Object[]) array.getValue(binding);\r
- if (index==-1) {\r
- int size = acc.size();\r
- acc.addAll(binding, values);\r
- return size;\r
- } else {\r
- acc.addAll(index, binding, values);\r
- return index;\r
- }\r
- \r
- } catch (AccessorException e) {\r
- throw new WireException( e );\r
- } catch (AdaptException e) {\r
- throw new WireException( e );\r
- }\r
- }\r
-\r
- @Override\r
- public int add(int accId, int index, MutableVariant value) throws WireException {\r
- try {\r
- ArrayAccessor acc = (ArrayAccessor) getAccessor(accId).accessor;\r
-\r
- if (index==-1) {\r
- int size = acc.size();\r
- acc.add(value.getBinding(), value.getValue());\r
- return size;\r
- } else {\r
- acc.add(index, value.getBinding(), value.getValue());\r
- return index;\r
- }\r
- } catch (AccessorException e) {\r
- throw new WireException( e );\r
- }\r
- }\r
-\r
- @Override\r
- public MutableVariant getArrayElement(int accId, int index) throws WireException {\r
- try {\r
- AccRecord ar = getAccessor(accId);\r
- ArrayAccessor acc = (ArrayAccessor) ar.accessor;\r
- Binding valueBinding = ((ArrayBinding)ar.binding).getComponentBinding();\r
- Object value = acc.get(index, valueBinding );\r
- MutableVariant v = new MutableVariant(valueBinding, value);\r
- return v;\r
- } catch (AccessorException e) {\r
- throw new WireException( e );\r
- }\r
- }\r
- \r
- @Override\r
- public MutableVariant getMapValue(int accId, MutableVariant key) throws WireException {\r
- try {\r
- AccRecord ar = getAccessor(accId);\r
- MapAccessor acc = (MapAccessor) ar.accessor;\r
- MapBinding mapBinding = (MapBinding)ar.binding;\r
- Object value = acc.get(key.getBinding(), key.getValue(), mapBinding.getValueBinding());\r
- if (value == null) return new MutableVariant();\r
- return new MutableVariant(mapBinding.getValueBinding(), value);\r
- } catch (AccessorException e) {\r
- throw new WireException( e );\r
- }\r
- }\r
-\r
- @Override\r
- public MutableVariant getMapValues(int accId) throws WireException {\r
- try {\r
- AccRecord ar = getAccessor(accId);\r
- MapAccessor acc = (MapAccessor) ar.accessor;\r
- MapBinding mapBinding = (MapBinding)ar.binding;\r
- Binding valueBinding = mapBinding.getValueBinding();\r
- Object[] values = acc.getValues( valueBinding );\r
- ArrayBinding arrayBinding = new ObjectArrayBinding(new ArrayType(valueBinding.type()), valueBinding);\r
- return new MutableVariant(arrayBinding, values);\r
- } catch (AccessorException e) {\r
- throw new WireException( e );\r
- }\r
- }\r
-\r
- @Override\r
- public MutableVariant getMapKeys(int accId) throws WireException {\r
- try {\r
- AccRecord ar = getAccessor(accId);\r
- MapAccessor acc = (MapAccessor) ar.accessor;\r
- MapBinding mapBinding = (MapBinding)ar.binding;\r
- Binding keyBinding = mapBinding.getKeyBinding();\r
- Object[] keys = acc.getKeys( keyBinding );\r
- ArrayBinding arrayBinding = new ObjectArrayBinding(new ArrayType(keyBinding.type()), keyBinding);\r
- return new MutableVariant(arrayBinding, keys);\r
- } catch (AccessorException e) {\r
- throw new WireException( e );\r
- }\r
- }\r
- \r
-}\r
-\r
+/*******************************************************************************
+ * Copyright (c) 2010- Association for Decentralized Information Management in
+ * Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.databoard.accessor.wire;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.simantics.databoard.Bindings;
+import org.simantics.databoard.Methods;
+import org.simantics.databoard.accessor.Accessor;
+import org.simantics.databoard.accessor.Accessor.Listener;
+import org.simantics.databoard.accessor.ArrayAccessor;
+import org.simantics.databoard.accessor.MapAccessor;
+import org.simantics.databoard.accessor.OptionalAccessor;
+import org.simantics.databoard.accessor.RecordAccessor;
+import org.simantics.databoard.accessor.UnionAccessor;
+import org.simantics.databoard.accessor.error.AccessorConstructionException;
+import org.simantics.databoard.accessor.error.AccessorException;
+import org.simantics.databoard.accessor.event.Event;
+import org.simantics.databoard.accessor.impl.AccessorParams;
+import org.simantics.databoard.accessor.interestset.InterestSet;
+import org.simantics.databoard.accessor.reference.ChildReference;
+import org.simantics.databoard.adapter.AdaptException;
+import org.simantics.databoard.annotations.Optional;
+import org.simantics.databoard.binding.ArrayBinding;
+import org.simantics.databoard.binding.Binding;
+import org.simantics.databoard.binding.MapBinding;
+import org.simantics.databoard.binding.RecordBinding;
+import org.simantics.databoard.binding.UnionBinding;
+import org.simantics.databoard.binding.error.BindingConstructionException;
+import org.simantics.databoard.binding.impl.ObjectArrayBinding;
+import org.simantics.databoard.binding.mutable.MutableVariant;
+import org.simantics.databoard.method.MethodInterface;
+import org.simantics.databoard.method.MethodInterface.Method;
+import org.simantics.databoard.method.MethodNotSupportedException;
+import org.simantics.databoard.method.MethodTypeBinding;
+import org.simantics.databoard.method.TcpConnection;
+import org.simantics.databoard.method.TcpConnection.ConnectionListener;
+import org.simantics.databoard.type.ArrayType;
+import org.simantics.databoard.type.Component;
+import org.simantics.databoard.type.Datatype;
+import org.simantics.databoard.type.MapType;
+import org.simantics.databoard.type.UnionType;
+import org.simantics.databoard.util.BijectionMap;
+
+/**
+ * WireServer exposes an accessor over TCP/IP socket.
+ *
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
+ */
+public class WireServer implements IWireServer {
+
+ Accessor accessor;
+ Map<TcpConnection, ClientRecord> clients = Collections.synchronizedMap( new HashMap<TcpConnection, ClientRecord>() );
+ MethodInterface mi;
+ AccessorParams params = AccessorParams.DEFAULT;
+
+ public WireServer(Accessor accessor) {
+ this.accessor = accessor;
+ try {
+ this.mi = Methods.bindInterface(IWireServer.class, this);
+ } catch (BindingConstructionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public MethodInterface getMethodInterface() {
+ return mi;
+ }
+
+ static class ClientRecord {
+ TcpConnection connection;
+ MethodInterface clientMethodInterface;
+ Method onEventsMethod;
+ IWireClient clientMethods;
+ int accIdCounter = 0;
+ int listIdCounter = 0;
+ BijectionMap<Integer, AccRecord> accessorTable = new BijectionMap<Integer, AccRecord>();
+ Map<Integer, LisRecord> listenerTable = new HashMap<Integer, LisRecord>();
+
+ public void dispose() {
+ }
+ }
+
+ static class AccRecord {
+ int accId;
+ Accessor accessor;
+ ChildReference reference;
+ Datatype type;
+ Binding binding;
+ }
+
+ static class LisRecord {
+ int lisId;
+ int accId;
+ InterestSet is;
+ Listener listener;
+ }
+
+ static class OnEventsRequest {
+ public @Optional Integer arg1;
+ public @Optional Event[] arg2;
+ }
+
+ /**
+ * Get or create new client handler associated with current thread.
+ * The result value depends on the current thread.
+ *
+ * @return client handler
+ * @throws WireException
+ */
+ public ClientRecord getClient() throws WireException {
+ TcpConnection connection = TcpConnection.getCurrentConnection();
+ if (connection == null) throw new WireException("Internal Error. This method must be invoked in ConnectionThread");
+ try {
+ synchronized(clients) {
+ ClientRecord handler = clients.get( connection );
+ if (handler == null) {
+
+ RecordBinding requestBinding = (RecordBinding) Bindings.getBindingUnchecked( OnEventsRequest.class );
+ Binding responseBinding = Bindings.INTEGER;
+ UnionType ut = new UnionType();
+ ut.components = new Component[0];
+ UnionBinding errorBinding = (UnionBinding) params.bindingScheme.getBinding( ut );
+ MethodTypeBinding onEventsBinding = new MethodTypeBinding("onEvents", requestBinding, responseBinding, errorBinding);
+
+ try {
+ handler = new ClientRecord();
+ handler.clientMethodInterface = connection.getRemoteMethodInterface();
+ handler.onEventsMethod = handler.clientMethodInterface.getMethod( onEventsBinding );
+ handler.connection = connection;
+ handler.clientMethods = Methods.createProxy(IWireClient.class, handler.connection.getRemoteMethodInterface());
+ } catch (MethodNotSupportedException e) {
+ throw new WireException(e);
+ }
+
+ connection.addConnectionListener( new ConnectionListener() {
+ @Override
+ public void onError(Exception error) {
+ TcpConnection connection = TcpConnection.getCurrentConnection();
+ ClientRecord handler = clients.remove(connection);
+ if (handler != null) handler.dispose();
+ }
+ @Override
+ public void onClosed() {
+ TcpConnection connection = TcpConnection.getCurrentConnection();
+ ClientRecord handler = clients.remove(connection);
+ if (handler != null) handler.dispose();
+ }});
+ clients.put(connection, handler);
+ }
+ return handler;
+ }
+ } catch (BindingConstructionException e) {
+ throw new WireException( e );
+ }
+ }
+
+ AccRecord getAccessor(int accId) throws WireException {
+ ClientRecord client = getClient();
+ AccRecord acc = client.accessorTable.getRight(accId);
+ if (acc==null) throw new WireException("Invalid accessor id");
+ return acc;
+ }
+
+ LisRecord getListener(int accId, int lisId) throws WireException {
+ ClientRecord client = getClient();
+ LisRecord lis = client.listenerTable.get(lisId);
+ return lis;
+ }
+
+ // Assumption is that each method is invoked in a connection thread.
+ // Therefore object synchronization is unnecessary.
+
+ @Override
+ public AccessorInfo openAccessor(ChildReference ref) throws WireException {
+ try {
+ ClientRecord client = getClient();
+ AccRecord acc = new AccRecord();
+ acc.accessor = accessor.getComponent(ref);
+ acc.accId = client.accIdCounter++;
+ acc.reference = ref;
+ acc.type = accessor.type();
+ acc.binding = params.bindingScheme.getBinding(acc.type);
+ client.accessorTable.map(acc.accId, acc);
+ AccessorInfo ai = new AccessorInfo();
+ ai.accessorId = acc.accId;
+ ai.type = acc.type;
+ return ai;
+ } catch (AccessorConstructionException e) {
+ throw new WireException( e );
+ } catch (BindingConstructionException e) {
+ throw new WireException( e );
+ }
+ }
+
+ @Override
+ public int closeAccessors(Integer[] accIds) throws WireException {
+ ClientRecord client = getClient();
+ for (Integer accId : accIds) {
+ client.accessorTable.removeWithLeft(accId);
+ }
+ return 0;
+ }
+ @Override
+ public MutableVariant getValue(int accId) throws WireException {
+ try {
+ AccRecord acc = getAccessor(accId);
+ Object value = acc.accessor.getValue(acc.binding);
+ return new MutableVariant(acc.binding, value);
+ } catch (AccessorException e) {
+ throw new WireException(e);
+ }
+ }
+
+ @Override
+ public ApplyResult apply(int accId, Event[] changeSet, boolean rollback) {
+ ApplyResult result = new ApplyResult();
+ result.rollbackLog = rollback ? new LinkedList<Event>() : null;
+ try {
+ AccRecord acc = getAccessor(accId);
+ List<Event> changeSetList = new ArrayList<Event>( changeSet.length );
+ for (Event e : changeSet) changeSetList.add( e );
+ acc.accessor.apply(changeSetList, result.rollbackLog);
+ } catch (WireException e) {
+ result.error = e;
+ } catch (AccessorException e) {
+ result.error = new WireException(e);
+ }
+ return result;
+ }
+
+ @Override
+ public int addListener(int accId, InterestSet interestSet,
+ ChildReference path) throws WireException {
+ try {
+
+ ClientRecord client = getClient();
+ AccRecord acc = getAccessor(accId);
+ final LisRecord lis = new LisRecord();
+ lis.accId = accId;
+ lis.is = interestSet;
+ lis.lisId = client.listIdCounter++;
+ lis.listener = new Listener() {
+ @Override
+ public void onEvents(Collection<Event> events) {
+ try {
+ // Push event to client
+ ClientRecord client = getClient();
+ Event[] eventArray = events.toArray( new Event[events.size()] );
+
+// client.clientMethods.onEvents(lis.lisId, eventArray);
+
+ // Invoke async.
+ OnEventsRequest req = new OnEventsRequest();
+ req.arg1 = lis.lisId;
+ req.arg2 = eventArray;
+ client.onEventsMethod.invoke( req );
+ } catch (WireException e) {
+ }
+ }};
+ acc.accessor.addListener(lis.listener, interestSet, path, null /*TODO Dispatch thread*/);
+ client.listenerTable.put(lis.lisId, lis);
+ return lis.lisId;
+ } catch (AccessorException e) {
+ throw new WireException(e.getClass().getName()+": "+e.getMessage());
+ }
+ }
+
+ @Override
+ public int removeListener(int lisId) throws WireException {
+ try {
+ ClientRecord client = getClient();
+ LisRecord lis = client.listenerTable.remove(lisId);
+ AccRecord acc = getAccessor(lis.accId);
+ acc.accessor.removeListener(lis.listener);
+ return 0;
+ } catch (AccessorException e) {
+ throw new WireException( e );
+ }
+ }
+
+ @Override
+ public int size(int accId) throws WireException {
+ try {
+ Accessor acc = getAccessor(accId).accessor;
+ if (acc instanceof ArrayAccessor) return ((ArrayAccessor)acc).size();
+ if (acc instanceof RecordAccessor) return ((RecordAccessor)acc).count();
+ if (acc instanceof MapAccessor) return ((MapAccessor)acc).size();
+ if (acc instanceof UnionAccessor) return ((UnionAccessor)acc).count();
+ throw new WireException("Cannot get size for "+acc.getClass().getName());
+ } catch (AccessorException e) {
+ throw new WireException( e );
+ }
+ }
+
+ @Override
+ public int clear(int accId) throws WireException {
+ try {
+ Accessor acc = getAccessor(accId).accessor;
+ if (acc instanceof ArrayAccessor) {
+ ArrayAccessor aa = ((ArrayAccessor)acc);
+ aa.remove(0, aa.size());
+ return 0;
+ }
+ if (acc instanceof MapAccessor) {
+ ((MapAccessor)acc).clear();
+ return 0;
+ }
+ throw new WireException("Cannot clear "+acc.getClass().getName());
+ } catch (AccessorException e) {
+ throw new WireException( e );
+ }
+ }
+/*
+ @Override
+ public void add(int accId, int index, Variant[] values) throws WireException {
+ try {
+ Accessor acc = getAccessor(accId).accessor;
+ if (acc instanceof ArrayAccessor) {
+ ArrayAccessor aa = (ArrayAccessor) acc;
+ if (values.length==0) return;
+
+ Binding b = values[0].getBinding();
+ boolean sameBinding = true;
+ for (Variant v : values) sameBinding &= v.getBinding()==b;
+
+ if (sameBinding) {
+ Object[] objs = new Object[ values.length ];
+ for (int i=0; i<values.length; i++) {
+ objs[i] = values[i].getValue();
+ }
+ aa.add(index, b, objs);
+ } else {
+ // Add entry one by one
+ for (Variant v : values) {
+ aa.add(index, v.getClass(), v.getValue());
+ index++;
+ }
+ }
+
+ } else
+ throw new WireException("Cannot add to "+acc.getClass().getName());
+ } catch (AccessorException e) {
+ throw new WireException( e );
+ }
+ }
+*/
+
+ @Override
+ public boolean containsKey(int accId, MutableVariant key) throws WireException {
+ try {
+ Accessor acc = getAccessor(accId).accessor;
+ if (acc instanceof MapAccessor == false)
+ throw new WireException("Not a map");
+ MapAccessor ma = (MapAccessor) acc;
+ return ma.containsKey(key.getBinding(), key.getValue());
+ } catch (AccessorException e) {
+ throw new WireException( e );
+ }
+ }
+
+ @Override
+ public boolean containsValue(int accId, MutableVariant value) throws WireException {
+ try {
+ Accessor acc = getAccessor(accId).accessor;
+ if (acc instanceof MapAccessor == false)
+ throw new WireException("Not a map");
+ MapAccessor ma = (MapAccessor) acc;
+ return ma.containsValue(value.getBinding(), value.getValue());
+ } catch (AccessorException e) {
+ throw new WireException( e );
+ }
+ }
+
+ @Override
+ public MutableVariant getFirstKey(int accId) throws WireException {
+ try {
+ Accessor acc = getAccessor(accId).accessor;
+ if (acc instanceof MapAccessor == false)
+ throw new WireException("Not a map");
+ MapAccessor ma = (MapAccessor) acc;
+ Binding keyBinding = params.bindingScheme.getBinding( ( (MapType) acc.type() ).keyType );
+ Object key = ma.getFirstKey( keyBinding );
+ return new MutableVariant(keyBinding, key);
+ } catch (AccessorException e) {
+ throw new WireException( e );
+ } catch (BindingConstructionException e) {
+ throw new WireException( e );
+ }
+ }
+
+ @Override
+ public MutableVariant getLastKey(int accId) throws WireException {
+ try {
+ Accessor acc = getAccessor(accId).accessor;
+ if (acc instanceof MapAccessor == false)
+ throw new WireException("Not a map");
+ MapAccessor ma = (MapAccessor) acc;
+ Binding keyBinding = params.bindingScheme.getBinding( ( (MapType) acc.type() ).keyType );
+ Object key = ma.getLastKey( keyBinding );
+ return new MutableVariant(keyBinding, key);
+ } catch (AccessorException e) {
+ throw new WireException( e );
+ } catch (BindingConstructionException e) {
+ throw new WireException( e );
+ }
+ }
+
+ @Override
+ public MutableVariant getLowerKey(int accId, MutableVariant key) throws WireException {
+ try {
+ Accessor acc = getAccessor(accId).accessor;
+ if (acc instanceof MapAccessor == false)
+ throw new WireException("Not a map");
+ MapAccessor ma = (MapAccessor) acc;
+ Binding keyBinding = key.getBinding();
+ Object lowerKey = ma.getLowerKey(keyBinding, key.getValue());
+ if (lowerKey==null) return new MutableVariant(Bindings.VOID, null);
+ return new MutableVariant(keyBinding, lowerKey);
+ } catch (AccessorException e) {
+ throw new WireException( e );
+ }
+ }
+
+ @Override
+ public MutableVariant getFloorKey(int accId, MutableVariant key) throws WireException {
+ try {
+ Accessor acc = getAccessor(accId).accessor;
+ if (acc instanceof MapAccessor == false)
+ throw new WireException("Not a map");
+ MapAccessor ma = (MapAccessor) acc;
+ Binding keyBinding = key.getBinding();
+ Object floorKey = ma.getFloorKey(keyBinding, key.getValue());
+ if (floorKey==null) return new MutableVariant(Bindings.VOID, null);;
+ return new MutableVariant(keyBinding, floorKey);
+ } catch (AccessorException e) {
+ throw new WireException( e );
+ }
+ }
+
+ @Override
+ public MutableVariant getCeilingKey(int accId, MutableVariant key) throws WireException {
+ try {
+ Accessor acc = getAccessor(accId).accessor;
+ if (acc instanceof MapAccessor == false)
+ throw new WireException("Not a map");
+ MapAccessor ma = (MapAccessor) acc;
+ Binding keyBinding = key.getBinding();
+ Object ceilingKey = ma.getCeilingKey(keyBinding, key.getValue());
+ if (ceilingKey==null) return new MutableVariant(Bindings.VOID, null);;
+ return new MutableVariant(keyBinding, ceilingKey);
+ } catch (AccessorException e) {
+ throw new WireException( e );
+ }
+ }
+
+ @Override
+ public MutableVariant getHigherKey(int accId, MutableVariant key) throws WireException {
+ try {
+ Accessor acc = getAccessor(accId).accessor;
+ if (acc instanceof MapAccessor == false)
+ throw new WireException("Not a map");
+ MapAccessor ma = (MapAccessor) acc;
+ Binding keyBinding = key.getBinding();
+ Object higherKey = ma.getHigherKey(keyBinding, key.getValue());
+ if (higherKey==null) return new MutableVariant(Bindings.VOID, null);;
+ return new MutableVariant(keyBinding, higherKey);
+ } catch (AccessorException e) {
+ throw new WireException( e );
+ }
+ }
+
+ @Override
+ public boolean hasValue(int accId) throws WireException {
+ try {
+ Accessor acc = getAccessor(accId).accessor;
+ if (acc instanceof OptionalAccessor == false)
+ throw new WireException("Not an option");
+ OptionalAccessor ma = (OptionalAccessor) acc;
+ return ma.hasValue();
+ } catch (AccessorException e) {
+ throw new WireException( e );
+ }
+ }
+
+ @Override
+ public int getTag(int accId) throws WireException {
+ try {
+ Accessor acc = getAccessor(accId).accessor;
+ if (acc instanceof UnionAccessor == false)
+ throw new WireException("Not an union");
+ UnionAccessor ma = (UnionAccessor) acc;
+ return ma.getTag();
+ } catch (AccessorException e) {
+ throw new WireException( e );
+ }
+ }
+
+ @Override
+ public int addAll(int accId, int index, MutableVariant array) throws WireException {
+ try {
+ ArrayAccessor acc = (ArrayAccessor) getAccessor(accId).accessor;
+ Datatype type = array.type();
+ if (type instanceof ArrayType == false) {
+ throw new WireException("addAll() array expepected, got "+array.type());
+ }
+
+ Binding componentBinding = ( (ArrayBinding) array.getBinding() ).getComponentBinding();
+ ArrayBinding binding = new ObjectArrayBinding((ArrayType) type, componentBinding);
+ Object values[] = (Object[]) array.getValue(binding);
+ if (index==-1) {
+ int size = acc.size();
+ acc.addAll(binding, values);
+ return size;
+ } else {
+ acc.addAll(index, binding, values);
+ return index;
+ }
+
+ } catch (AccessorException e) {
+ throw new WireException( e );
+ } catch (AdaptException e) {
+ throw new WireException( e );
+ }
+ }
+
+ @Override
+ public int add(int accId, int index, MutableVariant value) throws WireException {
+ try {
+ ArrayAccessor acc = (ArrayAccessor) getAccessor(accId).accessor;
+
+ if (index==-1) {
+ int size = acc.size();
+ acc.add(value.getBinding(), value.getValue());
+ return size;
+ } else {
+ acc.add(index, value.getBinding(), value.getValue());
+ return index;
+ }
+ } catch (AccessorException e) {
+ throw new WireException( e );
+ }
+ }
+
+ @Override
+ public MutableVariant getArrayElement(int accId, int index) throws WireException {
+ try {
+ AccRecord ar = getAccessor(accId);
+ ArrayAccessor acc = (ArrayAccessor) ar.accessor;
+ Binding valueBinding = ((ArrayBinding)ar.binding).getComponentBinding();
+ Object value = acc.get(index, valueBinding );
+ MutableVariant v = new MutableVariant(valueBinding, value);
+ return v;
+ } catch (AccessorException e) {
+ throw new WireException( e );
+ }
+ }
+
+ @Override
+ public MutableVariant getMapValue(int accId, MutableVariant key) throws WireException {
+ try {
+ AccRecord ar = getAccessor(accId);
+ MapAccessor acc = (MapAccessor) ar.accessor;
+ MapBinding mapBinding = (MapBinding)ar.binding;
+ Object value = acc.get(key.getBinding(), key.getValue(), mapBinding.getValueBinding());
+ if (value == null) return new MutableVariant();
+ return new MutableVariant(mapBinding.getValueBinding(), value);
+ } catch (AccessorException e) {
+ throw new WireException( e );
+ }
+ }
+
+ @Override
+ public MutableVariant getMapValues(int accId) throws WireException {
+ try {
+ AccRecord ar = getAccessor(accId);
+ MapAccessor acc = (MapAccessor) ar.accessor;
+ MapBinding mapBinding = (MapBinding)ar.binding;
+ Binding valueBinding = mapBinding.getValueBinding();
+ Object[] values = acc.getValues( valueBinding );
+ ArrayBinding arrayBinding = new ObjectArrayBinding(new ArrayType(valueBinding.type()), valueBinding);
+ return new MutableVariant(arrayBinding, values);
+ } catch (AccessorException e) {
+ throw new WireException( e );
+ }
+ }
+
+ @Override
+ public MutableVariant getMapKeys(int accId) throws WireException {
+ try {
+ AccRecord ar = getAccessor(accId);
+ MapAccessor acc = (MapAccessor) ar.accessor;
+ MapBinding mapBinding = (MapBinding)ar.binding;
+ Binding keyBinding = mapBinding.getKeyBinding();
+ Object[] keys = acc.getKeys( keyBinding );
+ ArrayBinding arrayBinding = new ObjectArrayBinding(new ArrayType(keyBinding.type()), keyBinding);
+ return new MutableVariant(arrayBinding, keys);
+ } catch (AccessorException e) {
+ throw new WireException( e );
+ }
+ }
+
+}
+