001: /*
002: * Copyright (c) 2001 Silvere Martin-Michiellot All Rights Reserved.
003: *
004: * Silvere Martin-Michiellot grants you ("Licensee") a non-exclusive,
005: * royalty free, license to use, modify and redistribute this
006: * software in source and binary code form,
007: * provided that i) this copyright notice and license appear on all copies of
008: * the software; and ii) Licensee does not utilize the software in a manner
009: * which is disparaging to Silvere Martin-Michiellot.
010: *
011: * This software is provided "AS IS," without a warranty of any kind. ALL
012: * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
013: * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
014: * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. Silvere Martin-Michiellot
015: * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES
016: * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
017: * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
018: * Silvere Martin-Michiellot OR ITS LICENSORS BE LIABLE
019: * FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
020: * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
021: * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
022: * OR INABILITY TO USE SOFTWARE, EVEN IF Silvere Martin-Michiellot HAS BEEN
023: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
024: *
025: * This software is not designed or intended for use in on-line control of
026: * aircraft, air traffic, aircraft navigation or aircraft communications; or in
027: * the design, construction, operation or maintenance of any nuclear
028: * facility. Licensee represents and warrants that it will not use or
029: * redistribute the Software for such purposes.
030: *
031: * @Author: Silvere Martin-Michiellot
032: *
033: */
034:
035: package com.db.client;
036:
037: import com.db.hanim.*;
038: import com.db.net.*;
039: import java.net.*;
040: import java.io.*;
041: import java.security.*;
042: import java.util.*;
043: import javax.net.ssl.*;
044: import javax.media.j3d.*;
045:
046: import com.sun.j3d.utils.scenegraph.io.*;
047: import com.sun.net.ssl.*;
048: import com.db.server.ClientInformation;
049:
050: /**
051: * This is the black box of the client side. It provides network capabilities but no GUI interface.
052: */
053:
054: public class Client implements Runnable {
055:
056: //There should be a small network cache in the client that is used to receive information sent from the corresponding out cache. This cache should pass
057: //information to the client as it locally requests it. On the opposite way, preemptive queries should be made by the clients to the server to assure proper
058: //display of user surrounding geometry. Pre fetch should be based on local time (don't overload Internet when the traffic is busy) as well as client processor
059: //(don't pre-fetch hundred of objects is client has not the ability to keep them). Other optimizations features could include pre-fetching user's preferred places
060: //geometry.
061:
062: //this is the Client Network Part
063: //its task is to send requests and get replies and notifications from the server
064: //it does build and updates continuously a Java3D BranchGroup based on the ClientInformation
065:
066: private ClientInformation clientInformation;
067:
068: private boolean connected;
069:
070: private BranchGroup branchGroup;
071:
072: //private Socket socket;
073: private SSLSocket sSLSocket;
074: private ObjectOutputStream objectOutputStream;
075: private ObjectInputStream objectInputStream;
076:
077: private final static int buffer = 2048;
078:
079: private Thread clientLoop;
080:
081: private javax.media.j3d.Locale locale;
082:
083: private long currentID;
084:
085: //use a reference to the locale to be used in the GUI part of the Client
086: public Client(javax.media.j3d.Locale locale) {
087:
088: this .locale = locale;
089: this .branchGroup = new BranchGroup();
090:
091: this .locale.addBranchGraph(branchGroup);
092:
093: this .currentID = 0;
094:
095: this .connected = false;
096:
097: }
098:
099: public javax.media.j3d.Locale getLocale() {
100:
101: return this .locale;
102:
103: }
104:
105: //acquire login via GUI
106: //select server via well known server and get position
107: //compute or set via GUI the drawingRadius and connectionSpeed
108: //then connect (with SSL)
109: //asks a Universe server to build up a new account or use existing one
110: //server delegates this responsability to ruler
111: //using given login and password and public key
112: //default skin is used and it is up to the user to change its skin at a later time once logged
113: //pass, tickets, inventory are set to null (unless the ruler decides otherwise)
114: //returns the account or the reason of failure
115: public void openConnection(ClientInformation clientInformation) {
116:
117: SSLSocketFactory sSLSocketFactory;
118: SSLContext sSLContext;
119: KeyManagerFactory keyManagerFactory;
120: KeyStore keyStore;
121: char[] passphrase;
122: Authentification authentification;
123:
124: if (!connected) {
125: this .clientInformation = clientInformation;
126:
127: try {
128: // set up a key manager so that we can do client authentication
129: // if asked by server
130: try {
131: passphrase = "passphrase".toCharArray();
132:
133: sSLContext = SSLContext.getInstance("TLS");
134: keyManagerFactory = KeyManagerFactory
135: .getInstance("SunX509");
136: keyStore = KeyStore.getInstance("JKS");
137:
138: //XXXX
139: //testkeys should be taken from clientInformation
140: keyStore.load(new FileInputStream("testkeys"),
141: passphrase);
142:
143: keyManagerFactory.init(keyStore, passphrase);
144: sSLContext.init(keyManagerFactory.getKeyManagers(),
145: null, null);
146:
147: sSLSocketFactory = sSLContext.getSocketFactory();
148: } catch (Exception exception) {
149: throw new IOException(exception.getMessage());
150: }
151:
152: //what it would be with a Socket
153: //socket = new Socket(clientInformation.getServerIP(), clientInformation.getServerPort(), clientInformation.getClientIP(), clientInformation.getClientPort());
154: sSLSocket = (SSLSocket) sSLSocketFactory.createSocket(
155: clientInformation.getServerIP(),
156: clientInformation.getServerPort(),
157: clientInformation.getClientIP(),
158: clientInformation.getClientPort());
159:
160: authentification = new Authentification(
161: clientInformation.getLoginInformation(),
162: Authentification.RECONNECT, clientInformation
163: .getServerIP(), clientInformation
164: .getServerPort(), clientInformation
165: .getPosition());
166:
167: objectInputStream = new ObjectInputStream(
168: new BufferedInputStream(sSLSocket
169: .getInputStream(), buffer));
170: objectOutputStream = new ObjectOutputStream(
171: new BufferedOutputStream(sSLSocket
172: .getOutputStream(), buffer));
173:
174: // send request
175: objectOutputStream.writeObject(authentification);
176: clientLoop = new Thread(this , "DB Client Network Loop");
177: clientLoop.start();
178: this .connected = true;
179:
180: } catch (Exception exception) {
181: exception.printStackTrace();
182: }
183:
184: }
185:
186: }
187:
188: public void run() {
189:
190: NetworkElement networkElement;
191:
192: //receive networkElements
193:
194: try {
195: networkElement = (NetworkElement) objectInputStream
196: .readObject();
197: this .handleMessage(networkElement);
198: }
199: //OptionalDataException, ClassNotFoundException, IOException
200: catch (Exception exception) {
201: }
202:
203: }
204:
205: //incoming buffer (replies and activity from other clients including notifications)
206: //server should automatically send messages when activity from ToolWorlds, GuideWorlds, Avatars has changed the area surrounding user
207: //receive and pass notifications to the GUI client side
208: //including disk caching
209: //world building, updating and deleting
210: public void handleMessage(NetworkElement networkElement) {
211:
212: //receive objects
213: long requestId;
214: Transform3D transform3D;
215: Node node;
216: Sound sound;
217: Object status;
218: int objectReference;
219: Hashtable information;
220: float price;
221: String currency;
222:
223: TransformGroup transformGroup;
224: Enumeration enumeration;
225: boolean found;
226: int index;
227:
228: SceneGraphStreamReader sceneGraphStreamReader;
229: HashMap namedObjects;
230:
231: //sceneGraphStreamReader = new SceneGraphStreamReader(objectInputStream);
232: //namedObjects = new HashMap();
233: //networkBranchGroup = sceneGraphStreamReader.readBranchGraph(namedObjects);
234:
235: requestId = networkElement.getRequestId();
236: transform3D = networkElement.getTransform3D();
237: node = networkElement.getGeometryDescription();
238: sound = networkElement.getSoundDescription();
239: objectReference = networkElement.getObjectReference();
240: //unused fields
241: status = networkElement.getStatus();
242: information = networkElement.getInformation();
243: price = networkElement.getPrice();
244: currency = networkElement.getCurrency();
245:
246: //add modify or remove geometry depending on the reference
247: enumeration = branchGroup.getAllChildren();
248: index = -1;
249: found = false;
250:
251: while (enumeration.hasMoreElements() && (!found)) {
252: node = (Node) enumeration.nextElement();
253: found = (((Integer) node.getUserData()).intValue() == objectReference);
254: index++;
255: }
256:
257: if (found) {
258: //object exists: update or delete
259: branchGroup.removeChild(index);
260: if ((node != null) || (sound != null)) {
261: transformGroup = new TransformGroup();
262: transformGroup.setTransform(transform3D);
263: transformGroup
264: .setUserData(new Integer(objectReference));
265: transformGroup.addChild(node);
266: transformGroup.addChild(sound);
267: branchGroup.addChild(transformGroup);
268: }
269: } else {
270: //add new object
271: if ((node != null) || (sound != null)) {
272: transformGroup = new TransformGroup();
273: transformGroup.setTransform(transform3D);
274: transformGroup
275: .setUserData(new Integer(objectReference));
276: transformGroup.addChild(node);
277: transformGroup.addChild(sound);
278: branchGroup.addChild(transformGroup);
279: }
280: }
281:
282: }
283:
284: //given local time (network traffic), client performance (processor, memory, graphics), prefered places,
285: //prefetch some ObjectWorlds, Avatars, BackgroundWorlds
286: //fetch using the DrawingRadius around the current user position ObjectWorlds, Avatars, BackgroundWorlds
287: //fetch also the avatar skin and inventory and make it available to user
288: //do it once at startup and UPDATE every time the user moves
289: public void prefetch() {
290:
291: //XXXXX
292:
293: }
294:
295: //disk cache to store ObjectWorlds, Avatars, BackgroundWorlds
296: //client first should check if ObjectSignature has changed by quering the server and if not use the copy in disk cache
297: public void diskcache() {
298:
299: //XXXXX
300:
301: }
302:
303: //maintain a list of unanswered requests so that the client can ressend them
304: public void resendPendingRequests() {
305:
306: //XXXXX
307:
308: }
309:
310: //notify my actions in the distant world server (and other clients)
311: public void notify(TransformGroup transformGroup, Object status) {
312:
313: SceneGraphStreamWriter sceneGraphStreamWriter;
314: BranchGroup networkBranchGroup;
315: HashMap namedObjects;
316: NetworkElement networkElement;
317: Transform3D transform3D;
318:
319: //sceneGraphStreamWriter = new SceneGraphStreamWriter(objectOutputStream);
320: //namedObjects = new HashMap();
321: //networkBranchGroup = new BranchGroup();
322: //networkBranchGroup.addChild(transformGroup);
323: //sceneGraphStreamWriter.writeBranchGraph(networkBranchGroup, namedObjects);
324: //sceneGraphStreamWriter.close();
325:
326: //send an update depending on the updated transformGroup object
327: //status is updated by using tools or whatever the client chooses as appropriate
328: transform3D = new Transform3D();
329: transformGroup.getTransform(transform3D);
330: networkElement = new NetworkElement(this .generateRequestID(),
331: transform3D, transformGroup.getChild(0),
332: (Sound) transformGroup.getChild(1), status,
333: ((Integer) transformGroup.getUserData()).intValue());
334: try {
335: objectOutputStream.writeObject(networkElement);
336: } catch (IOException iOException) {
337: }
338:
339: }
340:
341: public boolean isConnected() {
342:
343: return this .connected;
344:
345: }
346:
347: //closes current session
348: public void closeConnection() {
349:
350: if (this .connected) {
351:
352: //should warn the server
353: //XXXX
354: this .connected = false;
355: try {
356: if (sSLSocket != null) {
357: objectOutputStream.flush();
358:
359: objectInputStream.close();
360: objectOutputStream.close();
361:
362: sSLSocket.close();
363: }
364: } catch (IOException iOException) {
365: }
366: objectInputStream = null;
367: objectOutputStream = null;
368: sSLSocket = null;
369:
370: }
371:
372: }
373:
374: //compute a new ID for the request based on the client own internal ID
375: //and previous ID used (from which this ID must be different)
376: //every request should therefore has a unique ID for the server
377: private long generateRequestID() {
378:
379: long sum;
380:
381: sum = this .clientInformation.getClientIP().hashCode()
382: + this .currentID;
383:
384: this .currentID++;
385:
386: return ((sum * sum + 1) / 2) + this .currentID;
387:
388: }
389:
390: private String defaultDirectory() {
391:
392: return System.getProperty("user.dir");
393:
394: }
395:
396: }
|