KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > sync4j > server > session > SyncSessionHandler


1 /**
2  * Copyright (C) 2003-2005 Funambol
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  */

18 package sync4j.server.session;
19
20 import java.util.logging.Logger JavaDoc;
21 import java.util.logging.Level JavaDoc;
22
23 import java.util.*;
24
25 import sync4j.framework.config.ConfigurationConstants;
26 import sync4j.framework.core.*;
27 import sync4j.framework.database.Database;
28 import sync4j.framework.engine.SyncItemImpl;
29 import sync4j.framework.engine.SyncOperation;
30 import sync4j.framework.engine.SyncEngine;
31 import sync4j.framework.engine.SyncItemState;
32 import sync4j.framework.engine.MessageSizeCalculator;
33 import sync4j.framework.engine.source.MemorySyncSource;
34 import sync4j.framework.engine.source.SyncSource;
35 import sync4j.framework.engine.pipeline.MessageProcessingContext;
36 import sync4j.framework.logging.Sync4jLogger;
37 import sync4j.framework.protocol.*;
38 import sync4j.framework.security.*;
39 import sync4j.framework.server.Capabilities;
40 import sync4j.framework.server.ClientMapping;
41 import sync4j.framework.server.LastTimestamp;
42 import sync4j.framework.server.SyncTimestamp;
43 import sync4j.framework.server.error.ServerException;
44 import sync4j.framework.server.error.MappingException;
45 import sync4j.framework.server.error.ServerFailureException;
46 import sync4j.framework.server.error.InvalidCredentialsException;
47 import sync4j.framework.server.session.SessionHandler;
48 import sync4j.framework.server.session.SyncState;
49 import sync4j.framework.server.store.NotFoundException;
50 import sync4j.framework.server.store.PersistentStore;
51 import sync4j.framework.server.Sync4jDevice;
52 import sync4j.framework.tools.Base64;
53
54 import sync4j.server.config.Configuration;
55 import sync4j.server.engine.Sync4jEngine;
56
57 /**
58  * This class represents the handler for a SyncML session. It coordinates and
59  * handles the packages and messages as dictated by the protocol.
60  * <p>
61  * The entry point is <i>processMessage()</i>, which determines which message is
62  * expected and what processing has to be done (depending on the value of
63  * <i>currentState</i>). If an error accours, the session goes to the state
64  * <i>STATE_ERROR</i>; in this state no other messages but initialization can be
65  * performed.
66  * <p>
67  * In the current implementation separate initialization is required.
68  * <p>
69  * <i>SessionHandler</i> makes use of a <i>SyncEngine</i> for all
70  * tasks not related to the handling of the protocol.
71  * See <i>sync4j.framework.engine.SyncEngine</i> for more information.
72  *
73  * LOG NAME: sync4j.handler
74  *
75  * @see sync4j.framework.engine.SyncEngine
76  *
77  * @author Stefano Fornari @ Funambol
78  *
79  * @version $Id: SyncSessionHandler.java,v 1.32 2005/05/06 10:29:16 luigiafassina Exp $
80  *
81  */

82 public class SyncSessionHandler
83 implements SessionHandler,
84            java.io.Serializable JavaDoc,
85            SecurityConstants,
86            ConfigurationConstants {
87
88     // ------------------------------------------------------------ Private data
89

90     private int currentState = STATE_START;
91
92     //
93
// This data is true for slow sync
94
//
95
private boolean slow = false;
96
97     /**
98      * Gets the current state
99      *
100      * @return the current state
101      */

102     public int getCurrentState() {
103         return currentState;
104     }
105
106     private long creationTimestamp = -1;
107
108     /**
109      * Gets the creation timestamp of the session
110      *
111      * @return the creation timestamp
112      */

113     public long getCreationTimestamp() {
114         return creationTimestamp;
115     }
116
117     private transient Logger JavaDoc log = Sync4jLogger.getLogger("handler");
118
119     /**
120      * SyncTimestamp for the current synchronization
121      */

122     private SyncTimestamp nextTimestamp = null;
123
124     private transient SyncInitialization syncInit = null;
125     private transient SyncModifications modifications = null;
126
127     /**
128      * Contain the client device Id for the current session
129      */

130     private String JavaDoc clientDeviceId = null;
131
132     /**
133      * The databases that have to be synchronized. It is set in the initialization
134      * process.
135      */

136     private Database[] dbs = null;
137
138     /**
139      * The type of authetication does impose from the server
140      */

141     private String JavaDoc clientAuth = null;
142
143     /**
144      * Size of the current response message
145      */

146     private long messageSize = 0;
147
148     /**
149      * Which calculator should be used to calculate message size?
150      */

151     private MessageSizeCalculator sizeCalculator = null;
152
153     // -------------------------------------------------------------- Properties
154

155     /**
156      * The session id - read only
157      */

158     private String JavaDoc sessionId = null;
159
160     public String JavaDoc getSessionId() {
161         return this.sessionId;
162     }
163
164     /**
165      * The command id generator (it defaults ro a <i>CommandIdGenerator</i> instance)
166      */

167     private CommandIdGenerator cmdIdGenerator = new CommandIdGenerator();
168
169     public void setCommandIdGenerator(CommandIdGenerator cmdIdGenerator) {
170         this.cmdIdGenerator = cmdIdGenerator;
171     }
172
173     public CommandIdGenerator getCommandIdGenerator() {
174         return this.cmdIdGenerator;
175     }
176
177     /**
178      * The cmdIdGenerator must be reset each time the process
179      * of a message is starting
180      */

181     private void resetIdGenerator() {
182         cmdIdGenerator.reset();
183         syncEngine.setCommandIdGenerator(cmdIdGenerator);
184     }
185
186     /**
187      * The message id generator (it defaults ro a <i>SimpleIdGenerator</i> instance)
188      */

189     private SimpleIdGenerator msgIdGenerator = new SimpleIdGenerator();
190
191     /**
192      * The Last message Id from the client
193      */

194     private String JavaDoc lastMsgIdFromClient = null;
195
196     /**
197      * The <i>SyncEngine</i>
198      */

199     private Sync4jEngine syncEngine = null;
200
201     /**
202      * The SyncState for the syncronization process
203      */

204     private SyncState syncState = null;
205
206
207     // ---------------------------------------------------------- Public methods
208
public SyncEngine getSyncEngine() {
209         return this.syncEngine;
210     }
211
212     /**
213      * Indicates if the session is a new session
214      */

215     private boolean newSession = true;
216
217     public void setNew(boolean newSession) {
218         this.newSession = newSession;
219     }
220
221     public boolean isNew() {
222         return this.newSession;
223     }
224
225     /**
226      * Sets the message size calculator
227      *
228      * @param calculator the message size calculator
229      */

230     public void setSizeCalculator(MessageSizeCalculator calculator) {
231         this.sizeCalculator = calculator;
232     }
233
234     // ------------------------------------------------------------ Constructors
235

236     /**
237      * Creates a new instance of SyncSessionHandler
238      */

239     public SyncSessionHandler() {
240         this.creationTimestamp = System.currentTimeMillis();
241         this.syncEngine = new Sync4jEngine(Configuration.getConfiguration());
242     }
243
244     /**
245      * Creates a new instance of SyncSessionHandler with a given session id
246      */

247     public SyncSessionHandler(String JavaDoc sessionId) {
248         this();
249         this.sessionId = sessionId;
250     }
251
252     // ---------------------------------------------------------- Public methods
253

254     /**
255      * Returns true if the sessione has been authenticated.
256      *
257      * @return true if the sessione has been authenticated, false otherwise
258      */

259     public boolean isAuthenticated() {
260         return (syncState != null) && (syncState.authenticationState == AUTH_AUTHENTICATED);
261     }
262
263     /**
264      * Returns true if the session has not been authenticated because of an
265      * expired account.
266      *
267      * @return true if the account is expired
268      */

269     public boolean isAccountExpired() {
270         return syncEngine.getOfficer().isAccountExpired();
271     }
272
273     /**
274      * Processes the given message. See the class description for more
275      * information.
276      *
277      * @return the response message
278      * @param context
279      * @param message the message to be processed
280      * @throws InvalidCredentialsException
281      * @throws ProtocolException
282      */

283     public SyncML processMessage(SyncML message, MessageProcessingContext context)
284     throws ProtocolException, InvalidCredentialsException, ServerFailureException {
285         SyncML response = null;
286
287         //
288
// Reset the cmdIdGenerator has specified in the spec
289
//
290
resetIdGenerator();
291
292         //
293
// Each time a message is received for a particular session adjust the message ID
294
//
295
msgIdGenerator.next();
296
297         //
298
// We maintain the message Id from client
299
//
300
lastMsgIdFromClient = message.getSyncHdr().getMsgID();
301
302         //
303
// Initialize the device ID from the client request
304
//
305
clientDeviceId = message.getSyncHdr().getSource().getLocURI();
306
307         if (log.isLoggable(Level.FINEST)) {
308             log.finest("current state: " + getStateName(currentState));
309         }
310
311         Chal chal = ProtocolUtil.getStatusChal(message);
312
313         try {
314             switch (currentState) {
315                 case STATE_ERROR: // in case of error you can start a new initialization
316
case STATE_START:
317                     nextTimestamp = new SyncTimestamp();
318                     nextTimestamp.start = System.currentTimeMillis();
319                     nextTimestamp.tagClient = String.valueOf(nextTimestamp.start);
320
321                     syncState = new SyncState();
322
323                     syncEngine.setSyncTimestamp(new Date(nextTimestamp.start));
324
325                     //
326
// Set maximum message size
327
//
328
Meta meta = message.getSyncHdr().getMeta();
329                     if (meta != null && meta.getMaxMsgSize() != null) {
330                         syncState.maxMsgSize = meta.getMaxMsgSize().intValue();
331                     }
332
333                     //
334
// read device information from the database. Use this
335
// information with care until the client has been
336
// authenticated.
337
//
338
syncState.device = new Sync4jDevice(clientDeviceId);
339                     try {
340                         syncEngine.readDevice(syncState.device);
341                     } catch (NotFoundException e) {
342                         if (log.isLoggable(Level.WARNING)) {
343                             log.warning( "Client device '"
344                                        + clientDeviceId
345                                        + "' not found. Authentication may fail."
346                                        );
347                         }
348                     }
349
350                     syncState.syncMLVerProto =
351                                 message.getSyncHdr().getVerProto().getVersion();
352                     syncEngine.setSyncMLVerProto(syncState.syncMLVerProto);
353
354                     //
355
// Check if the client has sent the credential with the same
356
// authentication type imposed by the server
357
//
358
Cred cred = message.getSyncHdr().getCred();
359
360                     if (!checkClientAuth(cred)) {
361                         //
362
// the client uses an authentication type different
363
// from authentication server type
364
//
365
syncState.loggedCredential = null;
366                         syncState.authenticationState = AUTH_INVALID_CREDENTIALS;
367
368                         if (cred == null) {
369                             syncState.authenticationState =
370                                              syncState.AUTH_MISSING_CREDENTIALS;
371                         }
372                     } else {
373                         if (login(message.getSyncHdr().getCred(), clientDeviceId)) {
374                             if (log.isLoggable(Level.INFO)) {
375                                 log.info( syncState.loggedPrincipal.getUsername()
376                                         + '/'
377                                         + syncState.loggedPrincipal.getDeviceId()
378                                         + " logged in."
379                                         );
380                             }
381                             if (syncState.loggedPrincipal.getId() == null) {
382                                 try {
383                                     syncEngine.readPrincipal(syncState.loggedPrincipal);
384                                 } catch (NotFoundException e) {
385                                     if (log.isLoggable(Level.INFO)) {
386                                         log.info("Authenticated principal not found:" + syncState.loggedPrincipal);
387                                     }
388                                     syncState.authenticationState = AUTH_INVALID_CREDENTIALS;
389                                     syncState.loggedCredential = null;
390                                 }
391                             }
392
393                             //
394
// If the client is authenticated and the authentication
395
// type is MD5, the given client next nonce must be stored
396
// to be used at the next session.
397
//
398
if (chal != null) {
399                                 if (Cred.AUTH_TYPE_MD5.equals(chal.getType())) {
400                                     syncState.device.setServerNonce(Base64.decode(chal.getNextNonce().getValue()));
401                                     syncEngine.storeDevice(syncState.device);
402                                 }
403                             }
404                         } else {
405                             if (log.isLoggable(Level.INFO)) {
406                             log.info("Authentication failed for device "
407                                     + clientDeviceId
408                                     + ". Make sure that the client used correct "
409                                     + "username and password and that there "
410                                     + "is a principal associating the user "
411                                     + " to the device. "
412                                     );
413                             }
414                             syncState.authenticationState = AUTH_INVALID_CREDENTIALS;
415                             syncState.loggedCredential = null;
416                         }
417                     }
418
419                     moveTo(STATE_PKG1_RECEIVING);
420
421                 case STATE_PKG1_RECEIVING:
422
423                     response = processInitSyncMapMessage(message);
424
425                     if (!isAuthenticated()) {
426                         if (message.isLastMessage()) {
427                             response.setLastMessage();
428                         }
429                         moveTo(STATE_START);
430                     } else {
431                         if (message.isLastMessage()) {
432                             moveTo(STATE_PKG3_RECEIVING);
433                             if (syncState.cmdCache1.size() == 0) {
434                                 response.setLastMessage();
435                             }
436                         }
437                     }
438
439                     break;
440
441                 case STATE_PKG3_RECEIVING:
442                     response = processInitSyncMapMessage(message);
443
444                     if (message.isLastMessage()) {
445                         moveTo(STATE_PKG3_RECEIVED);
446                         if (syncState.cmdCache3.size() == 0) {
447                             response.setLastMessage();
448                         }
449                     }
450
451                     break;
452
453                 case STATE_PKG3_RECEIVED:
454                         response = processInitSyncMapMessage(message);
455
456                         if (syncState.cmdCache3.size() == 0) {
457                             response.setLastMessage();
458                             moveTo(STATE_PKG1_RECEIVING);
459                         }
460
461                     break;
462
463                 default:
464                     logout();
465                     throw new ProtocolException("Illegal state: " + currentState);
466             }
467
468             if ((currentState != STATE_ERROR) &&
469                 (currentState != STATE_START) &&
470                 response.isLastMessage() &&
471                 (ProtocolUtil.noMoreResponse(response)) ) {
472
473                 nextTimestamp.end = System.currentTimeMillis();
474                 moveTo(STATE_END);
475             }
476
477         } catch (ProtocolException e) {
478             log.throwing(getClass().getName(), "processMessage", e);
479             moveTo(STATE_ERROR);
480             throw e;
481         } catch (Throwable JavaDoc t) {
482             log.throwing(getClass().getName(), "processMessage", t);
483             moveTo(STATE_ERROR);
484             throw new ServerFailureException("Server error", t);
485         }
486
487         //
488
// Reassign ordered cmdId to all commands in the given list.
489
//
490
List commands = response.getSyncBody().getCommands();
491         ProtocolUtil.updateCmdId(commands);
492
493         if (log.isLoggable(Level.FINEST)) {
494             log.finest("About returning message: " + Util.toXML(response));
495         }
496
497         return response;
498     }
499
500     /**
501      * Check if the client authentication type is equals to the server
502      * authetication type.
503      *
504      * @param cred the client credential
505      *
506      * @return true if client and server auth type match, false otherwise
507      */

508     private boolean checkClientAuth(Cred cred) {
509
510         //
511
// Set the client authentication that will be used in the other part
512
// of the code too
513
//
514
Officer officer = syncEngine.getOfficer();
515         clientAuth = officer.getClientAuth();
516
517         if (cred == null) {
518             syncState.authenticationState = syncState.AUTH_MISSING_CREDENTIALS;
519             return false;
520         }
521
522         String JavaDoc clientAuthSent = cred.getType();
523
524         if (clientAuth.equalsIgnoreCase(clientAuthSent)) {
525             return true;
526         }
527
528         syncState.authenticationState = syncState.AUTH_INVALID_CREDENTIALS;
529         return false;
530     }
531
532     /**
533      * Processes an error condition. This method is called when the error is
534      * is not fatal and is manageable at a protocol/session level. This results
535      * in a well formed SyncML message with an appropriete error code.
536      * <p>
537      * Note that the offending message <i>msg</i> cannot be null, meaning that
538      * at least the incoming message was a SyncML message. In this context,
539      * <i>RepresentationException</i>s are excluded.
540      *
541      * @param the offending message - NOT NULL
542      * @param the exception representing the error condition - NOT NULL
543      *
544      * @throws sync4j.framework.core.Sync4jException in case of unexpected errors
545      *
546      * @return the response message
547      */

548     public SyncML processError(SyncML msg, Throwable JavaDoc error)
549     throws Sync4jException {
550         SyncHdr msgHeader = msg.getSyncHdr();
551
552         Item[] items = new Item[0];
553         int status = StatusCode.SERVER_FAILURE;
554
555         items = new Item[1];
556         items[0] = new Item(
557                        null, // target
558
null, // source
559
null, // meta
560
new ComplexData(error.getMessage()),
561                        false //MoreData
562
);
563
564         if (error instanceof ServerException) {
565             status = ((ServerException)error).getStatusCode();
566         }
567
568         Status statusCommand = new Status(
569                 cmdIdGenerator.next() ,
570                 msgHeader.getMsgID() ,
571                 "0" /* command ref */ ,
572                 "SyncHdr" /* see SyncML specs */ ,
573                 new TargetRef(msgHeader.getTarget()),
574                 new SourceRef(msgHeader.getSource()),
575                 null /* credential */ ,
576                 null /* challenge */ ,
577                 new Data(status) ,
578                 new Item[0]
579             );
580
581         String JavaDoc serverURI = syncEngine
582                          .getConfiguration()
583                          .getServerConfig()
584                          .getEngineConfiguration()
585                          .getServerURI();
586
587         SyncHdr syncHeader = new SyncHdr (
588                                   msgHeader.getVerDTD() ,
589                                   msgHeader.getVerProto() ,
590                                   msgHeader.getSessionID() ,
591                                   msgHeader.getMsgID() ,
592                                   new Target(msgHeader.getSource().getLocURI()),
593                                   new Source(serverURI) ,
594                                   null /* response URI */ ,
595                                   false ,
596                                   null /* credentials */ ,
597                                   null /* metadata */
598                                );
599
600         SyncBody syncBody = new SyncBody(
601                                 new AbstractCommand[] { statusCommand },
602                                 true /* final */
603                             );
604
605         moveTo(STATE_ERROR);
606
607         return new SyncML(syncHeader, syncBody);
608     }
609
610     /**
611      * Called by the <i>SessionManager</i> when the session is expired.
612      * It logs out the credential and release aquired resources.
613      */

614     public void expire() {
615         logout();
616     }
617
618     /**
619      * Called to interrupt the processing in case of errors depending on
620      * extenal causes (i.e. the transport). The current implementation just move
621      * the session state to the error state.
622      * <p>
623      * NOTE that the current implementation simply moves the state of the session
624      * to <i>STATE_ERROR</i>.
625      *
626      * @param statusCode the error code
627      *
628      * @see sync4j.framework.core.StatusCode for valid status codes
629      *
630      */

631     public void abort(int statusCode) {
632         moveTo(STATE_ERROR);
633     }
634
635     /**
636      * Called to permanently commit the synchronization. It does the following:
637      * <ul>
638      * <li>persists the <i>last</i> timestamp to the database for the sources
639      * successfully synchronized
640      * </ul>
641      */

642     public void commit() {
643         assert (syncState.loggedPrincipal != null);
644
645         LastTimestamp last = null;
646
647         PersistentStore ps = syncEngine.getStore();
648
649         Database[] dbsGen = syncEngine.getDbs();
650
651         for (int i = 0; (dbsGen != null) && (i < dbsGen.length); ++i) {
652             if (dbsGen[i].getStatusCode() != StatusCode.OK) {
653                 //
654
// This database is in error. Do not commit it.
655
//
656
continue;
657             }
658             last = new LastTimestamp(
659                     syncState.loggedPrincipal.getId(),
660                     dbsGen[i].getName(),
661                     dbsGen[i].getAnchor().getNext(),
662                     dbsGen[i].getServerAnchor().getNext(),
663                     nextTimestamp.start,
664                     nextTimestamp.end
665             );
666
667             if (log.isLoggable(Level.FINEST)) {
668                 log.finest("Commiting database "
669                         + dbsGen[i].getName()
670                         + " ( "
671                         + last
672                         + " )"
673                 );
674             }
675
676             try {
677                 boolean stored = ps.store(last);
678                 if (log.isLoggable(Level.FINEST)) {
679                     log.finest("LastTimeStamp stored: " + stored);
680                 }
681             } catch (Sync4jException e) {
682                 log.severe("Error in saving persistent data");
683                 log.throwing(getClass().getName(), "commit", e);
684             }
685         }
686     }
687
688     // --------------------------------------------------------- Private methods
689

690     /**
691      * Processes the given message applying initialization processing first,
692      * and then synchronization processing.
693      *
694      * @param message the message to be processed
695      *
696      * @return the response message
697      *
698      * @throws ProtocolException in case of protocol errors
699      */

700     private SyncML processInitSyncMapMessage(SyncML message)
701     throws ProtocolException {
702         SyncInitialization init = null;
703         SyncModifications sync = null;
704         SyncMapping map = null;
705         SyncML response = null;
706         SyncHdr header = null;
707
708         String JavaDoc msgId = msgIdGenerator.current();
709
710         //
711
// First of all check if this message contains initialization elements.
712
//
713
if (ProtocolUtil.isInitMessage(message)) {
714             init = processInitMessage(message);
715             header = init.getResponseHeader(msgId);
716         }
717
718         //
719
// If we are still not authenticated, we do not process sync and map
720
// commands and we directly return the init response, which will
721
// contains proper status for all commands.
722
//
723
if (!isAuthenticated()) {
724             response = init.getResponseMessage(msgId);
725         } else {
726
727             //
728
// If the client is authenticated and the authentication
729
// type is MD5, the given client next nonce must be stored
730
// to be used at the next session.
731
//
732
Chal chal = ProtocolUtil.getStatusChal(message);
733             if (chal != null) {
734                 if (Cred.AUTH_TYPE_MD5.equals(chal.getType())) {
735                     syncState.device.setServerNonce(Base64.decode(chal.getNextNonce().getValue()));
736                     syncEngine.storeDevice(syncState.device);
737                 }
738             }
739
740             //
741
// Then check if the message contains synchronization elements.
742
//
743
if (ProtocolUtil.isSyncMessage(message)) {
744                 sync = processSyncMessage(message);
745                 if (header == null) {
746                     header = sync.getResponseHeader(msgId);
747                 }
748             }
749
750             //
751
// Then process any incoming Map command
752
//
753
if (ProtocolUtil.isMapMessage(message) || ProtocolUtil.noMoreResponse(message)) {
754                 map = processMapMessage(message);
755                 if (header == null) {
756                     header = map.getResponseHeader(msgId);
757                 }
758             }
759
760             //
761
// If we do not have a separate initialization, we jump to the
762
// state STATE_PKG3_RECEIVING.
763
//
764
if ((init != null) && (sync != null)) {
765                 if (message.isLastMessage()) {
766                     moveTo(STATE_PKG3_RECEIVED);
767                 } else {
768                     moveTo(STATE_PKG3_RECEIVING);
769                 }
770             }
771
772             //
773
// After processing the commands, if any mapping was modified, the
774
// change must be persisted
775
//
776
syncEngine.storeMappings();
777
778             //
779
// If client max msg size is too small, do not even try
780
//
781
AbstractCommand[] cmdToSend = null;
782             if (checkMaxMsgSize()) {
783                 messageSize = sizeCalculator.getSize(header)
784                             + sizeCalculator.getSyncBodyOverhead()
785                             + sizeCalculator.getRespURIOverhead()
786                             ;
787                 cmdToSend = commandsToSend(init, sync, map);
788             } else {
789                 cmdToSend = new AbstractCommand[1];
790
791                 Status status = (Status)init.getResponseCommands(msgId).get(0);
792
793                 status.setData(new Data(StatusCode.SYNCHRONIZATION_FAILED));
794
795                 cmdToSend[0] = status;
796
797                 if (log.isLoggable(Level.INFO)) {
798                     log.info( "The MaxMsgSize ("
799                             + syncState.maxMsgSize
800                             + ") is smaller than the minimum message size "
801                             + "supported by the server. The session will abort."
802                             );
803                 }
804             }
805
806             try {
807                 response = new SyncML(
808                     header,
809                     new SyncBody(cmdToSend, false)
810                 );
811             } catch (RepresentationException e) {
812                 throw new ProtocolException(e.getMessage());
813             }
814         }
815
816         return response;
817     }
818
819     /**
820      * Processes the initialization commands in the given message like if it
821      * contained only initialization information. Init commands are:
822      * <ul>
823      * <li>SyncHeader (credentials)</li>
824      * <li>Put (client capabilities)</li>
825      * <li>Get (server capabilities)</li>
826      * <li>Alert (database alerting)</li>
827      * </ul>
828      *
829      * @param message the message to be processed
830      *
831      * @return the SyncInitialization object representing the initialization
832      * request-response associated to the incoming message
833      *
834      * @throws ProtocolException in the case of violation of the specification
835      */

836     private SyncInitialization processInitMessage(SyncML message)
837     throws ProtocolException {
838         if (log.isLoggable(Level.FINEST)) {
839             log.finest("Processing the initialization commands");
840         }
841
842         try {
843             syncInit = new SyncInitialization(message.getSyncHdr() ,
844                                               message.getSyncBody());
845
846             //
847
// Store the information that will be used in the Syncronization process
848
//
849
syncState.addClientAlerts(syncInit.getClientAlerts());
850
851             syncInit.setIdGenerator(cmdIdGenerator);
852
853             //
854
// Sets the server authentication type so that in the case the client
855
// will be challenged accordingly
856
//
857
syncInit.setClientAuth(clientAuth);
858
859             //
860
// If authentication type is MD5 then generate a new NextNonce
861
//
862
if (clientAuth.equalsIgnoreCase(Cred.AUTH_TYPE_MD5)) {
863                 NextNonce nonce = ProtocolUtil.generateNextNonce();
864                 syncInit.setNextNonce(nonce);
865                 syncState.device.setClientNonce(nonce.getValue());
866                 syncEngine.storeDevice(syncState.device);
867             }
868
869             if (isAuthenticated()) {
870                 syncInit.setAuthorizedStatusCode(StatusCode.AUTHENTICATION_ACCEPTED);
871
872                 syncInit.setClientCapabilitiesRequired(false);
873
874                 //
875
// If the client gave its capabilities, process them
876
//
877
processClientCapabilities(syncInit.getClientDeviceInfo());
878
879                 //
880
// Gets the databases requested for synchronization and for each of
881
// them checks if the database exists on the server and if the
882
// credential is allowed to synchronize it
883
//
884
dbs = syncInit.getDatabasesToBeSynchronized(syncState.loggedPrincipal);
885
886                 //
887
// This will change the status code of the elements of clientDBs to
888
// reflect the availability and accessibility of the given databases
889
// on the server
890
//
891
syncEngine.prepareDatabases(syncState.loggedPrincipal, dbs, nextTimestamp);
892
893                 if (log.isLoggable(Level.FINEST)) {
894                     log.finest("Requested databases: " + Arrays.asList(dbs));
895                 }
896
897                 boolean noDataSource = true; // there are no datasource allowed to synchronize
898

899                 ArrayList dbList = new ArrayList();
900                 for (int i = 0; ((dbs != null) && (i < dbs.length)); ++i) {
901                     syncInit.setStatusCodeForCommand(
902                             dbs[i].getAlertCommand(),
903                             dbs[i].getStatusCode()
904                     );
905
906                     if (dbs[i].isOkStatusCode()) {
907                         noDataSource = false;
908                         syncEngine.addClientSource(
909                                 new MemorySyncSource(
910                                     dbs[i].getName(),
911                                     null,
912                                     dbs[i].getSource().getLocURI())
913                         );
914
915                         Meta m = ((Item)dbs[i].getAlertCommand().getItems().get(0)).getMeta();
916                         if (m != null) {
917                             Long JavaDoc l = m.getMaxObjSize();
918                             if (l != null) {
919                                 syncState.maxObjSize = l.longValue();
920                             } else {
921                                 syncState.maxObjSize = 0;
922                             }
923                         }
924                         dbList.add(dbs[i]);
925                     }
926                 }
927                 dbs = (Database[])dbList.toArray(new Database[dbList.size()]);
928
929                 syncEngine.setDbs(dbs);
930
931                 //
932
// Setting the databases to synchronize. This will force the relative
933
// <Alert>s to be inserted in the response.
934
//
935
syncInit.setDatabases(dbs);
936
937                 //
938
// Setting server capabilities
939
//
940
syncInit.setServerCapabilities(
941                     syncEngine.getServerCapabilities(syncInit.getDTDVersion())
942                 );
943             } else {
944                 if (isAccountExpired()) {
945                     syncInit.setAuthorizedStatusCode(StatusCode.PAYMENT_REQUIRED);
946                 } else if (syncState.authenticationState == AUTH_MISSING_CREDENTIALS) {
947                     syncInit.setAuthorizedStatusCode(StatusCode.MISSING_CREDENTIALS);
948                 } else if (syncState.authenticationState == AUTH_INVALID_CREDENTIALS) {
949                     syncInit.setAuthorizedStatusCode(StatusCode.INVALID_CREDENTIALS);
950                 } else {
951                     syncInit.setAuthorizedStatusCode(StatusCode.FORBIDDEN);
952                 }
953             }
954
955             //
956
// Now we set server credentials. If the client challenged the server,
957
// we use the requested auth mechanism, otherwise, we use the default
958
// server authentication scheme.
959
// Not that if credentials are not set in server configuration,
960
// getServerCredential returns null, which means that no credentials
961
// will be returned to the client.
962
//
963
Chal chal = getChal(message);
964
965             String JavaDoc serverAuth = syncEngine.getOfficer().getServerAuth();
966
967             if (chal == null) {
968                 if (serverAuth.equalsIgnoreCase(Cred.AUTH_NONE)) {
969                     syncInit.setServerCredentials(null);
970                     syncState.serverAuthenticationState = AUTH_AUTHENTICATED;
971                 } else {
972                     if (syncState.serverAuthenticationState != AUTH_ACCEPTED) {
973                         Meta meta = new Meta();
974                         meta.setType(serverAuth);
975                         meta.setNextNonce(new NextNonce(Base64.encode(
976                             syncState.device.getServerNonce()))
977                             );
978
979                         chal = new Chal(meta);
980                         syncInit.setServerCredentials(
981                             syncEngine.getServerCredentials(chal, syncState.device)
982                         );
983                     }
984                 }
985             } else {
986                 Cred cred = checkServerAuthentication(message);
987                 if (cred != null) {
988                     syncInit.setServerCredentials(cred);
989                 }
990             }
991
992             return syncInit;
993
994         } catch (Sync4jException e) {
995             throw new ProtocolException(e);
996         }
997     }
998
999     /**
1000     * Check if the client MaxMsgSize is greater than server minmsgsize.
1001     * If the client MaxMsgSize is smaller than minmsgsize then change
1002     * the status code of SyncHdr into StatusCode.SYNCHRONIZATION_FAILED.
1003     *
1004     * @return true if the client maxmsgsize is ok, false otherwise
1005     */

1006    private boolean checkMaxMsgSize() {
1007        final long minMsgSize = syncEngine
1008                                .getConfiguration()
1009                                .getServerConfig()
1010                                .getEngineConfiguration()
1011                                .getMinMaxMsgSize();
1012
1013        if (log.isLoggable(Level.FINEST)) {
1014            log.finest( "Checking if MaxMsgSize is larger than the minimum "
1015                      + "size supported by the server ("
1016                      + minMsgSize
1017                      + ")"
1018                      );
1019        }
1020
1021        return ((syncState.maxMsgSize == 0) || (syncState.maxMsgSize >= minMsgSize));
1022    }
1023
1024    /**
1025     * Check if are required the server capabilities and check their size.
1026     * If that size added at size of SyncHdr and SyncHdr Status and all
1027     * Alert command is more than MaxMsgSize then set Get Status with
1028     * StatusCode.REQUESTED_ENTITY_TOO_LARGE
1029     */

1030    private void checkSizeCapabilities() {
1031        //
1032
// If server capabilities are required then check their size in order
1033
// to establish if it will be possible to send them
1034
//
1035
AbstractCommand[] cmds =
1036            (AbstractCommand[])syncState.cmdCache1.toArray(new AbstractCommand[0]);
1037        Status statusGet = (Status)ProtocolUtil.filterCommands(
1038            cmds,
1039            Status.class,
1040            Get.COMMAND_NAME
1041        );
1042
1043        if (statusGet != null) {
1044            ArrayList results = ProtocolUtil.filterCommands(
1045                cmds,
1046                Results.class
1047            );
1048
1049            if (results.size() > 0) {
1050                //
1051
// Calculate size Results
1052
//
1053
long sizeCap = sizeCalculator.getSyncMLOverhead()
1054                             + sizeCalculator.getSyncBodyOverhead()
1055                             + sizeCalculator.getSize((Results)results.get(0));
1056                if (sizeCap > syncState.maxMsgSize) {
1057                    if (log.isLoggable(Level.INFO)) {
1058                        log.info("Capabilities too big!");
1059                    }
1060                    statusGet.setData(new Data(StatusCode.REQUESTED_ENTITY_TOO_LARGE));
1061
1062                    syncState.cmdCache1.remove(results);
1063                }
1064            }
1065        }
1066    }
1067
1068    /**
1069     * Extracts or create a Chal object that represents how server credentials
1070     * should be created.<br>
1071     * If the client challenged the server, we must use the requested auth
1072     * mechanism, otherwise, we use the default server authentication scheme.
1073     *
1074     * @param msg the request message
1075     *
1076     * @return a Chal object reqpresenting how server credentials should be
1077     * formatted.
1078     */

1079    private Chal getChal(final SyncML msg) {
1080        Chal chal = ProtocolUtil.getStatusChal(msg);
1081        if (chal != null) {
1082            if (log.isLoggable(Level.FINEST)) {
1083                log.finest("Challenged server authentication with scheme " + chal.getType());
1084            }
1085        }
1086
1087        return chal;
1088    }
1089
1090    /**
1091     * Checks if the server authentication succedeed. It throws a
1092     * ProtocolException if not.
1093     *
1094     * @param msg the SyncML message to check
1095     *
1096     * @return true if the server should retry authentication, false otherwise
1097     *
1098     * @throw ProcotolException if server authentication did not succeed
1099     */

1100    private Cred checkServerAuthentication(SyncML msg)
1101    throws ProtocolException {
1102        String JavaDoc serverAuth = syncEngine.getOfficer().getServerAuth();
1103        int headerStatusCode = ProtocolUtil.getHeaderStatusCode(msg);
1104        if (headerStatusCode == -1) {
1105            return null;
1106        }
1107
1108        if ( (headerStatusCode == StatusCode.INVALID_CREDENTIALS ||
1109              headerStatusCode == StatusCode.MISSING_CREDENTIALS)) {
1110            if (
1111                (serverAuth.equalsIgnoreCase(Cred.AUTH_TYPE_MD5) ) &&
1112                (syncState.serverAuthenticationState == AUTH_UNAUTHENTICATED)
1113               ) {
1114                   syncState.serverAuthenticationState = AUTH_RETRY_1;
1115                   return syncEngine.getServerCredentials(getChal(msg), syncState.device);
1116            } else {
1117                throw new ProtocolException("Unable to authenticate the client");
1118            }
1119        } else if (headerStatusCode == StatusCode.AUTHENTICATION_ACCEPTED) {
1120            //
1121
// Authenticated with code 212
1122
//
1123
if (log.isLoggable(Level.FINEST)) {
1124                log.finest("Server logged (code 212)");
1125        }
1126            syncState.serverAuthenticationState = AUTH_ACCEPTED;
1127        } else {
1128            //
1129
// Authenticated with code 200
1130
//
1131
if (log.isLoggable(Level.FINEST)) {
1132                log.finest("Server auhenticated (code 200)");
1133            }
1134            syncState.serverAuthenticationState = AUTH_AUTHENTICATED;
1135            return syncEngine.getServerCredentials(getChal(msg), syncState.device);
1136        }
1137        return null;
1138    }
1139
1140    /**
1141     * Processes the given synchronization message.
1142     *
1143     * @param syncRequest the message to be processed
1144     *
1145     * @return the SyncModifications object representing the request-response
1146     * under processing
1147     *
1148     * @throws ProtocolException in case of errors
1149     */

1150    private SyncModifications processSyncMessage(SyncML syncRequest)
1151    throws ProtocolException {
1152
1153        if (log.isLoggable(Level.FINEST)) {
1154            log.finest("Processing the given synchronization message");
1155            log.finest("client sources: " + syncEngine.getClientSources());
1156        }
1157
1158        try {
1159            modifications =
1160                    new SyncModifications(syncRequest.getSyncHdr() ,
1161                                          syncRequest.getSyncBody(),
1162                                          syncEngine.getDbs());
1163
1164            Sync[] syncCommands = modifications.getClientSyncCommands();
1165
1166            //
1167
// Check if into Sync command there is a MaxObjSize for LargeObject
1168
//
1169
checkMaxObjSizeIntoSync(syncCommands);
1170
1171            //
1172
// TBD: verify this!!!!
1173
//
1174
if (syncRequest.isLastMessage()) {
1175                syncEngine.setLastMessage(true);
1176            }
1177
1178            List responseCommands = processModifications(modifications);
1179
1180            //
1181
// Add Status for Item with large object
1182
//
1183
if (syncState.getListStatusCmdNotProcessed() != null) {
1184                responseCommands.addAll(syncState.getListStatusCmdNotProcessed());
1185                syncState.clearListStatusCmdNotProcessed();
1186            }
1187
1188            if (log.isLoggable(Level.FINEST)) {
1189                log.finest("responseCommands: " + responseCommands);
1190            }
1191
1192            modifications.setIdGenerator(cmdIdGenerator);
1193            modifications.setFlag(Flags.FLAG_ALL_RESPONSES_REQUIRED);
1194
1195            Status[] statusSyncs = (Status[])ProtocolUtil.filterCommands(
1196                responseCommands,
1197                new String JavaDoc[]{ Status.COMMAND_NAME }
1198            ).toArray(new Status[0]);
1199
1200            modifications.setClientModificationsStatus(statusSyncs);
1201
1202            // .setServerModifications sets all the modification commands
1203
// required to be send to client from Server
1204

1205            // cannot block this whole code,as this code may be used
1206
// by other DBS within same sync but differnt alert code.
1207
AbstractCommand[] serverModifications =
1208                    (AbstractCommand[])ProtocolUtil.filterCommands(
1209                        responseCommands,
1210                        new String JavaDoc[]{Sync.COMMAND_NAME}
1211                    ).toArray(new AbstractCommand[0]);
1212
1213            //
1214
// The server must send its modifications only after the client sent
1215
// the last message
1216
//
1217
modifications.setServerModifications(serverModifications);
1218
1219            return modifications;
1220
1221        } catch (Sync4jException e) {
1222            throw new ProtocolException(e);
1223        }
1224    }
1225
1226    /**
1227     * Checks if the given command can be sent in the current message without
1228     * exceeding maxMsgSize.
1229     *
1230     * @param cmd the command to check
1231     *
1232     * @return true if the command can be sent, false otherwise
1233     */

1234    private boolean checkSize(AbstractCommand cmd) {
1235        return (syncState.maxMsgSize != 0) &&
1236               ( (messageSize + sizeCalculator.getCommandSize(cmd)) < syncState.maxMsgSize);
1237    }
1238
1239    /**
1240     * Checks if the given size is available in the current message without
1241     * exceeding maxMsgSize.
1242     *
1243     * @param size the command to check
1244     *
1245     * @return true if the command can be sent, false otherwise
1246     */

1247    private boolean checkSize(long size) {
1248        return (syncState.maxMsgSize != 0) &&
1249               ( (messageSize + size) < syncState.maxMsgSize);
1250    }
1251
1252    /**
1253     * Checks if the given size is available in the current message without
1254     * exceeding maxMsgSize starting from the given message size.
1255     *
1256     * @param size the command to check
1257     * @param msgSize the message size
1258     *
1259     * @return true if the command can be sent, false otherwise
1260     */

1261    private boolean checkSize(long size, long msgSize) {
1262        return (syncState.maxMsgSize != 0) &&
1263               ( (msgSize + size) < syncState.maxMsgSize);
1264    }
1265
1266    /**
1267     * Check if into Sync command there is a MaxObjSize for LargeObject.
1268     * If present cache that value into SyncState.
1269     *
1270     * @param Sync[] the array of Sync command client
1271     */

1272    private void checkMaxObjSizeIntoSync(Sync[] syncCmds) {
1273        if (syncCmds != null && syncCmds.length > 0) {
1274            //
1275
// The MaxObjSize is equals for every Sync command
1276
//
1277
if (syncCmds[0].getMeta() != null &&
1278                syncCmds[0].getMeta().getMaxObjSize() != null
1279               ) {
1280                syncState.maxObjSize = syncCmds[0].getMeta().getMaxObjSize().longValue();
1281            }
1282        }
1283    }
1284
1285    /**
1286     * Executes the given modifications and for each of them returns a status
1287     * command. It forwards the execution to the synchronization engine.
1288     *
1289     * @param modifications synchronization commands containing the modifications
1290     * that the client requires
1291     *
1292     * @return an array of command objects, each containig the result of
1293     * a modification or a modification itself
1294     * @throws Sync4jException
1295     */

1296    private List processModifications(SyncModifications modifications)
1297    throws Sync4jException {
1298        Sync[] syncCommands = modifications.getClientSyncCommands();
1299
1300        ArrayList responseCommands = new ArrayList();
1301
1302        if ((syncCommands == null) || (syncCommands.length == 0)) {
1303            //
1304
// Nothing to process!
1305
//
1306
return responseCommands;
1307        }
1308
1309        SyncHdr header = modifications.getSyncHeader();
1310        String JavaDoc msgId = header.getMsgID() ;
1311        boolean headerNoResponse = header.isNoResp() ;
1312
1313        syncEngine.setCommandIdGenerator(cmdIdGenerator);
1314
1315        //
1316
// First of all prepare the memory sources with the modification commands
1317
// receveid by the client. Sent the Status commands to check if there
1318
// is a "Refresh required": in this case is necessary to do a slow sync.
1319
//
1320

1321        Status[] statusCommands =
1322            (Status[])ProtocolUtil.filterCommands(
1323                                             modifications.getClientCommands(),
1324                                             Status.class
1325            ).toArray(new Status[0]);
1326
1327        //
1328
// Check if in the given list of commands there is data to add
1329
// at the previous data.
1330
//
1331
if ((syncCommands != null) && (syncCommands.length>0)) {
1332            checkForReceivedLargeObject(syncCommands);
1333        }
1334
1335        //
1336
// First of all prepare the memory sources with the modification commands
1337
// receveid by the client
1338
//
1339
prepareMemorySources(syncCommands, statusCommands);
1340
1341        try {
1342            syncEngine.sync(syncState.loggedPrincipal);
1343        } catch (Sync4jException e) {
1344            log.throwing(getClass().getName(), "processModifications", e);
1345
1346            if (log.isLoggable(Level.SEVERE)) {
1347                log.severe(e.getMessage());
1348            }
1349        }
1350
1351        //
1352
// Status code for commands
1353
//
1354
if (headerNoResponse == false) {
1355            //
1356
// Sync commands
1357
//
1358
responseCommands.addAll(statusForSyncs(syncCommands));
1359
1360            //
1361
// Status for server-side executed modification commands
1362
//
1363
Status[] operationStatus =
1364                syncEngine.getModificationsStatusCommands(msgId);
1365
1366            for (int i=0; i<operationStatus.length; ++i) {
1367                responseCommands.add(operationStatus[i]);
1368            }
1369        }
1370
1371        //
1372
// SyncCommands sent back to the client
1373
// No sync sent to client if ONE_WAY_FROM_CLIENT
1374
//
1375

1376        Database[] dbs = syncEngine.getDbs();
1377        ItemizedCommand[] commands = null;
1378        String JavaDoc uri = null;
1379        for (int i = 0; (i < dbs.length); ++i) {
1380            //
1381
// If the alert represents a client only action, just ignore it
1382
//
1383
if (AlertCode.isClientOnlyCode(dbs[i].getMethod())) {
1384                continue;
1385            }
1386
1387            uri = dbs[i].getName();
1388
1389            SyncOperation[] operations = syncEngine.getSyncOperations(uri);
1390            commands = syncEngine.operationsToCommands(operations, uri, slow);
1391            Long JavaDoc noc = isNumberOfChangesSupported()
1392                     ? new Long JavaDoc(commands.length)
1393                     : null
1394                     ;
1395            responseCommands.add(
1396                    new Sync(
1397                            cmdIdGenerator.next(),
1398                            false,
1399                            null,
1400                            dbs[i].getTarget(),
1401                            dbs[i].getSource(),
1402                            null,
1403                            noc,
1404                            commands
1405                    )
1406            );
1407            syncEngine.resetSyncOperations(uri);
1408        } // next i
1409

1410        //
1411
// And here we can update the server-side LUID-GUID mappings
1412
//
1413
syncEngine.updateServerMappings(slow);
1414
1415        return responseCommands;
1416    }
1417
1418    /**
1419     * Process the completion message from the client check status for sync
1420     * command and update client mapping
1421     * @param message
1422     * @return the response message
1423     * @throws ProtocolException
1424     */

1425    private SyncMapping processMapMessage(SyncML message)
1426    throws ProtocolException {
1427        if (log.isLoggable(Level.INFO)) {
1428            log.info("Handling mapping ...");
1429        }
1430
1431        try {
1432            SyncMapping mapping = new SyncMapping(
1433                                  message.getSyncHdr(),
1434                                  message.getSyncBody()
1435                              );
1436
1437            mapping.setIdGenerator(cmdIdGenerator);
1438
1439            if (log.isLoggable(Level.INFO)) {
1440               log.info("Performing mapping ...");
1441            }
1442
1443            String JavaDoc uri = null;
1444
1445            sync4j.framework.core.Map[] mapCommands = mapping.getMapCommands();
1446            MapItem[] mapItems = null ;
1447            for (int j=0; ((mapCommands != null) && (j<mapCommands.length)); ++j) {
1448                uri = mapCommands[j].getTarget().getLocURI();
1449                mapItems =
1450                    (MapItem[])mapCommands[j].getMapItems().toArray(
1451                                                            new MapItem[0]);
1452
1453                for (int i = 0; i < mapItems.length; i++) {
1454                    MapItem mapItem = mapItems[i];
1455
1456                    //Adding item properties to the persistent mapping
1457
String JavaDoc guid = mapItem.getTarget().getLocURI();
1458                    String JavaDoc luid = mapItem.getSource().getLocURI();
1459                    syncEngine.updateMapping(uri, guid, luid);
1460                }
1461            }
1462
1463            return mapping;
1464
1465        } catch (Sync4jException e) {
1466            throw new ProtocolException(e);
1467        }
1468    }
1469
1470    /**
1471     * Makes a state transition. Very simple implementation at the moment: it
1472     * changes the value of <i>currentState</i> to the given value.
1473     *
1474     * @param state the new state
1475     */

1476    private void moveTo(int state) {
1477        if (log.isLoggable(Level.FINEST)) {
1478            log.finest("moving to state " + getStateName(state));
1479        }
1480        currentState = state;
1481    }
1482
1483    /**
1484     * Prepares the memory sources with the modification commands receveid
1485     * by the client.
1486     * <p>
1487     * Note that if the requested synchronization is a slow sync, the items are
1488     * inserted as "existing" items, regardless the command they belong to.
1489     * (maybe this will change as soon as the specification becomes clearer)
1490     * If there is no alert for slow sync, is need to check if exist a status
1491     * with "Refresh required" (code 508)
1492     *
1493     * @param syncCommands the commands used to prepare the source
1494     * @param statusCommands the status commands sent by the client
1495     *
1496     */

1497    private void prepareMemorySources(Sync[] syncCommands, Status[] statusCommands) {
1498
1499        List sources = syncEngine.getClientSources();
1500
1501        //
1502
// For efficiency: put the databases in a HashMap
1503
//
1504
HashMap dbMap = new HashMap();
1505        for (int i=0; ((syncEngine.getDbs() != null) && (i<syncEngine.getDbs().length)); ++i) {
1506            dbMap.put((syncEngine.getDbs())[i].getName(), (syncEngine.getDbs())[i]);
1507        }
1508
1509        //
1510
// First of all prepare the memory sources with the modification commands
1511
// receveid by the client
1512
//
1513
int method;
1514        slow = false;
1515        Target target = null;
1516        MemorySyncSource mss = null;
1517        AbstractCommand[] modifications = null;
1518        for (int i = sources.size(); i > 0; --i) {
1519            if (log.isLoggable(Level.FINEST)) {
1520                log.finest("Preparing "
1521                          + syncEngine.getClientSources().get(i - 1)
1522                          + " with "
1523                          + Arrays.asList(syncCommands)
1524                );
1525            }
1526
1527            mss = (MemorySyncSource) sources.get(i - 1);
1528
1529            String JavaDoc uri = mss.getSourceURI();
1530
1531            modifications = new AbstractCommand[0];
1532            ArrayList alModifications = new ArrayList();
1533
1534            //
1535
// The client device could send more Sync commands for the same source
1536
//
1537
for (int j = 0; ((syncCommands != null) && (j < syncCommands.length)); ++j) {
1538                target = syncCommands[j].getTarget();
1539                if ((target != null) && (uri.equals(target.getLocURI()))) {
1540                    if (syncCommands[j].getCommands() != null) {
1541                        alModifications.addAll(syncCommands[j].getCommands());
1542                    }
1543                }
1544            }
1545
1546            modifications = (AbstractCommand[])alModifications.toArray(new AbstractCommand[0]);
1547
1548            method = ((Database)dbMap.get(uri)).getMethod();
1549            slow = ((method == AlertCode.SLOW) || (method == AlertCode.REFRESH_FROM_SERVER));
1550
1551            if (!slow) {
1552                String JavaDoc targetRef = null;
1553                String JavaDoc statusCode = null;
1554                ArrayList targetRefs = null;
1555                for (int k=0; statusCommands!=null && k<statusCommands.length; k++) {
1556                    targetRefs = statusCommands[k].getTargetRef();
1557                    if (targetRefs == null || targetRefs.size() == 0) {
1558                        continue;
1559                    }
1560                    targetRef = ((TargetRef)targetRefs.get(0)).getValue();
1561                    if ((targetRef != null) && (uri.equals(targetRef))) {
1562                        statusCode = statusCommands[k].getData().getData();
1563                        if (statusCode.equals("" + StatusCode.REFRESH_REQUIRED)) {
1564                            slow = true;
1565                            ((Database)dbMap.get(uri)).setStatusCode(StatusCode.REFRESH_REQUIRED);
1566                        }
1567                        break;
1568                    }
1569                }
1570            }
1571
1572            prepareMemorySource(mss, modifications, slow);
1573        }
1574    }
1575
1576    /**
1577     * Prepares a source that represents the image of the client database. This
1578     * is done combining the existing client mapping with the modifications sent
1579     * by the client. Than this source can be compared with the server view of
1580     * the database.
1581     *
1582     * @param source the e source to prepare
1583     * @param commands the client modifications
1584     * @param slowSync true if the preparation is for a slow sync, false
1585     * otherwise
1586     *
1587     */

1588    private void prepareMemorySource(MemorySyncSource source ,
1589                                     AbstractCommand[] commands,
1590                                     boolean slowSync) {
1591        ArrayList deleted = new ArrayList();
1592        ArrayList created = new ArrayList();
1593        ArrayList updated = new ArrayList();
1594
1595        ClientMapping guidluid = null;
1596
1597        //
1598
// First of all, in the case of fast sync, the items already mapped are
1599
// added as existing items (with GUID as key).
1600
// In the case of slow sync, the mappings are cleared so that they
1601
// won't generate conflicts.
1602
//
1603
guidluid = (ClientMapping)syncEngine.getMapping(
1604                       syncState.loggedPrincipal,
1605                       source.getSourceURI(),
1606                       slowSync
1607                   );
1608
1609        String JavaDoc name = null;
1610        for (int i = 0; ((commands != null) && (i < commands.length)); ++i) {
1611            if (commands[i] == null) {
1612                continue;
1613            }
1614            name = commands[i].getName();
1615            if (slowSync && !Delete.COMMAND_NAME.equals(name)) {
1616                List items = Arrays.asList(
1617                                 syncEngine.itemsToSyncItems(
1618                                     source,
1619                                     (ModificationCommand)commands[i],
1620                                     SyncItemState.SYNCHRONIZED,
1621                                     nextTimestamp.start
1622                                 )
1623                             );
1624                updated.addAll(items);
1625
1626                continue;
1627            }
1628            if (Add.COMMAND_NAME.equals(commands[i].getName())) {
1629                created.addAll(
1630                        Arrays.asList(
1631                                syncEngine.itemsToSyncItems(
1632                                    source,
1633                                    (ModificationCommand)commands[i],
1634                                    SyncItemState.NEW,
1635                                    nextTimestamp.start
1636                                )
1637                        )
1638                );
1639                continue;
1640            }
1641
1642            if (Delete.COMMAND_NAME.equals(commands[i].getName())) {
1643                deleted.addAll(
1644                        Arrays.asList(
1645                                syncEngine.itemsToSyncItems(
1646                                    source,
1647                                    (ModificationCommand)commands[i],
1648                                    SyncItemState.DELETED,
1649                                    nextTimestamp.start
1650                                )
1651                        )
1652                );
1653                continue;
1654            }
1655
1656            if (Replace.COMMAND_NAME.equals(commands[i].getName())) {
1657                updated.addAll(
1658                        Arrays.asList(
1659                                syncEngine.itemsToSyncItems(
1660                                    source,
1661                                    (ModificationCommand)commands[i],
1662                                    SyncItemState.UPDATED,
1663                                    nextTimestamp.start
1664                                )
1665                        )
1666                );
1667                continue;
1668            }
1669        }
1670
1671        source.initialize(deleted, created, updated);
1672
1673        if (syncEngine.isLastMessage()) {
1674            source.setExistingItems(createItemsFromMapping(source));
1675        }
1676    }
1677
1678    /**
1679     * Create and return the status commands for the executed &lt;Sync&gt;s.
1680     *
1681     * @param syncCommands the Sync commands
1682     *
1683     * @return the status commands in a List collection
1684     */

1685    private List statusForSyncs(Sync[] syncCommands) {
1686        ArrayList ret = new ArrayList();
1687
1688        String JavaDoc uri = null;
1689        Target target = null;
1690        Source source = null;
1691        TargetRef targetRef = null;
1692        SourceRef sourceRef = null;
1693        int statusCode = StatusCode.OK;
1694        String JavaDoc statusMessage = "";
1695        Item[] items = null;
1696
1697        for (int i = 0; ( (syncCommands != null )
1698                        && (i < syncCommands.length)); ++i) {
1699
1700            target = syncCommands[i].getTarget();
1701            source = syncCommands[i].getSource();
1702
1703            //
1704
// A Sync command can be empty....
1705
//
1706
uri = (target==null) ? null : target.getLocURI();
1707            if ((uri == null) || (syncEngine.getClientSource(uri) != null)) {
1708                statusCode = syncEngine.getClientSourceStatus(uri);
1709                statusMessage = syncEngine.getClientStatusMessage(uri);
1710            } else {
1711                statusCode = StatusCode.NOT_FOUND;
1712                statusMessage = null;
1713            }
1714
1715            targetRef = (target == null) ? null : new TargetRef(uri);
1716
1717            sourceRef = (source == null) ? null : new SourceRef(syncCommands[i].getSource());
1718
1719            //
1720
// If there is a status message to report, add a new Item to the
1721
// Status command. Otherwise, no item is appended
1722
//
1723
if (statusMessage != null) {
1724                items = new Item[] {
1725                            new Item(null, null, null, new ComplexData(statusMessage), false)
1726                        };
1727            } else {
1728                items = new Item[0];
1729            }
1730
1731            ret.add(
1732                new Status(
1733                        cmdIdGenerator.next(),
1734                        lastMsgIdFromClient,
1735                        syncCommands[i].getCmdID().getCmdID(),
1736                        syncCommands[i].COMMAND_NAME,
1737                        targetRef,
1738                        sourceRef,
1739                        null,
1740                        null,
1741                        new Data(statusCode),
1742                        items
1743                )
1744            );
1745        } // next i
1746

1747        return ret;
1748    }
1749
1750    /**
1751     * Checks that the credentials of the given message are allowed to start a
1752     * session.
1753     *
1754     * @param credential the message
1755     * @param deviceId the deviceId
1756     */

1757    private boolean login(Cred credential, String JavaDoc deviceId) {
1758        //
1759
// May be the credential is already logged in...
1760
//
1761
logout();
1762
1763        if (credential == null) {
1764            syncState.authenticationState = AUTH_MISSING_CREDENTIALS;
1765            return false;
1766        }
1767
1768        Sync4jPrincipal p = Sync4jPrincipal.fromCredential(
1769            credential.getData(),
1770            credential.getType(),
1771            deviceId
1772        );
1773        Authentication auth = credential.getAuthentication();
1774        auth.setDeviceId(deviceId);
1775        auth.setSyncMLVerProto(syncState.syncMLVerProto);
1776        NextNonce nn = new NextNonce(syncState.device.getClientNonce());
1777        auth.setNextNonce(nn);
1778
1779        if (syncEngine.login(credential)) {
1780            if (clientAuth.equalsIgnoreCase(Cred.AUTH_TYPE_MD5)) {
1781                p.setUsername(credential.getAuthentication().getUsername());
1782            }
1783
1784            //
1785
// The logged principal is find only after the authentication process
1786
//
1787
p.setId(credential.getAuthentication().getPrincipalId());
1788
1789            if (syncEngine.authorize(p, SecurityConstants.RESOURCE_SESSION)) {
1790
1791                syncState.loggedCredential = credential;
1792                syncState.loggedPrincipal = p ;
1793
1794                syncState.authenticationState = AUTH_AUTHENTICATED;
1795
1796                return true;
1797            }
1798        }
1799        return false;
1800    }
1801
1802    /**
1803     * Logs out the logged in credential
1804     */

1805    private void logout() {
1806        if (isAuthenticated()) {
1807            syncEngine.logout(syncState.loggedCredential);
1808        }
1809        syncState.authenticationState = AUTH_INVALID_CREDENTIALS;
1810        syncState.loggedCredential = null;
1811        syncState.loggedPrincipal = null;
1812    }
1813
1814    /**
1815     * Called by the <i>SyncBean</i> when the container release the session.
1816     * It commit the change to the DB, logs out the credential and
1817     * release aquired resources.
1818     */

1819    public void endSession() {
1820        commit();
1821        logout();
1822    }
1823
1824    private String JavaDoc getStateName(int state) {
1825        String JavaDoc stateName = "STATE_UNKNOWN";
1826
1827        switch (state) {
1828            case STATE_START : stateName = "STATE_START" ; break;
1829            case STATE_END : stateName = "STATE_END" ; break;
1830            case STATE_PKG1_RECEIVING : stateName = "STATE_PKG1_RECEIVING" ; break;
1831            case STATE_PKG1_RECEIVED : stateName = "STATE_PKG1_RECEIVED" ; break;
1832            case STATE_PKG3_RECEIVING : stateName = "STATE_PKG3_RECEIVING" ; break;
1833            case STATE_PKG3_RECEIVED : stateName = "STATE_PKG3_RECEIVED" ; break;
1834            case STATE_ERROR : stateName = "STATE_ERROR" ; break;
1835            default : stateName = "STATE_UNKNOWN" ; break;
1836        }
1837
1838        return stateName;
1839    }
1840
1841    public SyncState getSyncState() {
1842        return this.syncState;
1843    }
1844
1845    /**
1846     * Checks if in the given list of commands there is data to add to
1847     * previous data. The command with the data to add is the first in the list.
1848     * If there is a command, merges the previous data with the data contained in
1849     * the command.
1850     *
1851     * @param syncs the command list to check
1852     *
1853     * @throws ProtocolException if the list not contains a valid Results
1854     */

1855    private void checkForReceivedLargeObject(Sync[] syncs) {
1856        if (log.isLoggable(Level.FINEST)) {
1857            log.finest("Checking if there are data to add to previous data");
1858        }
1859
1860        Item lo = null;
1861
1862        if (syncState.receivedLargeObject != null) {
1863            Item item =
1864                ProtocolUtil.getSyncItem(syncs, syncState.receivedLargeObject);
1865
1866            if (item == null) {
1867                if (log.isLoggable(Level.WARNING)) {
1868                    log.warning("The end of data for a chuncked objects has NOT been received!");
1869                }
1870
1871                createAlert(AlertCode.NO_END_OF_DATA);
1872
1873                syncState.receivedLargeObject = null;
1874                syncState.sizeOfReceivedLargeObject = null;
1875                syncState.syncLocURI = null;
1876                syncState.itemLocURI = null;
1877
1878                return;
1879            } else {
1880                if (!item.isMoreData()) {
1881                    //
1882
// This is the last chunk
1883
//
1884
syncState.receivedLargeObject = null;
1885                }
1886            }
1887        }
1888
1889        if (syncState.receivedLargeObject == null) {
1890            int i = 0;
1891            for (i=0; (lo == null) && (i<syncs.length); ++i) {
1892                lo = ProtocolUtil.getLargeObject(syncs[i].getCommands());
1893            }
1894
1895            if (lo == null) {
1896                //
1897
// no LO found
1898
//
1899
return;
1900            }
1901
1902            String JavaDoc loURI = (lo.getSource() != null)
1903                         ? lo.getSource().getLocURI()
1904                         : lo.getTarget().getLocURI()
1905                         ;
1906
1907            syncState.receivedLargeObject = syncs[i-1].getTarget().getLocURI()
1908                                          + '/'
1909                                          + loURI
1910                                          ;
1911            return;
1912        }
1913    }
1914
1915    private void createAlert(int alertCode) {
1916        if (syncState.receivedLargeObject == null) {
1917            return;
1918        }
1919
1920        int p = syncState.receivedLargeObject.lastIndexOf('/');
1921
1922        if (p<0) {
1923            return;
1924        }
1925
1926        String JavaDoc uri = syncState.receivedLargeObject.substring(p+1);
1927
1928        Item item = new Item(new Target(uri), null, null, null, false);
1929        ArrayList items = new ArrayList(1);
1930        items.add(item);
1931
1932        syncState.cmdCache3.add(
1933            new Alert(
1934                cmdIdGenerator.next(),
1935                false,
1936                null,
1937                alertCode,
1938                (Item[])(items.toArray(new Item[0]))
1939            )
1940        );
1941    }
1942
1943    /**
1944     * Returns an array of AbstractCommand containing the commands to send in
1945     * the response message taking into account engine state, sent commands and
1946     * max message size.
1947     *
1948     * @param init a SyncInitialization instance
1949     * @param sync a SyncModifications instance
1950     * @param map a ClientMapping instance
1951     *
1952     * @return an array of AbstractCommand containing the commands to send in
1953     * the response message
1954     *
1955     * @throws ProtocolException in case of protocol errors
1956     */

1957    private AbstractCommand[] commandsToSend(SyncInitialization init,
1958                                             SyncModifications sync,
1959                                             SyncMapping map )
1960    throws ProtocolException {
1961
1962        //
1963
// Step 1: cache all commands to be sent on the proper array list
1964
//
1965
if (init != null) {
1966            syncState.cmdCache1.addAll(init.getResponseCommands(msgIdGenerator.current()));
1967        }
1968
1969        if (sync != null) {
1970            List commands = sync.getResponseCommands(msgIdGenerator.current());
1971            List status = ProtocolUtil.filterCommands(commands, new String JavaDoc[] { Status.COMMAND_NAME });
1972            syncState.cmdCache1.addAll(status);
1973            commands.removeAll(status);
1974
1975            syncState.cmdCache3.addAll(commands);
1976            //
1977
// Note: this could be done more efficiently when inserting the
1978
// commands (instead of using addAll...
1979
//
1980
ProtocolUtil.mergeSyncCommands(syncState.cmdCache3);
1981        }
1982
1983        if (map != null) {
1984            List commands = map.getResponseCommands(msgIdGenerator.current());
1985            List status = ProtocolUtil.filterCommands(commands, new String JavaDoc[] { Status.COMMAND_NAME });
1986            syncState.cmdCache1.addAll(status);
1987            commands.removeAll(status);
1988            syncState.cmdCache3.addAll(commands);
1989        }
1990
1991        ProtocolUtil.removeHeaderStatus(syncState.cmdCache1);
1992
1993        //
1994
// Step 2: first of all we have to send all PKG#1 responses, status
1995
// first and then Alerts and Results
1996
//
1997
List cache = null; // which cache are we using?
1998
AbstractCommand[] allCmds = null;
1999        AbstractCommand[] statuses = null;
2000        AbstractCommand[] commands = null;
2001
2002        cache = new ArrayList();
2003
2004        //
2005
// Modification commands can be sent only after PKG1 has been completely
2006
// received
2007
//
2008
cache.addAll(syncState.cmdCache1);
2009        if (currentState == STATE_PKG3_RECEIVED) {
2010            cache.addAll(syncState.cmdCache3);
2011        }
2012
2013        allCmds = (AbstractCommand[])
2014                    cache.toArray(new AbstractCommand[cache.size()]);
2015
2016        ArrayList statusList = ProtocolUtil.filterCommands(allCmds, Status.class);
2017        ArrayList commandList = ProtocolUtil.inverseFilterCommands(allCmds, Status.class);
2018
2019        statuses = ProtocolUtil.sortStatusCommand(statusList);
2020        commands =
2021                (AbstractCommand[])commandList.toArray(new AbstractCommand[commandList.size()]);
2022
2023        //
2024
// If we would have to send server capabilities, but they are too big,
2025
// we remove them from the commands to send and we set a proper state.
2026
//
2027
checkSizeCapabilities();
2028
2029        //
2030
// Step 3: select the commands we can send without exceding maxMsgSize
2031
//
2032
commandList = new ArrayList();
2033        for (int i=0; i<statuses.length; ++i) {
2034            if (!checkSize(statuses[i])) {
2035                return (AbstractCommand[])commandList.toArray(new AbstractCommand[0]);
2036            }
2037
2038            messageSize += sizeCalculator.getCommandSize(statuses[i]);
2039            commandList.add(statuses[i]);
2040            syncState.cmdCache1.remove(statuses[i]);
2041        }
2042
2043        for (int i=0; i<commands.length; ++i) {
2044            if (!checkSize(commands[i])) {
2045                if (commands[i] instanceof Sync) {
2046                    Sync s = splitSync((Sync)commands[i]);
2047                    commandList.add(s);
2048                    messageSize += sizeCalculator.getCommandSize(s);
2049                }
2050
2051                return (AbstractCommand[])commandList.toArray(new AbstractCommand[0]);
2052            }
2053
2054            messageSize += sizeCalculator.getCommandSize(commands[i]);
2055            commandList.add(commands[i]);
2056            syncState.cmdCache1.remove(commands[i]);
2057            syncState.cmdCache3.remove(commands[i]);
2058        }
2059
2060        return (AbstractCommand[])commandList.toArray(new AbstractCommand[0]);
2061    }
2062
2063    /**
2064     * Given a Sync command, a new Sync command is created with no more items
2065     * then the ones that fit in the message. The item inserted into the new
2066     * command are removed from the input command.
2067     *
2068     * @param cmd the Sync commmand
2069     *
2070     * @return a new Sync command with only the items that fit into the message size
2071     *
2072     */

2073    private Sync splitSync(Sync cmd) {
2074        ArrayList commands = cmd.getCommands();
2075
2076        //
2077
// First of all remove all commands that will never be sent; these are:
2078
// - Items bigger then MaxObjSize (if specfied)
2079
// - Items bigger then MaxMsgSize-MSG_OVERHEAD if the client does not
2080
// support large object
2081
//
2082
boolean lo = isLargeObjectSupported();
2083        if (lo && (syncState.maxObjSize > 0)) {
2084            removeTooBigCommands(commands, syncState.maxObjSize);
2085        } else if (!lo) {
2086            removeTooBigCommands(commands, syncState.maxMsgSize - sizeCalculator.getMsgSizeOverhead());
2087        }
2088
2089        Sync newSync = new Sync( cmdIdGenerator.next(),
2090                                 cmd.isNoResp(),
2091                                 cmd.getCred(),
2092                                 cmd.getTarget(),
2093                                 cmd.getSource(),
2094                                 cmd.getMeta(),
2095                                 null,
2096                                 new AbstractCommand[0]);
2097
2098        ArrayList newCommands = new ArrayList();
2099
2100        long syncSize = sizeCalculator.getCommandSize(newSync);
2101        long tmpMessageSize = messageSize;
2102
2103        int howManyCommands = commands.size();
2104
2105        Iterator i = commands.iterator();
2106        while (i.hasNext()) {
2107            AbstractCommand c = (AbstractCommand)i.next();
2108
2109            if (!checkSize(syncSize + sizeCalculator.getCommandSize(c), tmpMessageSize)) {
2110                break;
2111            }
2112
2113            newCommands.add(c);
2114            tmpMessageSize += sizeCalculator.getCommandSize(c);
2115        }
2116
2117        commands.removeAll(newCommands);
2118
2119        //
2120
// If no commands were sent, let's send them as large objects (if
2121
// supported by the client).
2122
//
2123
if (lo && (howManyCommands > 0) && (howManyCommands == commands.size())) {
2124            ItemizedCommand c = (ItemizedCommand)commands.get(0);
2125            newCommands.add(nextLOChunk(cmd, c, tmpMessageSize));
2126            if (((Item)c.getItems().get(0)).getData().getData().length() == 0) {
2127                commands.remove(c);
2128            }
2129        }
2130
2131        //
2132
// If number of changes was specified, adjust it to the new values
2133
//
2134
if (cmd.getNumberOfChanges() != null) {
2135            cmd.setNumberOfChanges(new Long JavaDoc(commands.size()));
2136            newSync.setNumberOfChanges(new Long JavaDoc(newCommands.size()));
2137        }
2138
2139        newSync.setCommands((AbstractCommand[])newCommands.toArray(new AbstractCommand[0]));
2140
2141        return newSync;
2142    }
2143
2144    /**
2145     * Checks if the current mesasge is the last of the package. A message is
2146     * the last of the package if its corresponding command cache is empty and
2147     * the state is a "RECEIVED" state.
2148     *
2149     * @return true if the message is the last of the package, false otherwise
2150     */

2151    private boolean isLastMessage() {
2152        return ((currentState == STATE_PKG1_RECEIVED) && (syncState.cmdCache1.size() == 0))
2153            || ((currentState == STATE_PKG3_RECEIVED) && (syncState.cmdCache3.size() == 0))
2154            ;
2155    }
2156
2157    /**
2158     * Processes the client capabilities setting the device object accordingly.
2159     *
2160     * @param devInfo NULL - if null, no client caps were given
2161     */

2162    private void processClientCapabilities(DevInf devInfo) {
2163        if (devInfo != null) {
2164            syncState.devInf = devInfo;
2165        }
2166    }
2167
2168    /**
2169     * Removes the commands bigger then the given threshold from the given list.
2170     *
2171     * @param commands the command list
2172     * @param size the threshold size
2173     */

2174    private void removeTooBigCommands(List commands, long size) {
2175        ArrayList remove = new ArrayList();
2176
2177        Iterator i = commands.iterator();
2178        while(i.hasNext()) {
2179            ItemizedCommand c = (ItemizedCommand)i.next();
2180            Item item = (Item)c.getItems().get(0);
2181            Data d = item.getData();
2182            if ((d != null) && (d.getData().length() > size)) {
2183                if (log.isLoggable(Level.WARNING)) {
2184                    log.warning( "Item "
2185                               + item.getSource().getLocURI()
2186                               + " is bigger than the maximum allowed size ("
2187                               + size
2188                               + "). It is removed from the list of commands to send."
2189                               );
2190                }
2191                remove.add(c);
2192            }
2193        }
2194        commands.removeAll(remove);
2195    }
2196
2197    /**
2198     * Splits the data of the given command. The new chunk of data will be
2199     * returned in newly created command; data sent are removed from cmd. The
2200     * first time, a Meta with the object size is stored in cmd so that we can
2201     * detect when this is the first chunk or not.
2202     *
2203     * @param sync the Sync command containing cmd
2204     * @param cmd the command containing the object to split
2205     * @param msgSize the size of the message that will include the chunk
2206     *
2207     * @return boolean true if the data is splitted, false otherwise
2208     */

2209    private ItemizedCommand nextLOChunk(Sync sync, ItemizedCommand cmd, long msgSize) {
2210        if (!(cmd instanceof Add) && !(cmd instanceof Replace)) {
2211            //
2212
// This should never happen!!!
2213
//
2214
return null;
2215        }
2216
2217        Item item = (Item)cmd.getItems().get(0);
2218
2219        Data data = item.getData();
2220        String JavaDoc dataValue = data.getData();
2221        int dataLength = dataValue.length();
2222
2223        ItemizedCommand chunk = null;
2224
2225        Item chunkItem = new Item(item.getTarget() ,
2226                                  item.getSource() ,
2227                                  null ,
2228                                  new ComplexData(""),
2229                                  false );
2230
2231        String JavaDoc chunkURI = null;
2232        if (cmd instanceof Add) {
2233            chunk = new Add(cmd.getCmdID() ,
2234                            cmd.isNoResp() ,
2235                            cmd.getCred() ,
2236                            cmd.getMeta() ,
2237                            new Item[] { chunkItem });
2238            chunkURI = chunkItem.getSource().getLocURI();
2239        } else if (cmd instanceof Replace) {
2240            chunk = new Replace(cmd.getCmdID() ,
2241                                cmd.isNoResp() ,
2242                                cmd.getCred() ,
2243                                cmd.getMeta() ,
2244                                new Item[] { chunkItem });
2245            chunkURI = chunkItem.getTarget().getLocURI();
2246        }
2247
2248        String JavaDoc loURI = sync.getSource().getLocURI()
2249                     + '/'
2250                     + chunkURI
2251                     ;
2252        if (!loURI.equals(syncState.sendingLOURI)) {
2253            Meta m = cmd.getMeta();
2254            if (m == null) {
2255                m = new Meta();
2256            }
2257
2258            Meta chunkMeta = new Meta();
2259            chunkMeta.setType(m.getType());
2260            chunkMeta.setSize(new Long JavaDoc(dataLength));
2261
2262            chunk.setMeta(chunkMeta);
2263
2264            syncState.sendingLOURI = loURI;
2265        } else {
2266            chunk.setMeta(null);
2267        }
2268
2269        //
2270
// calculate the chunk length
2271
//
2272
int chunkLength =
2273            (int)(syncState.maxMsgSize - (msgSize + sizeCalculator.getMsgSizeOverhead()));
2274        if (dataLength < chunkLength) {
2275            chunkItem.getData().setData(dataValue);
2276            chunkItem.setMoreData(Boolean.FALSE);
2277            item.getData().setData("");
2278        } else {
2279            chunkItem.getData().setData(dataValue.substring(0, chunkLength));
2280            chunkItem.setMoreData(Boolean.TRUE);
2281            item.getData().setData(dataValue.substring(chunkLength));
2282        }
2283
2284        return chunk;
2285    }
2286
2287    /**
2288     * Does the client support large object? The returned value is the one
2289     * in the device object if not differently specified by the client in the
2290     * device capabilities. If so, the information in the DevInf object
2291     * overwrites the one stored into the database.
2292     */

2293    private boolean isLargeObjectSupported() {
2294        return
2295            (syncState.maxObjSize > 0)
2296            ||
2297            ((syncState.devInf != null) && syncState.devInf.isSupportLargeObjs())
2298            ||
2299            syncState.device.getCapabilities().isSupportLargeObject()
2300            ;
2301    }
2302
2303    /**
2304     * Does the client support large object? The returned value is the one
2305     * in the device object if not differently specified by the client in the
2306     * device capabilities. If so, the information in the DevInf object
2307     * overwrites the one stored into the database.
2308     */

2309    private boolean isNumberOfChangesSupported() {
2310        if (syncState.devInf != null) {
2311            return syncState.devInf.isSupportNumberOfChanges();
2312        }
2313
2314        return syncState.device.getCapabilities().isSupportNumberOfChanges();
2315    }
2316
2317    /**
2318     * Recreates the device sync sources from the LUID-GUID mapping.
2319     *
2320     * @param source the source the items belong to
2321     *
2322     * @return a List representing the items on the device database
2323     */

2324    private List createItemsFromMapping(SyncSource source) {
2325        ClientMapping m = syncEngine.getMapping(source.getSourceURI());
2326
2327        ArrayList items = new ArrayList();
2328
2329        if (m == null) {
2330            if (log.isLoggable(Level.SEVERE)) {
2331                log.severe( "Client mapping not found for "
2332                          + source.getSourceURI()
2333                          + '!'
2334                          );
2335                return items;
2336            }
2337        }
2338
2339        java.util.Map JavaDoc map = m.getMapping();
2340
2341        Iterator i = map.keySet().iterator();
2342        Object JavaDoc key = null;
2343        while (i.hasNext()) {
2344            key = i.next();
2345            items.add(
2346                new SyncItemImpl(source, key, map.get(key), SyncItemState.SYNCHRONIZED)
2347            );
2348        }
2349
2350        return items;
2351    }
2352}
2353
Popular Tags