]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/accessor/wire/WireServer.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / accessor / wire / WireServer.java
1 /*******************************************************************************
2  * Copyright (c) 2010- Association for Decentralized Information Management in
3  * Industry THTH ry.
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
8  * 
9  * Contributors:
10  *    VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.databoard.accessor.wire;
13
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;
20 import java.util.Map;
21
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;
59
60 /**
61  * WireServer exposes an accessor over TCP/IP socket. 
62  *
63  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
64  */
65 public class WireServer implements IWireServer {
66         
67         Accessor accessor;
68         Map<TcpConnection, ClientRecord> clients = Collections.synchronizedMap( new HashMap<TcpConnection, ClientRecord>() );
69         MethodInterface mi;
70         AccessorParams params = AccessorParams.DEFAULT;
71         
72         public WireServer(Accessor accessor) {
73                 this.accessor = accessor;
74                 try {
75                         this.mi = Methods.bindInterface(IWireServer.class, this);
76                 } catch (BindingConstructionException e) {
77                         throw new RuntimeException(e);
78                 }
79         }
80         
81         public MethodInterface getMethodInterface() {
82                 return mi;
83         }
84         
85         static class ClientRecord {
86                 TcpConnection connection;
87                 MethodInterface clientMethodInterface;
88                 Method onEventsMethod;
89                 IWireClient clientMethods;
90                 int accIdCounter = 0;
91                 int listIdCounter = 0;          
92                 BijectionMap<Integer, AccRecord> accessorTable = new BijectionMap<Integer, AccRecord>();
93                 Map<Integer, LisRecord> listenerTable = new HashMap<Integer, LisRecord>();
94                 
95                 public void dispose() {                 
96                 }               
97         }
98         
99         static class AccRecord {
100                 int accId;
101                 Accessor accessor;
102                 ChildReference reference;
103                 Datatype type;
104                 Binding binding;
105         }       
106         
107         static class LisRecord {
108                 int lisId;
109                 int accId;
110                 InterestSet is;
111                 Listener listener;
112         }
113         
114         static class OnEventsRequest {
115                 public @Optional Integer arg1;
116                 public @Optional Event[] arg2;
117         }
118             
119         /**
120          * Get or create new client handler associated with current thread.
121          * The result value depends on the current thread.  
122          * 
123          * @return client handler
124          * @throws WireException
125          */
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");
129                 try {
130                 synchronized(clients) {
131                         ClientRecord handler = clients.get( connection );
132                         if (handler == null) {
133
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);
140                                 
141                                 try {
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);
149                                 }
150                                 
151                                 connection.addConnectionListener( new ConnectionListener() {
152                                         @Override
153                                         public void onError(Exception error) {
154                                                 TcpConnection connection = TcpConnection.getCurrentConnection();
155                                                 ClientRecord handler = clients.remove(connection);
156                                                 if (handler != null) handler.dispose();
157                                         }
158                                         @Override
159                                         public void onClosed() {
160                                                 TcpConnection connection = TcpConnection.getCurrentConnection();
161                                                 ClientRecord handler = clients.remove(connection);
162                                                 if (handler != null) handler.dispose();
163                                         }});
164                                 clients.put(connection, handler);
165                         }
166                         return handler;
167                 }
168                 } catch (BindingConstructionException e) {
169                         throw new WireException( e );
170                 }
171         }
172
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");
177                 return acc;
178         }
179         
180         LisRecord getListener(int accId, int lisId) throws WireException {
181                 ClientRecord client = getClient();
182                 LisRecord lis = client.listenerTable.get(lisId);
183                 return lis;
184         }
185         
186         // Assumption is that each method is invoked in a connection thread.
187         // Therefore object synchronization is unnecessary.
188         
189         @Override
190         public AccessorInfo openAccessor(ChildReference ref) throws WireException {
191                 try {
192                         ClientRecord client = getClient();
193                         AccRecord acc = new AccRecord();
194                         acc.accessor = accessor.getComponent(ref);
195                         acc.accId = client.accIdCounter++;
196                         acc.reference = ref;
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;
202                         ai.type = acc.type;
203                         return ai;
204                 } catch (AccessorConstructionException e) {
205                         throw new WireException( e );
206                 } catch (BindingConstructionException e) {
207                         throw new WireException( e );
208                 }
209         }
210
211         @Override
212         public int closeAccessors(Integer[] accIds) throws WireException {
213                 ClientRecord client = getClient();
214                 for (Integer accId : accIds) {
215                         client.accessorTable.removeWithLeft(accId);
216                 }
217                 return 0;
218         }
219         @Override
220         public MutableVariant getValue(int accId) throws WireException {
221                 try {
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);
227                 }
228         }
229
230         @Override
231         public ApplyResult apply(int accId, Event[] changeSet, boolean rollback) {
232                 ApplyResult result = new ApplyResult();
233                 result.rollbackLog = rollback ? new LinkedList<Event>() : null;         
234                 try {
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) {
240                         result.error = e;
241                 } catch (AccessorException e) {
242                         result.error = new WireException(e);
243                 }               
244                 return result;
245         }
246
247         @Override
248         public int addListener(int accId, InterestSet interestSet,
249                         ChildReference path) throws WireException {
250                 try {
251                         
252                         ClientRecord client = getClient();
253                         AccRecord acc = getAccessor(accId);
254                         final LisRecord lis = new LisRecord();
255                         lis.accId = accId;
256                         lis.is = interestSet;
257                         lis.lisId = client.listIdCounter++;
258                         lis.listener = new Listener() {
259                                 @Override
260                                 public void onEvents(Collection<Event> events) {
261                                         try {
262                                                 // Push event to client
263                                                 ClientRecord client = getClient();
264                                                 Event[] eventArray = events.toArray( new Event[events.size()] );
265
266 //                                              client.clientMethods.onEvents(lis.lisId, eventArray);
267
268                                                 // Invoke async.
269                                                 OnEventsRequest req = new OnEventsRequest();
270                                                 req.arg1 = lis.lisId;
271                                                 req.arg2 = eventArray;
272                                                 client.onEventsMethod.invoke( req );
273                                         } catch (WireException e) {
274                                         }
275                                 }};
276                         acc.accessor.addListener(lis.listener, interestSet, path, null /*TODO Dispatch thread*/);
277                         client.listenerTable.put(lis.lisId, lis);
278                         return lis.lisId;
279                 } catch (AccessorException e) {
280                         throw new WireException(e.getClass().getName()+": "+e.getMessage());
281                 }               
282         }
283
284         @Override
285         public int removeListener(int lisId) throws WireException {
286                 try {
287                         ClientRecord client = getClient();              
288                         LisRecord lis = client.listenerTable.remove(lisId);
289                         AccRecord acc = getAccessor(lis.accId);
290                         acc.accessor.removeListener(lis.listener);
291                         return 0;
292                 } catch (AccessorException e) {
293                         throw new WireException( e ); 
294                 }               
295         }
296
297         @Override
298         public int size(int accId) throws WireException {
299                 try {
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 );
308                 }
309         }
310
311         @Override
312         public int clear(int accId) throws WireException {
313                 try {
314                         Accessor acc = getAccessor(accId).accessor;
315                         if (acc instanceof ArrayAccessor) {
316                                 ArrayAccessor aa = ((ArrayAccessor)acc); 
317                                 aa.remove(0, aa.size());
318                                 return 0;
319                         }
320                         if (acc instanceof MapAccessor) {
321                                 ((MapAccessor)acc).clear();
322                                 return 0;
323                         }                       
324                         throw new WireException("Cannot clear "+acc.getClass().getName());
325                 } catch (AccessorException e) {
326                         throw new WireException( e );
327                 }
328         }
329 /*
330         @Override
331         public void add(int accId, int index, Variant[] values) throws WireException {
332                 try {
333                         Accessor acc = getAccessor(accId).accessor;
334                         if (acc instanceof ArrayAccessor) {
335                                 ArrayAccessor aa = (ArrayAccessor) acc;
336                                 if (values.length==0) return;
337                                 
338                                 Binding b = values[0].getBinding();
339                                 boolean sameBinding = true;
340                                 for (Variant v : values) sameBinding &= v.getBinding()==b;
341                                 
342                                 if (sameBinding) {
343                                         Object[] objs = new Object[ values.length ];
344                                         for (int i=0; i<values.length; i++) {
345                                                 objs[i] = values[i].getValue();
346                                         }
347                                         aa.add(index, b, objs);                                 
348                                 } else {
349                                         // Add entry one by one
350                                         for (Variant v : values) {
351                                                 aa.add(index, v.getClass(), v.getValue());
352                                                 index++;
353                                         }
354                                 }
355                                 
356                         } else          
357                         throw new WireException("Cannot add to "+acc.getClass().getName());
358                 } catch (AccessorException e) {
359                         throw new WireException( e );
360                 }
361         }
362 */
363
364         @Override
365         public boolean containsKey(int accId, MutableVariant key) throws WireException {
366                 try {
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 );
374                 }
375         }
376
377         @Override
378         public boolean containsValue(int accId, MutableVariant value) throws WireException {
379                 try {
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 );
387                 }
388         }
389
390         @Override
391         public MutableVariant getFirstKey(int accId) throws WireException {
392                 try {
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 );
404                 }
405         }
406
407         @Override
408         public MutableVariant getLastKey(int accId) throws WireException {
409                 try {
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 );
421                 }
422         }
423
424         @Override
425         public MutableVariant getLowerKey(int accId, MutableVariant key) throws WireException {
426                 try {
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 );
437                 }       
438         }
439
440         @Override
441         public MutableVariant getFloorKey(int accId, MutableVariant key) throws WireException {
442                 try {
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 );
453                 }       
454         }
455
456         @Override
457         public MutableVariant getCeilingKey(int accId, MutableVariant key) throws WireException {
458                 try {
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 );
469                 }       
470         }
471
472         @Override
473         public MutableVariant getHigherKey(int accId, MutableVariant key) throws WireException {
474                 try {
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 );
485                 }       
486         }
487
488         @Override
489         public boolean hasValue(int accId) throws WireException {
490                 try {
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 );
498                 }       
499         }
500
501         @Override
502         public int getTag(int accId) throws WireException {
503                 try {
504                         Accessor acc = getAccessor(accId).accessor;
505                         if (acc instanceof UnionAccessor == false) 
506                                 throw new WireException("Not an union");
507                         UnionAccessor ma = (UnionAccessor) acc;
508                         return ma.getTag();
509                 } catch (AccessorException e) {
510                         throw new WireException( e );
511                 }       
512         }
513         
514         @Override
515         public int addAll(int accId, int index, MutableVariant array) throws WireException {
516                 try {
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());
521                         }
522                         
523                         Binding componentBinding = ( (ArrayBinding) array.getBinding() ).getComponentBinding();
524                         ArrayBinding binding = new ObjectArrayBinding((ArrayType) type, componentBinding);
525                         Object values[] = (Object[]) array.getValue(binding);
526                         if (index==-1) {
527                                 int size = acc.size();
528                                 acc.addAll(binding, values);
529                                 return size;
530                         } else {
531                                 acc.addAll(index, binding, values);
532                                 return index;
533                         }
534                                                 
535                 } catch (AccessorException e) {
536                         throw new WireException( e );
537                 } catch (AdaptException e) {
538                         throw new WireException( e );
539                 }
540         }
541
542         @Override
543         public int add(int accId, int index, MutableVariant value) throws WireException {
544                 try {
545                         ArrayAccessor acc = (ArrayAccessor) getAccessor(accId).accessor;
546
547                         if (index==-1) {
548                                 int size = acc.size();
549                                 acc.add(value.getBinding(), value.getValue());
550                                 return size;
551                         } else {
552                                 acc.add(index, value.getBinding(), value.getValue());
553                                 return index;
554                         }
555                 } catch (AccessorException e) {
556                         throw new WireException( e );
557                 }
558         }
559
560         @Override
561         public MutableVariant getArrayElement(int accId, int index) throws WireException {
562                 try {
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);
568                         return v;
569                 } catch (AccessorException e) {
570                         throw new WireException( e );
571                 }
572         }
573         
574         @Override
575         public MutableVariant getMapValue(int accId, MutableVariant key) throws WireException {
576                 try {
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 );
585                 }
586         }
587
588         @Override
589         public MutableVariant getMapValues(int accId) throws WireException {
590                 try {
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 );
600                 }
601         }
602
603         @Override
604         public MutableVariant getMapKeys(int accId) throws WireException {
605                 try {
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 );
615                 }
616         }
617         
618 }
619