Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/vrspace/server/ProxyDispatcher.java


1   package org.vrspace.server;
2   
3   import java.util.*;
4   import org.vrspace.util.*;
5   import org.vrspace.attributes.*;
6   import org.vrspace.server.db.*;
7   
8   /**
9   This dispatcher only forwards events to/from client/other host. <br>
10  But it should maintain a scene for all foreign objects, in order to reduce
11  network trafic and speed up event distribution, also keep local-remote
12  user mappings etc.
13  */
14  public class ProxyDispatcher extends Dispatcher {
15    protected static Hashtable connections = new Hashtable();
16    public boolean remove = false;
17    public boolean autocommit = true;
18    public ProxyDispatcher( Client gate, Client client ) throws Exception {
19      Logger.logDebug( "New ProxyDispatcher for "+client.getName()+" to "+gate.getName() );
20      server = client.getDispatcher().getServer(client);
21      
22      checkConnections( client );
23      startDB( client, gate.getName() );
24      
25      AuthInfo a = getAuthInfo( client, gate.getName() );
26      
27      Proxy proxy = new Proxy( client, client.dispatcher, gate, a.login, a.password);
28      
29      connected( client, a );
30      
31      connections.put( client, proxy );
32      // let client clean up the mess
33      //Util.sleep(3000);
34      // start transfer
35      proxy.connection.addObserver(proxy);
36      Logger.logDebug( "Proxy for "+client.getName()+" initialized" );
37  
38    }
39    public ProxyDispatcher( String host, int port, Client client ) throws Exception {
40      Logger.logDebug( "New ProxyDispatcher for "+client.getName()+" to "+host+":"+port );
41      
42      checkConnections( client );
43      startDB( client, host + "_" + port );
44      AuthInfo a = getAuthInfo( client, host+":"+port );
45      
46      Proxy proxy = new Proxy( client, client.dispatcher, host, port, a.login, a.password);
47      
48      connected( client, a );
49  
50      connections.put( client, proxy );
51      // let client clean up the mess
52      //Util.sleep(3000);
53      // start transfer
54      proxy.connection.addObserver(proxy); //?????
55      Logger.logDebug( "Proxy for "+client.getName()+" initialized" );
56    }
57    private void checkConnections( Client client ) {
58      Proxy proxy = (Proxy) connections.get( client );
59      server = client.getDispatcher().getServer(client);
60      Connection conn = null;
61      if ( proxy != null ) {
62        Logger.logError( client.getName()+" - another connection!!!" );
63        remove( proxy );
64      }
65    }
66    private void connected( Client client, AuthInfo a ) throws Exception {
67      if ( space != null ) {
68        space.put( a );
69        space.commit();
70      }
71      Logger.logDebug( "New Proxy for "+client.getName() );
72      // tell other users we're offline
73      client.online = false;
74      // clear the scene
75      client.scene.removeAll();
76      client.scene.clear();
77      // more setup
78      client.dispatcher = this;
79      // ok we can change to on-line now - they should know we're on another host
80      client.online = true;
81    }
82    private void startDB( Client client, String name ) {
83      try {
84        String dbUrl = client.getDispatcher().space.create( name );
85        DB newDB = (DB) Class.forName(server.getProperty("vrspace.db.class")).newInstance();
86        space = new DBCache( newDB );
87        space.connect( dbUrl );
88        Logger.logInfo( "Connected to proxy DB "+dbUrl );
89      } catch ( Throwable t ) {
90        Logger.logError( "Failed create/connect proxy DB for "+name, t );
91      }
92    }
93    private AuthInfo getAuthInfo( Client client, String name ) {
94      AuthInfo a = null;
95      try {
96        a = getAuthInfo( client );
97      } catch ( Throwable t ) {
98        // first login to remote host
99        Logger.logDebug( "No remote AuthInfo - "+t );
100     }
101     if ( a == null ) {
102       a = new AuthInfo( client.getName() + "@" + server.getProperty( "vrspace.server.name" ), ""+System.currentTimeMillis() );
103       a.className = client.getClassName();
104       a.id = client.db_id;
105       Logger.logDebug( "First visit to "+name+" by "+client.getName() );
106     }
107     return a;
108   }
109   /**
110   Return client to previous state
111   */
112   void remove( Proxy proxy ) {
113     proxy.connection.deleteObserver( proxy );
114     connections.remove( proxy.client );
115     try {
116       proxy.client.dispatcher = proxy.dispatcher;
117       //proxy.connection.close();
118       proxy.connection.quit();
119       // let client clean up the mess
120       //Util.sleep(3000);
121       proxy.client.scene = new Scene( proxy.client );
122       proxy.client.scene.update( proxy.client.pos );
123     } catch ( NullPointerException e ) {
124       // client disconnected
125     } catch ( Exception e ) {
126       // scene exception
127       Logger.logError("Error removing proxy "+proxy, e);
128     }
129     Logger.logInfo( "Remote session closed" );
130   }
131   /** start */
132   public void login( Client c ) throws ConnectionException {
133     Proxy proxy = (Proxy) connections.get( c );
134     proxy.login();
135   }
136   public class Proxy implements Observer {
137     Dispatcher dispatcher;
138     Client client;
139     Connection connection;
140     double x,y,z,rotx,roty,rotz,angle;
141     long startTime, lastReceived;
142     boolean identified = false;
143     long myId;
144     String myClass;
145     Queue queue = new Queue();
146     public Proxy( Client client,
147                   Dispatcher dispatcher,
148                   Client gate,
149                   //Connection connection,
150                   String login,
151                   String password )
152            throws ConnectionException
153     {
154       // remember user's coordinates
155       x = client.pos.x;
156       y = client.pos.y;
157       z = client.pos.z;
158       rotx = client.pos.rotx;
159       roty = client.pos.roty;
160       rotz = client.pos.rotz;
161       angle = client.pos.angle;
162       //this.connection = connection;
163       this.client = client;
164       this.dispatcher = dispatcher;
165       // connect
166       //connection = new PipedConnection(gate.session, login, password); // TEMP DISABLED
167       throw new RuntimeException( "Temporary disabled!" );
168     }
169     public Proxy( Client client,
170                   Dispatcher dispatcher, 
171                   String host,
172                   int port,
173                   String login,
174                   String password )
175            throws ConnectionException
176     {
177       // remember user's coordinates
178       x = client.pos.x;
179       y = client.pos.y;
180       z = client.pos.z;
181       rotx = client.pos.rotx;
182       roty = client.pos.roty;
183       rotz = client.pos.rotz;
184       angle = client.pos.angle;
185       // connect
186       connection = new Connection(host, port, login, password);
187       this.client = client;
188       this.dispatcher = dispatcher;
189     }
190     public void login() throws ConnectionException {
191       ((PipedConnection) connection).login();
192     }
193     /**
194     Listens connection to remote server and forwards to client's session
195     */
196     public synchronized void update( Observable conn, Object msg ) {
197       //Logger.logDebug( "Remote message: "+msg );
198       if ( msg instanceof String ) {
199         if ( ! identified ) {
200           Message message = new Message( (String) msg );
201           identified = message.getClassName().equals("YouAre");
202           if ( identified ) {
203             myClass = message.getEventValue();
204             myId = message.getId();
205             try {
206               VRObject myInstance = get( client, myClass, myId );
207               if ( myInstance == null ) {
208                 myInstance = VRObject.newInstance( myClass );
209                 myInstance.db_id = myId;
210                 put( client, myInstance );
211               }
212             } catch ( Exception e ) {
213               Logger.logError( "Can't store my instance", e );
214             }
215             // set users avatar
216             try {
217               Object url = client.getClass().getField("url").get( client );
218               connection.send( message.getEventValue()+" "+message.getId()+" url "+url );
219             } catch ( Throwable t ) {
220               Logger.logDebug( "Cannot set avatar url: "+t );
221             }
222             startTime = System.currentTimeMillis();
223             Logger.logInfo( client.getID()+" "+client.getName()+" - remote ID "+ myClass + " " + myId );
224             while ( queue.size() > 0 ) {
225               Request next = new Request( client, (String) queue.remove() );
226               try {
227                 remoteRequest( next );
228               } catch ( Throwable t ) {
229                 Logger.logError("Error processing "+next, t);
230               }
231             }
232             if ( client.session != null ) {
233               //client.session.write( (String) msg );
234               client.session.write( "", message );
235             }
236           } else {
237             // cache requests
238             queue.add( msg );
239           }
240         } else {
241           if ( msg instanceof String && ((String)msg).indexOf("+quit") == 0) {
242             // quit request - return to previous world
243             // don't send this back to client - let remote host clears
244             // the scene first, and than break the connection
245             // maybe send 'busy'?
246             // send something to the gate?
247             // user position like entering portal
248             client.pos.x = x;
249             client.pos.y = y;
250             client.pos.z = z;
251             // turn the user on the other side, like exiting the portal :)
252             // USE PORTAL ORIENTATION AND POSITION!!!
253             client.pos.rotx = rotx;
254             client.pos.roty = roty;
255             client.pos.rotz = rotz;
256             client.pos.angle = angle;
257           } else if ( msg instanceof Exception ) {
258             Logger.logError("Received exception",(Exception)msg);
259             client.pos.x = x;
260             client.pos.y = y;
261             client.pos.z = z;
262             // turn the user on the other side, like exiting the portal :)
263             // USE PORTAL ORIENTATION AND POSITION!?
264             client.pos.rotx = rotx;
265             client.pos.roty = roty;
266             client.pos.rotz = rotz;
267             client.pos.angle = angle;
268           } else {
269             try {
270               //Logger.logDebug( "Forwarding message: "+msg );
271               remoteRequest( new Request( client, (String) msg ) );
272             } catch ( RequestException reqE ) {
273               Logger.logError( "Error processing remote request: "+msg, reqE );
274             }
275             lastReceived=System.currentTimeMillis();
276           }
277         }
278       } else if ( msg instanceof Throwable ) {
279         Logger.logError("Received exception",(Throwable)msg);
280       } else {
281         Logger.logError( "Invalid message: "+msg );
282       }
283       if ( ! connection.isActive() ) {
284         remove( this );
285         space.disconnect();
286         Logger.logDebug( "Connection closed, db disconnected" );
287       }
288     }
289     /**
290     Forwards client's request to remote host - update local db here?
291     */
292     public void send( Request r ) {
293       //Logger.logDebug( "Forwarding client request ("+r.getClassName()+" "+r.getId()+" "+r.getEventName()+" "+r.getEventValue()+"): "+r );
294       // ignore all requests for some time
295       //if ( identified && System.currentTimeMillis() > 10000 + startTime ) {
296       if ( identified ) {
297         if ( r.getClassName().equals( client.getClassName() ) && r.getId() == client.db_id ) {
298           r = new Request( client, myClass + " " + myId + " " + r.getEventName() + "  " + r.getEventValue() );
299         }
300         connection.send( r.toString() );
301       } else {
302         Logger.logWarning( "Ignored request "+r );
303       }
304     }
305   }
306 
307   /**
308   Forward Request to the Client
309   */
310   protected void forward( Request r ) {
311     if ( r.getClient().session != null ) {
312       //client.session.sendRequest( r.toString() );
313       r.getClient().session.sendRequest( r );
314       //Logger.logDebug( "Forwarded message: "+r );
315     }
316   }
317   /**
318   Process remote request and forward it to the client
319   */
320   synchronized void remoteRequest( Request r ) throws RequestException {
321     //Logger.logDebug( "Forwarding message: "+r );
322     //super.request( r );
323     Client client = r.getClient();
324     try {
325       if ( r.getId() > -1 ) {
326         VRObject obj = r.object;
327         if ( obj == null ) {
328           try {
329             obj = (VRObject)space.get( r.getClassName(), r.getId() );
330             // cached
331           } catch ( Exception dbE ) {
332             Logger.logError( "Error fetching "+r, dbE );
333           }
334         }
335         if (obj == null) {
336           // object not in database - maybe Add/Remove
337           //obj = (VRObject) r.getClient().getClassLoader().loadClass( r.getClassName() ).newInstance();
338           //obj.db_id = r.getId();
339           obj = VRObject.fromText( r.toString() )[0];
340           //Logger.logDebug( client.getID()+" got event "+r );
341         }
342         // simple privilege check:
343         if ( client instanceof Admin ) {
344           obj = VRObject.fromText( r.toString() )[0];
345           if ( obj instanceof Remove ) {
346             if ( remove ) {
347               obj.sendEvent( r );
348               space.commit();
349             } else {
350               //Logger.logDebug( "Won't "+obj );
351             }
352           } else if ( obj instanceof Add ) {
353             VRObject child = get( client, ((Add)obj).className, ((Add)obj).db_id );
354             if ( child == null ) {
355               // new object
356               obj.sendEvent( r );
357               space.commit();
358             }
359             // else ignore - we already have that in local db
360           }
361           // else done later
362         }
363         // Admin & others need foreign objects in local scene:
364         if ( obj instanceof Remove ) {
365           VRObject child = get( client, ((Remove)obj).className, ((Remove)obj).db_id );
366           if ( child instanceof Remove ) {
367             throw new RequestException( r, client.getID()+" got invalid remote request: "+child );
368           }
369           if ( child != null ) {
370             //Logger.logDebug( client.getID()+": removing "+child );
371             client.removeObject( child );
372           } else {
373             Logger.logWarning( client.getID()+" can't "+obj+" - not found. Forwarding message to the client." );
374             forward( r );
375           }
376         } else if ( obj instanceof Add ) {
377           VRObject child = get( client, ((Add)obj).className, ((Add)obj).db_id );
378           if ( child instanceof Add ) {
379             throw new RequestException( r, client.getID()+" got invalid remote request: "+child );
380           }
381           try {
382             //Logger.logDebug( client.getID()+": adding "+child );
383             if ( child == null ) {
384               child = VRObject.newInstance( ((Add)obj).className );
385               child.db_id = ((Add)obj).db_id;
386               space.put( child );
387             }
388             // Note: at this point, object may have no properties at all
389             // we can check this with VRObject.toText() ( length() == 0 )
390             // but it is not necessary since Session ignores empty requests - any property change forwards anyway as Client.addObject() adds the client as Observer
391             client.addObject( child );
392           } catch ( Throwable t ) {
393             Logger.logError( client.getID()+" can't "+obj+" - not found. Forwarding message to the client.", t );
394             forward( r );
395           }
396         } else { //if ( ! (obj instanceof Add) && ! (obj instanceof Remove) )
397           // only Admin can set local properties
398           r.object = obj;
399           Proxy proxy = ((Proxy) connections.get( client ));
400           if ( obj != null && obj.getId() == proxy.myId && obj.getClassName().equals( proxy.myClass ) ) {
401             // my request - map to my properties:
402             // just in case:
403             r.setClass( proxy.myClass );
404             r.setId( proxy.myId );
405             if ( obj instanceof Client ) {
406               Logger.logDebug( obj.getID()+": my property - "+r );
407               forward( r );
408               // we have to trick OwnedDBObjects and stuff
409               r.client = (Client) obj;
410               obj.sendEvent( r );
411             }
412             /*
413             // copy foreign properties to this client - RISK! CKECKME
414             r.setClass( r.getClient().getClassName() );
415             r.setId( r.getClient().getId() );
416             r.object = r.getClient();
417             r.getClient().sendEvent( r );
418             */
419           } else {
420             if ( client instanceof Admin ) {
421               //Logger.logDebug( client.getID()+" dispatching "+r );
422               obj.sendEvent( r ); // this doesn't work!!! Seems that Admin does not observe this object... so workaround:
423               forward( r );
424             } else {
425               // other clients can't set any object properties - forward only
426               // (otherwise Client.addObject/removeObject calls forward to the client)
427               forward( r );
428               //Logger.logDebug( client.getID()+" got unwanted message from remote object: "+obj );
429             }
430           }
431           //space.put( obj );  // admin only
432         }
433         //space.commit(); //admin only - removeme
434         // this if has to come after sendRequest()! Request.toString() depends on object existence.
435         r.object = obj;
436       }
437     } catch ( Throwable e ) {
438       Logger.logError("Can't forward ("+r.getClassName()+" "+r.getId()+")"+r, e);
439     }
440   }
441   /** 
442   Process request from the client. This forwards request to remote host, changes local client ID to remote ID if necessary
443   */
444   public void request( Request r ) throws RequestException {
445     // log remote requests?
446     //RequestLog.logRequest(r);  // forget it till it works well
447     //Logger.logDebug( "Dispatching: "+r.getClassName()+"["+r.getId()+"]."+r.getEventName()+"="+r.getEventValue()+" by "+r.getClient().getID() );
448     Proxy proxy = ((Proxy) connections.get( r.getClient() ));
449     if ( r.getId() == proxy.myId && r.getClassName().equals( proxy.myClass )) {
450       //Logger.logDebug( r.getClient().getID()+" - my remote property changed: "+r );
451     } else {
452       // fixit
453       if ( proxy.client == r.getClient() ) {
454         r = (Request) r.clone();
455         //Logger.logDebug( proxy.client.getID()+" Dispatching: "+r );
456         if ( r.getId() > 0 ) {
457           r.setClass( proxy.myClass );
458           r.setId( proxy.myId );
459           //Logger.logDebug( r.getClient().getID()+": Fixed request - "+r );
460         } else {
461           // command - forward only
462           // execute locally - TODO
463         }
464       }
465     }
466     proxy.send(r);
467   }
468 
469   /**
470   This should not be called
471   */
472   synchronized public Client login( Session session,
473                                     String login,
474                                     String password,
475                                     boolean daemon
476                                   )
477                              throws Exception
478   {
479     throw new UnsupportedOperationException ( "Don't call this method!" );
480   }
481 
482   /**
483   Logout Client <b>c</b> from remote host, session is not used
484   */
485   protected void logout( Client c, Session s ) {
486     Proxy proxy = (Proxy) connections.get( c );
487     try {
488       proxy.connection.close();
489     } catch ( Throwable t ) {
490       Logger.logError("Error in logout", t);
491     }
492     proxy.dispatcher.logout( c, s );
493     remove( proxy );
494   }
495 
496   /**
497   Called from server upon shutdown
498   */
499   protected void shutdown() {
500     throw new UnsupportedOperationException ( "Should I support this?" );
501   }
502 }