|
1 /* |
|
2 * Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 #include "logger.h" |
|
20 #include "functionserver.h" |
|
21 #include "bluetoothfunctionserver.h" |
|
22 #include "bluetoothremotedevice.h" |
|
23 #include "bluetoothclientconnection.h" |
|
24 #include "fs_methodcall.h" |
|
25 |
|
26 #include "btl2capserverconnection.h" |
|
27 #include "bluetoothpusheventlistener.h" |
|
28 #include "javasymbianoslayer.h" |
|
29 #include <e32cmn.h> |
|
30 |
|
31 using namespace std; |
|
32 |
|
33 namespace java |
|
34 { |
|
35 namespace bluetooth |
|
36 { |
|
37 |
|
38 const TInt KSizeOfListenQueue = 7; |
|
39 |
|
40 OS_EXPORT L2CAPServerConnection::L2CAPServerConnection( |
|
41 java::bluetooth::BluetoothFunctionServer* server): |
|
42 mAcceptMonitor(NULL), |
|
43 mAsyncAccept(false), |
|
44 mAvoidFilter(false), |
|
45 mBtUrlParams(NULL), |
|
46 mBtClientConn(NULL), |
|
47 mServer(server), |
|
48 mIsConnected(EFalse), |
|
49 mServRec(NULL), |
|
50 mState(ENone) |
|
51 { |
|
52 } |
|
53 |
|
54 OS_EXPORT L2CAPServerConnection::~L2CAPServerConnection() |
|
55 { |
|
56 CloseServer(); |
|
57 delete mAcceptMonitor; |
|
58 } |
|
59 |
|
60 OS_EXPORT int L2CAPServerConnection::openServer(bool authorize, |
|
61 bool authenticate, bool encrypt, bool master, int receiveMTU, |
|
62 int transmitMTU) |
|
63 { |
|
64 //convert and call ServerOpen() |
|
65 JELOG2(EJavaBluetooth); |
|
66 LOG2(EJavaBluetooth, EInfo, |
|
67 "* L2CAPServerConnection::openServer TxMTU:%d RxMTU:%d", |
|
68 transmitMTU, receiveMTU); |
|
69 return ServerOpen(authorize, authenticate, encrypt, master, receiveMTU, |
|
70 transmitMTU); |
|
71 } |
|
72 |
|
73 OS_EXPORT int L2CAPServerConnection::ServerOpen(TBool authorize, |
|
74 TBool authenticate, TBool encrypt, TBool master, TInt receiveMTU, |
|
75 TInt transmitMTU) |
|
76 { |
|
77 int result = 0; |
|
78 LOG2(EJavaBluetooth, EInfo, |
|
79 "+ L2CAPServerConnection::ServerOpen TxMTU:%d RxMTU:%d", |
|
80 transmitMTU, receiveMTU); |
|
81 TRAP(result, CallMethodL(this, &L2CAPServerConnection::ServerOpenL, |
|
82 authorize, authenticate, encrypt, master, receiveMTU, transmitMTU, |
|
83 mServer)); |
|
84 return result; |
|
85 } |
|
86 |
|
87 OS_EXPORT ServiceRecord *L2CAPServerConnection::getServiceRecordHandle() |
|
88 { |
|
89 return mServRec; |
|
90 } |
|
91 |
|
92 void L2CAPServerConnection::ServerOpenL(TBool authorize, TBool authenticate, |
|
93 TBool encrypt, TBool master, TInt receiveMTU, TInt transmitMTU) |
|
94 { |
|
95 LOG(EJavaBluetooth, EInfo, "+ L2CAPServerConnection::ServerOpenL "); |
|
96 |
|
97 if (mState != ENone) |
|
98 { |
|
99 User::Leave(KErrInUse); |
|
100 } |
|
101 |
|
102 User::LeaveIfError(mSocketServ.Connect()); |
|
103 User::LeaveIfError(mSocketServ.ShareAuto()); |
|
104 |
|
105 TRAPD(err, |
|
106 { |
|
107 // Set this active object to connecting state |
|
108 mState = EConnecting; |
|
109 |
|
110 // Set a Bluetooth socket address |
|
111 BindL(authorize, authenticate, |
|
112 encrypt, master, receiveMTU, transmitMTU); |
|
113 |
|
114 // Set a socket to listen for incoming connections |
|
115 ListenL(); |
|
116 } |
|
117 ); |
|
118 |
|
119 if (KErrNone != err) |
|
120 { |
|
121 mState = ENone; |
|
122 mSocketServ.Close(); |
|
123 User::LeaveIfError(err); |
|
124 } |
|
125 |
|
126 LOG(EJavaBluetooth, EInfo, "- L2CAPServerConnection::ServerOpenL "); |
|
127 } |
|
128 |
|
129 void L2CAPServerConnection::BindL(TBool authorize, TBool authenticate, |
|
130 TBool encrypt, TBool master, TInt receiveMTU, TInt transmitMTU) |
|
131 { |
|
132 LOG2(EJavaBluetooth, EInfo, |
|
133 "+ L2CAPServerConnection::BindL TxMTU:%d RxMTU:%d", |
|
134 transmitMTU, receiveMTU); |
|
135 // Load protocol, L2CAP |
|
136 TProtocolDesc protocolDesc; |
|
137 User::LeaveIfError(mSocketServ.FindProtocol(KL2CAPDesC(), protocolDesc)); |
|
138 |
|
139 LOG(EJavaBluetooth, EInfo, |
|
140 " L2CAPServerConnection::BindL: Opening A Listerner Socket "); |
|
141 // Open a socket |
|
142 mListenSock = CBluetoothSocket::NewL(*this, mSocketServ, |
|
143 protocolDesc.iSockType, KL2CAP); |
|
144 |
|
145 // Set security |
|
146 TBTServiceSecurity secSettings; |
|
147 |
|
148 secSettings.SetAuthentication(authenticate); |
|
149 secSettings.SetAuthorisation(authorize); |
|
150 secSettings.SetEncryption(encrypt); |
|
151 |
|
152 // Later once connection accepted this flag is used to set the role |
|
153 mMasterRoleRequested = master; |
|
154 |
|
155 LOG(EJavaBluetooth, EInfo, |
|
156 " L2CAPServerConnection::BindL: Attaching The Security Settings "); |
|
157 // Attach the security settings. |
|
158 mBtSockAddr.SetSecurity(secSettings); |
|
159 |
|
160 if (transmitMTU >= KL2MinMTU || receiveMTU >= KL2MinMTU) |
|
161 { |
|
162 TL2CapConfigPkg options; |
|
163 TInt errMtu = KErrNone; |
|
164 |
|
165 if (transmitMTU >= KL2MinMTU) |
|
166 { |
|
167 errMtu = options().SetMaxTransmitUnitSize(transmitMTU); |
|
168 if (KErrNone != errMtu) |
|
169 { |
|
170 ELOG2(EJavaBluetooth, |
|
171 " L2CAPServerConnection::BindL: TxMTU(%d) Err:%d", |
|
172 transmitMTU, errMtu); |
|
173 } |
|
174 } |
|
175 else |
|
176 { |
|
177 errMtu = options().SetMaxTransmitUnitSize(DEFAULT_MTU); |
|
178 if (KErrNone != errMtu) |
|
179 { |
|
180 ELOG2(EJavaBluetooth, |
|
181 " L2CAPServerConnection::BindL: Default TxMTU(%d) Err:%d", |
|
182 transmitMTU, errMtu); |
|
183 } |
|
184 } |
|
185 |
|
186 if (receiveMTU >= KL2MinMTU) |
|
187 { |
|
188 errMtu = options().SetMaxReceiveUnitSize(receiveMTU); |
|
189 if (KErrNone != errMtu) |
|
190 { |
|
191 ELOG2(EJavaBluetooth, |
|
192 " L2CAPServerConnection::BindL: RxMTU(%d) Err:%d", |
|
193 receiveMTU, errMtu); |
|
194 } |
|
195 } |
|
196 else |
|
197 { |
|
198 errMtu = options().SetMaxReceiveUnitSize(DEFAULT_MTU); |
|
199 if (KErrNone != errMtu) |
|
200 { |
|
201 ELOG2(EJavaBluetooth, |
|
202 " L2CAPServerConnection::BindL: Default RxMTU(%d) Err:%d", |
|
203 receiveMTU, errMtu); |
|
204 } |
|
205 } |
|
206 //L2CAP Specific stuff |
|
207 mListenSock->SetOpt(KL2CAPUpdateChannelConfig, KSolBtL2CAP, options); |
|
208 } |
|
209 |
|
210 // Setting l2cap passive port |
|
211 mBtSockAddr.SetPort(KL2CAPPassiveAutoBind); |
|
212 |
|
213 LOG(EJavaBluetooth, EInfo, |
|
214 " L2CAPServerConnection::BindL: Binding Bluetooth Socket "); |
|
215 // Bind bluetooth socket |
|
216 User::LeaveIfError(mListenSock->Bind(mBtSockAddr)); |
|
217 LOG(EJavaBluetooth, EInfo, "- L2CAPServerConnection::BindL"); |
|
218 } |
|
219 |
|
220 OS_EXPORT int L2CAPServerConnection::GetServerPSM() |
|
221 { |
|
222 int result = 0; |
|
223 CallMethod(result, this, &L2CAPServerConnection::GetServerPSMValue, mServer); |
|
224 return result; |
|
225 } |
|
226 |
|
227 int L2CAPServerConnection::GetServerPSMValue() |
|
228 { |
|
229 JELOG2(EJavaBluetooth); |
|
230 if (mState != EConnecting) |
|
231 { |
|
232 return -1; |
|
233 } |
|
234 return mListenSock->LocalPort(); |
|
235 } |
|
236 |
|
237 void L2CAPServerConnection::ListenL() |
|
238 { |
|
239 JELOG2(EJavaBluetooth); |
|
240 // Listen on port |
|
241 User::LeaveIfError(mListenSock->Listen(KSizeOfListenQueue)); |
|
242 } |
|
243 |
|
244 OS_EXPORT int L2CAPServerConnection::asyncAccept( |
|
245 java::bluetooth::BluetoothPushEventListener* aEventListener, |
|
246 BtUrlParams *aBtUrlParams) |
|
247 { |
|
248 JELOG2(EJavaBluetooth); |
|
249 //Store event listener and notify it when client is accepted. |
|
250 //Make a call to accept and do not wait. |
|
251 mAsyncAccept = true; |
|
252 mPushEventListener = aEventListener; |
|
253 mBtUrlParams = aBtUrlParams; |
|
254 int result = 0; |
|
255 TRAPD(err, CallMethodL(result, this, &L2CAPServerConnection::AcceptL, |
|
256 mServer)); |
|
257 return err; |
|
258 } |
|
259 |
|
260 OS_EXPORT long L2CAPServerConnection::Accept() |
|
261 { |
|
262 JELOG2(EJavaBluetooth); |
|
263 long result = 0; |
|
264 mBtClientConn = NULL; |
|
265 |
|
266 TRAPD(err, CallMethodL(result, this, &L2CAPServerConnection::AcceptL, |
|
267 mServer)); |
|
268 |
|
269 if (err != KErrNone) |
|
270 { |
|
271 return err; |
|
272 } |
|
273 |
|
274 mAcceptMonitor->wait(); |
|
275 |
|
276 if (mAcceptStatus != KErrNone) |
|
277 { |
|
278 return mAcceptStatus; |
|
279 } |
|
280 |
|
281 mAcceptedSocket = NULL; |
|
282 |
|
283 return reinterpret_cast<long>(mBtClientConn); |
|
284 } |
|
285 |
|
286 long L2CAPServerConnection::AcceptL() |
|
287 { |
|
288 JELOG2(EJavaBluetooth); |
|
289 |
|
290 // Open blank socket and pass it to accept to be assigned a proper |
|
291 // socket upon completion of Accept() |
|
292 mAcceptedSocket = CBluetoothSocket::NewL(*this, mSocketServ); |
|
293 LOG(EJavaBluetooth, EInfo, |
|
294 " L2CAPServerConnection::Accept: Set To Accept Incoming Connections "); |
|
295 |
|
296 if (NULL == mAcceptMonitor) |
|
297 { |
|
298 mAcceptMonitor = java::util::Monitor::createMonitor(); |
|
299 } |
|
300 |
|
301 if (true == mMasterRoleRequested) |
|
302 { |
|
303 if (KErrNone == mAcceptedSocket->RequestMasterRole()) |
|
304 { |
|
305 LOG(EJavaBluetooth, EInfo, |
|
306 " L2CAPServerConnection::Accept: Added the master role"); |
|
307 } |
|
308 } |
|
309 mAcceptStatus = 0; |
|
310 |
|
311 // Enabling the advertising flag |
|
312 if (NULL != mServRec) |
|
313 { |
|
314 mServRec -> setAdvertiseFs(ETrue); |
|
315 } |
|
316 // Initiating accepting for the new client connection |
|
317 mListenSock->Accept((*mAcceptedSocket)); |
|
318 |
|
319 return 0; |
|
320 } |
|
321 |
|
322 OS_EXPORT int L2CAPServerConnection::initializeServiceRecord( |
|
323 TInt aPsmValue, TUUID &aServiceUUID, TDesC8 &aServiceName) |
|
324 { |
|
325 JELOG2(EJavaBluetooth); |
|
326 |
|
327 int result = 0; |
|
328 |
|
329 if (NULL == mServRec) |
|
330 { |
|
331 TRAP(result, CallMethodL(mServRec, this, |
|
332 &L2CAPServerConnection::createServiceRecordL, mServer)); |
|
333 |
|
334 if (KErrNone == result) |
|
335 { |
|
336 TInt protocol = PROTOCOL_L2CAP; |
|
337 |
|
338 TRAP(result, mServRec->initializeRecordL(protocol, |
|
339 aPsmValue, aServiceUUID, aServiceName)); |
|
340 } |
|
341 } |
|
342 |
|
343 return result; |
|
344 } |
|
345 |
|
346 OS_EXPORT int L2CAPServerConnection::initializeServiceRecord( |
|
347 int aPsmValue, wstring aServiceUUID, wstring aServiceName) |
|
348 { |
|
349 JELOG2(EJavaBluetooth); |
|
350 int result = 0; |
|
351 |
|
352 if (NULL == mServRec) |
|
353 { |
|
354 TRAP(result, CallMethodL(mServRec, this, |
|
355 &L2CAPServerConnection::createServiceRecordL, mServer)); |
|
356 |
|
357 if (KErrNone == result) |
|
358 { |
|
359 result = mServRec->initializeRecord(PROTOCOL_L2CAP, |
|
360 aPsmValue, aServiceUUID, aServiceName); |
|
361 } |
|
362 } |
|
363 return result; |
|
364 } |
|
365 |
|
366 OS_EXPORT int L2CAPServerConnection::restorePersistentRecord() |
|
367 { |
|
368 JELOG2(EJavaBluetooth); |
|
369 int result = 0; |
|
370 |
|
371 if (NULL != mServRec) |
|
372 { |
|
373 result = mServRec->restorePersistentRecord(); |
|
374 } |
|
375 return result; |
|
376 } |
|
377 |
|
378 ServiceRecord *L2CAPServerConnection::createServiceRecordL() |
|
379 { |
|
380 JELOG2(EJavaBluetooth); |
|
381 return ServiceRecord::NewL(mServer); |
|
382 } |
|
383 |
|
384 int L2CAPServerConnection::CloseServer() |
|
385 { |
|
386 JELOG2(EJavaBluetooth); |
|
387 int result = 0; |
|
388 mIsConnected = EFalse; |
|
389 mAsyncAccept = false; |
|
390 CallMethod(this, &L2CAPServerConnection::Close, mServer); |
|
391 return result; |
|
392 } |
|
393 |
|
394 void L2CAPServerConnection::Close() |
|
395 { |
|
396 JELOG2(EJavaBluetooth); |
|
397 |
|
398 // Deleting service record. |
|
399 ServiceRecord::cleanup(mServRec); |
|
400 |
|
401 if (mState != ENone) |
|
402 { |
|
403 LOG(EJavaBluetooth, EInfo, " L2CAPServerConnection::Close "); |
|
404 mListenSock->CancelAll(); |
|
405 mListenSock->Shutdown(RSocket::EImmediate); |
|
406 delete mListenSock; //Destructor closes the socket |
|
407 mListenSock = NULL; |
|
408 mState = ENone; |
|
409 mAcceptStatus = KErrCancel; |
|
410 if (mAcceptMonitor) |
|
411 mAcceptMonitor->notify(); |
|
412 } |
|
413 } |
|
414 |
|
415 void L2CAPServerConnection::DoCancel() |
|
416 { |
|
417 JELOG2(EJavaBluetooth); |
|
418 } |
|
419 |
|
420 bool L2CAPServerConnection::isConnectionAllowed(TBTSockAddr aBtAddr) |
|
421 { |
|
422 JELOG2(EJavaBluetooth); |
|
423 if (NULL == mBtUrlParams) |
|
424 { |
|
425 return true; |
|
426 } |
|
427 |
|
428 bool authorize = false; |
|
429 bool authenticate = false; |
|
430 |
|
431 HBufC* devAddr = HBufC::New(aBtAddr.Length()); |
|
432 TPtr ptr = devAddr->Des(); |
|
433 TBTDevAddr btDeviceAddress = aBtAddr.BTAddr(); |
|
434 btDeviceAddress.GetReadable((TDes&)ptr); |
|
435 if (true == mBtUrlParams->isBlockedSender(*devAddr)) |
|
436 { |
|
437 delete devAddr; |
|
438 return false; |
|
439 } |
|
440 if (true == mBtUrlParams->isAllowedSender(*devAddr, authorize, authenticate)) |
|
441 { |
|
442 LOG(EJavaBluetooth, EInfo, |
|
443 " L2CAPServerConnection::isConnectionAllowed AllowedSender"); |
|
444 bool flag = true; |
|
445 TInt64 longBtAddr = 0; |
|
446 TLex16 toParse(*devAddr); |
|
447 toParse.Val(longBtAddr, EHex); |
|
448 LOG1(EJavaBluetooth, EInfo, |
|
449 " L2CAPServerConnection::isConnectionAllowed Long BTRemote address %llx",longBtAddr); |
|
450 if (true == authorize) |
|
451 { |
|
452 flag = BluetoothRemoteDevice::getSecurityProperty( |
|
453 REMOTE_AUTHORIZED, longBtAddr); |
|
454 } |
|
455 if (true == flag && true == authenticate) |
|
456 { |
|
457 flag = BluetoothRemoteDevice::getSecurityProperty( |
|
458 REMOTE_AUTHENTICATED, longBtAddr); |
|
459 } |
|
460 delete devAddr; |
|
461 return flag; |
|
462 } |
|
463 delete devAddr; |
|
464 LOG(EJavaBluetooth, EInfo, "- L2CAPServerConnection::isConnectionAllowed"); |
|
465 return false; |
|
466 } |
|
467 |
|
468 // |
|
469 void L2CAPServerConnection::avoidFilter() |
|
470 { |
|
471 JELOG2(EJavaBluetooth); |
|
472 mAvoidFilter = true; |
|
473 } |
|
474 //------------------------------------------------------------------------------ |
|
475 // Methods from MBluetoothSocketNotifier to handle Bluetooth Events |
|
476 //------------------------------------------------------------------------------ |
|
477 |
|
478 //Notification of an accept complete event |
|
479 void L2CAPServerConnection::HandleAcceptCompleteL(TInt err) |
|
480 { |
|
481 TBTSockAddr btRemoteAddr; |
|
482 int negotiatedReceiveMtu = 0; |
|
483 int negotiatedTransmitMtu = 0; |
|
484 |
|
485 mAcceptStatus = err; |
|
486 |
|
487 LOG1(EJavaBluetooth, EInfo, "+ L2CAPServerConnection::HandleAcceptCompleteL Err:%d", err); |
|
488 |
|
489 if (KErrNone == err) |
|
490 { |
|
491 mAcceptedSocket->RemoteName(btRemoteAddr); |
|
492 if (mAsyncAccept && (!mAvoidFilter) && (false == isConnectionAllowed(btRemoteAddr))) |
|
493 { |
|
494 mAcceptedSocket->CancelAll(); |
|
495 mAcceptedSocket->Shutdown(RSocket::EImmediate); |
|
496 delete mAcceptedSocket; |
|
497 mAcceptedSocket = NULL; |
|
498 ELOG(EJavaBluetooth, |
|
499 "L2CAPServerConnection::HandleAcceptCompleteL Connection Rejected"); |
|
500 AcceptL(); |
|
501 return; |
|
502 } |
|
503 TBuf<20> addr; |
|
504 TInt64 longBtAddr = 0; |
|
505 TBTDevAddr btDeviceAddress = btRemoteAddr.BTAddr(); |
|
506 btDeviceAddress.GetReadable(addr); |
|
507 TLex16 toParse(addr); |
|
508 toParse.Val(longBtAddr, EHex); |
|
509 LOG1(EJavaBluetooth, EInfo, |
|
510 "L2CAPServerConnection::HandleAcceptCompleteL: Address: %ld", longBtAddr); |
|
511 mBtClientConn = new BluetoothClientConnection(mAcceptedSocket, mServer); |
|
512 mAcceptedSocket->GetOpt(KL2CAPInboundMTU, KSolBtL2CAP, negotiatedReceiveMtu); |
|
513 mAcceptedSocket->GetOpt(KL2CAPNegotiatedOutboundMTU, KSolBtL2CAP, negotiatedTransmitMtu); |
|
514 mBtClientConn->initialize( |
|
515 PROTOCOL_L2CAP, longBtAddr, negotiatedReceiveMtu, negotiatedTransmitMtu); |
|
516 if (mAsyncAccept) |
|
517 { |
|
518 mPushEventListener->handleConnectionRequest(mBtClientConn, err); |
|
519 } |
|
520 else |
|
521 { |
|
522 mAcceptMonitor->notify(); |
|
523 } |
|
524 } |
|
525 else |
|
526 { |
|
527 if (mAsyncAccept) |
|
528 { |
|
529 // If Push framework cancels the listening then return otherwise |
|
530 // continue listening. |
|
531 if (KErrCancel == err) |
|
532 { |
|
533 mPushEventListener->handleConnectionRequest(NULL, err); |
|
534 } |
|
535 else |
|
536 { |
|
537 ELOG(EJavaBluetooth, |
|
538 " L2CAPServerConnection::HandleAcceptCompleteL: Error while accept. Listening again."); |
|
539 AcceptL(); |
|
540 } |
|
541 return; |
|
542 } |
|
543 else |
|
544 { |
|
545 mAcceptMonitor->notify(); |
|
546 } |
|
547 } |
|
548 |
|
549 // If connection accepted, then it should |
|
550 // disable the advertising flag |
|
551 if (NULL != mServRec) |
|
552 { |
|
553 mServRec -> setAdvertiseFs(EFalse); |
|
554 } |
|
555 |
|
556 LOG(EJavaBluetooth, EInfo, "- L2CAPServerConnection::HandleAcceptCompleteL"); |
|
557 } |
|
558 |
|
559 // Notification of a baseband event |
|
560 void L2CAPServerConnection::HandleActivateBasebandEventNotifierCompleteL( |
|
561 TInt /*aErr*/, TBTBasebandEventNotification& /*aEventNotification*/) |
|
562 { |
|
563 } |
|
564 |
|
565 //Notification of a connection complete event |
|
566 void L2CAPServerConnection::HandleConnectCompleteL(TInt /*aErr*/) |
|
567 { |
|
568 } |
|
569 |
|
570 //Notification of a ioctl complete event |
|
571 void L2CAPServerConnection::HandleIoctlCompleteL(TInt /*aErr*/) |
|
572 { |
|
573 } |
|
574 |
|
575 //Notification of a receive complete event |
|
576 void L2CAPServerConnection::HandleReceiveCompleteL(TInt /*aErr*/) |
|
577 { |
|
578 } |
|
579 |
|
580 //Notification of a send complete event |
|
581 void L2CAPServerConnection::HandleSendCompleteL(TInt /*aErr*/) |
|
582 { |
|
583 } |
|
584 |
|
585 //Notification of a shutdown complete event |
|
586 void L2CAPServerConnection::HandleShutdownCompleteL(TInt /*aErr*/) |
|
587 { |
|
588 } |
|
589 |
|
590 } //end namespace bluetooth |
|
591 } //end namespace java |