001: /* ----- BEGIN LICENSE BLOCK -----
002: * Version: MPL 1.1
003: *
004: * The contents of this file are subject to the Mozilla Public License Version
005: * 1.1 (the "License"); you may not use this file except in compliance with
006: * the License. You may obtain a copy of the License at
007: * http://www.mozilla.org/MPL/
008: *
009: * Software distributed under the License is distributed on an "AS IS" basis,
010: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
011: * for the specific language governing rights and limitations under the
012: * License.
013: *
014: * The Original Code was the Rendezvous client.
015: * This Code is now part of DataShare.
016: *
017: * The Initial Developer of the Original Code is
018: * Ball Aerospace & Technologies Corp, Fairborn, Ohio
019: * Portions created by the Initial Developer are Copyright (C) 2001
020: * the Initial Developer. All Rights Reserved.
021: *
022: * Contributor(s): Charles Wood <cwood@ball.com>
023: *
024: * ----- END LICENSE BLOCK ----- */
025: /* RCS $Id: DataShareClient.java,v 1.2 2002/01/29 20:55:59 lizellaman Exp $
026: * $Log: DataShareClient.java,v $
027: * Revision 1.2 2002/01/29 20:55:59 lizellaman
028: * Added LoggingInterface, modified the PropertiesInterface implementation
029: *
030: * Revision 1.1 2002/01/03 03:21:36 lizellaman
031: * existing file, moved to client package
032: *
033: * Revision 1.1.1.1 2001/10/23 13:43:46 lizellaman
034: * initial sourceforge release
035: *
036: */
037:
038: package org.datashare.client;
039:
040: import org.datashare.objects.DataShareConnectionDescriptor;
041: import org.datashare.objects.ActivateConnectionObject;
042: import org.datashare.objects.DataShareObject;
043: import org.datashare.objects.ChannelDescription;
044: import org.datashare.objects.ChannelDescriptionArray;
045: import org.datashare.objects.RegistrationInfo;
046: import org.datashare.objects.RequestTree;
047: import org.datashare.objects.ServerInfo;
048: import org.datashare.objects.ClientSessionInfo;
049: import org.datashare.objects.CreateSessionRequest;
050: import org.datashare.objects.RegisterTreeListener;
051: import org.datashare.objects.TreeObject;
052: import org.datashare.objects.UpdateAvailableMsg;
053: import org.datashare.SessionUtilities;
054:
055: import java.net.InetAddress;
056: import java.util.Arrays;
057:
058: /**
059: * This class will provide the methods necessary to establish the commandStatusChannel
060: * to a DataShare server, activate it, and create a data channel for sharing data (no history).
061: * The parent class needs to provide the server, the commandStatusPort,
062: * a user name, password, an enterprise (for organizing clients/sessions if there are lots),
063: * a session name/description, and a channel name/description/type.
064: */
065: public class DataShareClient implements ClientDataReceiverInterface {
066: private DataShareClientInterface rci; // who we will pass data on to and will notify about errors
067: private String userName; // the name that this client wishes to be known by (may be modified by the DataShareServer
068: private String password; // password for username's account
069: public String sessionName; // the session that we are to use (all our Sessions only have one Channel)
070: private String sessionDescription;
071: private String enterpriseName; // used to organize the client in the server's tree of Clients
072: public String clientUniqueName = null; // clientName that has been made unique by the DataShareServer
073: private String publicInfo; // information about this user that is to be shared with other users
074: private InetAddress serverInetAddress = null; // will be set to our server
075: private String clientClass;
076:
077: // these have to do with the connection to the server's command status channel
078: private DataShareConnectionDescriptor commandStatusConnectionDescriptor = null;
079: private DataShareConnection commandStatusConnection = null;
080: private int serverCommandStatusPort = 42444; // this value must match the one used by the DataShareServer!!!
081:
082: // these have to do with the data connection
083: private DataShareConnectionDescriptor dataConnectionDescriptor = null;
084: private DataShareConnection dataConnection = null;
085: private String channelName;
086: private String channelDescription;
087: private int channelType;
088:
089: /**
090: * Constructor
091: */
092: public DataShareClient() {
093: }
094:
095: /**
096: * causes connections to DataShareServer to be established
097: * @param channelType must be ChannelDescription.MULTICAST, ChannelDescription.TCP, or
098: * ChannelDescription.UDP
099: */
100: public void initialize(DataShareClientInterface rci,
101: InetAddress serverInetAddress, int serverCommandStatusPort,
102: String sessionName, String sessionDescription,
103: String userName, String password, String publicInfo,
104: String enterpriseName, String channelName,
105: String channelDescription, int channelType) {
106: this .rci = rci;
107: clientClass = rci.getClass().toString();
108: if (clientClass.startsWith("class "))
109: clientClass = clientClass.substring(5); // skip past 'class '
110: this .serverInetAddress = serverInetAddress;
111: this .serverCommandStatusPort = serverCommandStatusPort;
112: this .sessionName = sessionName;
113: this .sessionDescription = sessionDescription;
114: this .userName = userName;
115: this .password = password;
116: this .publicInfo = publicInfo;
117: this .enterpriseName = enterpriseName;
118: this .channelName = channelName;
119: this .channelDescription = channelDescription;
120: this .channelType = channelType;
121: connectToServer();
122: }
123:
124: /**
125: * calling this will connect this client to the DataShareServer commandStatusPort
126: */
127: private void connectToServer() {
128: commandStatusConnectionDescriptor = new DataShareConnectionDescriptor(
129: "", /* sessionName, not used for commandStatus */
130: new ChannelDescription(), /* channelDescription */
131: serverInetAddress, /* serverIP */
132: serverCommandStatusPort); /* serverPort */
133: try {
134: //System.out.println("creating commandStatus connection...");
135: commandStatusConnection = new DataShareConnection(
136: commandStatusConnectionDescriptor, this );
137: RegistrationInfo ri = new RegistrationInfo(userName, /* clientUserName */
138: password, /* password */
139: enterpriseName, /* enterpriseName */
140: clientClass, /* class of main client */
141: RegistrationInfo.UNKNOWN, /* clientMode */
142: publicInfo); /* otherInfo */
143: //System.out.println("Sending a RegistrationInfo object to the server...");
144: commandStatusConnection.sendToAll(ri); // doesn't matter which way we send it, just get it to the server
145: // we should get a ServerInfo object back shortly
146: //System.out.println("Waiting for the server to send us a ServerInfo object...");
147: } catch (Exception e) {
148: System.out
149: .println("Problems creating our commandStatus connection...");
150: e.printStackTrace();
151: rci.commandStatusConnectionError(true, e.getMessage());
152: rci.dataChannelIsReady(false);
153: shutDown();
154: }
155: }
156:
157: /**
158: * Use this method to send an object to everybody in a Channel (including our instance)
159: */
160: public void sendDataToAll(Object object) {
161: if (dataConnection != null)
162: dataConnection.sendToAll(object);
163: else
164: System.out
165: .println("Trying to send data before the data connection is ready");
166: }
167:
168: /**
169: * Use this method to send an object to everybody in a Channel (excluding our instance)
170: */
171: public void sendDataToOthers(Object object) {
172: if (dataConnection != null)
173: dataConnection.sendToOthers(object);
174: else
175: System.out
176: .println("Trying to send data before the data connection is ready");
177: }
178:
179: /**
180: * Use this method to send an object to one client in a Channel
181: */
182: public void sendDataToClient(Object object,
183: String destinationClientKey) {
184: if (dataConnection != null)
185: dataConnection.sendToClient(object, destinationClientKey);
186: else
187: System.out
188: .println("Trying to send data before the data connection is ready");
189: }
190:
191: /**
192: */
193: private void createDataChannel() {
194: ChannelDescription cd = new ChannelDescription(channelName,
195: channelDescription, ChannelDescription.TCP);
196: ChannelDescription cda1[] = { cd };
197: ChannelDescriptionArray cda = new ChannelDescriptionArray(cda1);
198: // first, we need to create a description of our session on the server
199: CreateSessionRequest csr = new CreateSessionRequest(
200: sessionName, sessionDescription, false, // sessionIsPrivate
201: true, // autoDelete any empty Channels
202: cda, true); // join any session that looks like this one
203: commandStatusConnection.sendToAll(csr); // doesn't matter which way we send it, just get it to the server
204: }
205:
206: /**
207: * this method is called when this instance is supposed to clean up and shutdown
208: */
209: public void shutDown() {
210: try {
211: if (dataConnection != null) {
212: rci.dataChannelIsReady(false);
213: dataConnection.closeAll();
214: dataConnection = null;
215: }
216: } catch (Exception e) {
217: }
218: try {
219: if (commandStatusConnection != null) {
220: commandStatusConnection.closeAll();
221: commandStatusConnection = null;
222: }
223: } catch (Exception e) {
224: }
225:
226: }
227:
228: /**
229: * call this method if you want to be notified when clients/sessions come and go via
230: * the updateReceived() method.
231: */
232: public void registerForUpdates(boolean allChanges) {
233: commandStatusConnection.sendToAll(new RegisterTreeListener(
234: allChanges)); // we want updates
235: }
236:
237: /**
238: * call this if you want the server to send a full tree. The tree type will be based on the last
239: * call to registerForUpdates().
240: */
241: public void requestNewTreeNodes() {
242: commandStatusConnection.sendToAll(new RequestTree());
243: }
244:
245: /**
246: * this method will be called when data is received on our commandStatus connection
247: */
248: public void dataReceived(DataShareObject dso) {
249: try {
250: Object object = SessionUtilities
251: .retrieveObject(dso.objectBytes);
252: if (object instanceof ServerInfo) {
253: //System.out.println("Received a ServerInfo object");
254: ServerInfo serverInfo = (ServerInfo) object;
255: clientUniqueName = serverInfo.clientKey; // from now on, we use this name to talk to the server
256: this .createDataChannel();
257: } else if (object instanceof UpdateAvailableMsg) {
258: UpdateAvailableMsg msg = (UpdateAvailableMsg) object;
259: rci.updateReceived(msg);
260: } else if (object instanceof TreeObject) {
261: TreeObject to = (TreeObject) object;
262: rci.treeReceived(to.allNodes);
263: } else if (object instanceof ClientSessionInfo) { // join any session that we receive info about this way
264: System.out
265: .println("Received a ClientSessionInfo object...");
266: ClientSessionInfo ourNewSession = (ClientSessionInfo) object;
267: // Check for a server using Network Address Translation (NAT):
268: // test to see that server sent an IP address for data connection that
269: // agrees with the one we think the server is using...
270: // unless it is a Multicast channel.
271: byte[] originalAddress = serverInetAddress.getAddress();
272: for (int channelCount = 0; channelCount < ourNewSession.dsConnDescriptor.length; channelCount++) {
273: byte[] dataAddress = ourNewSession.dsConnDescriptor[channelCount].serverIP
274: .getAddress();
275: int channelProtocol = ourNewSession.dsConnDescriptor[channelCount].type;
276: if (channelProtocol != ChannelDescription.MULTICAST
277: && !Arrays.equals(originalAddress,
278: dataAddress)) {
279: System.out
280: .println("Server is using a different IP address now, suspect NAT is used...");
281: // server is now sending us a different IP address than we used for CommandStatus connection,
282: // assume NAT is used and convert the server IP address in data channels back to original.
283: ourNewSession.dsConnDescriptor[channelCount].serverIP = serverInetAddress;
284: }
285: }
286: joinSession(ourNewSession);
287: } else {
288: System.out
289: .println("received unexpected object in command stream");
290: }
291: } catch (Exception e) {
292: e.printStackTrace();
293: }
294: }
295:
296: /**
297: * called to create network connection to data channel after server has told us what we need
298: * to know in order to connect.
299: */
300: private void joinSession(ClientSessionInfo newSession) {
301: try {
302: dataConnectionDescriptor = newSession.dsConnDescriptor[0]; // assume only one channel returned
303:
304: dataConnection = new DataShareConnection(
305: dataConnectionDescriptor, rci);
306: // System.out.println("Sending the ActivateConnectionObject over our data channel...");
307: dataConnection.sendToAll(new ActivateConnectionObject(
308: this .clientUniqueName), true);
309: rci.dataChannelIsReady(true);
310: //System.out.println("Successfully created our data connection");
311: } catch (Exception e) {
312: e.printStackTrace();
313: rci.dataChannelIsReady(false);
314: }
315: }
316:
317: /**
318: * this method is called when the commandStatusConnection has been lost,
319: * this will be used to notify anybody that cares...
320: */
321: public void connectionLost(DataShareConnection dsc) {
322: rci.commandStatusConnectionError(true,
323: "Lost commandStatus connection to server");
324: }
325:
326: }
|