KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > sync4j > framework > protocol > ClientModifications


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
19 package sync4j.framework.protocol;
20
21 import java.util.List JavaDoc;
22 import java.util.ArrayList JavaDoc;
23
24 import sync4j.framework.core.*;
25 import sync4j.framework.database.Database;
26
27 import sync4j.framework.protocol.ProtocolUtil;
28 import sync4j.framework.protocol.v11.ClientModificationsRequirements;
29
30 /**
31  * Represents a Client Modification package of the SyncML protocol.
32  *
33  * The class is designed to be used in two times. First a <i>ClientModification</i>
34  * is created and checked for validity and compliancy with the protocol. Than
35  * <i>getResponse()</i> can be used to get a response message for the given
36  * request. During the request validation process some information about the
37  * request message are cached into instance variables and used in <i>getResponse()</i>.<br>
38  *
39  * @author Stefano Fornari @ Funambol
40  *
41  * @version $Id: ClientModifications.java,v 1.1 2005/05/16 17:32:55 nichele Exp $
42  */

43 public class ClientModifications
44 extends SyncPackage
45 implements Flags {
46
47     // ------------------------------------------------------------ Constructors
48

49     /** Constructors. It creates a new instance from the message header and body
50      * plus the databases to synchronize.
51      * It also checks if the requirements specified by the SyncML protocol are
52      * met; if not a Sync4jException is thrown.
53      * @param syncHeader the header of the syncronization packet
54      * @param syncBody the body of the syncronization packet
55      * @param syncDb the array of databases to be synchronized
56      * @throws Sync4jException in case SyncML requiremets are not respected
57      */

58     public ClientModifications(final SyncHdr syncHeader,
59                                final SyncBody syncBody ,
60                                Database[] syncDb )
61     throws ProtocolException {
62         super(syncHeader, syncBody);
63         checkRequirements();
64         databases = syncDb;
65     }
66     
67     public ClientModifications(final SyncHdr syncHeader,
68                                final SyncBody syncBody )
69     throws Sync4jException {
70         super(syncHeader, syncBody);
71     }
72     
73     // -------------------------------------------------------------- Properties
74

75     /**
76      * Has the server sent its capabilities and is expecting a response?
77      * If yes, <i>serverCapabilitiesCmdId</i> is set to the id of the Put command
78      * sent by the server. If not, <i>serverCapabilitiesCmdId</i> is empty.
79      */

80     private CmdID serverCapabilitiesCmdId = null;
81
82     /**
83      * Returns the serverCapabilitiesCmdId property.
84      *
85      * @return the serverCapabilitiesCmdId property.
86      */

87     public CmdID getServerCapabilitiesCmdId() {
88         return this.serverCapabilitiesCmdId;
89     }
90
91     /**
92      * Sets the serverCapabilitiesCmdId property.
93      *
94      * @param serverCapabilitiesCmdId new value
95      */

96     public void setServerCapabilitiesCmdId(CmdID serverCapabilitiesCmdId) {
97         this.serverCapabilitiesCmdId = serverCapabilitiesCmdId;
98     }
99
100     /**
101      * Has the server requested client capabilities?
102      * If yes, <i>clientCapabilitiesCmdId</i> is set to the id of the Get command
103      * sent by the server. If not, <i>clientCapabilitiesCmdId</i> is empty.
104      */

105     private CmdID clientCapabilitiesCmdId = null;
106
107     /**
108      * Returns the clientCapabilitiesCmdId property.
109      *
110      * @return the clientCapabilitiesCmdId property.
111      */

112     public CmdID getClientCapabilitiesCmdId() {
113         return this.clientCapabilitiesCmdId;
114     }
115
116     /**
117      * Sets the serverCapabilitiesCmdId property.
118      * @param clientCapabilitiesCmdId new value
119      */

120     public void setClientCapabilitiesCmdId(CmdID clientCapabilitiesCmdId) {
121         this.clientCapabilitiesCmdId = clientCapabilitiesCmdId;
122     }
123
124     /**
125      * The results command in response to the request of client capabilities
126      */

127     private Results clientCapabilitiesResults = null;
128
129     /**
130      * Returns the clientCapabilitiesResults property.
131      *
132      * @return the clientCapabilitiesResults property.
133      */

134     public Results getClientCapabilitiesResults() {
135         return this.clientCapabilitiesResults;
136     }
137
138     /**
139      * The status command in response to the sending of server capabilities
140      */

141     private Status serverCapabilitiesStatus = null;
142
143     /**
144      * Returns the serverCapabilitiesStatus property.
145      *
146      * @return the serverCapabilitiesStatus property.
147      */

148     public Status getServerCapabilitiesStatus() {
149         return this.serverCapabilitiesStatus;
150     }
151
152     /**
153      * The client Sync command identifier. It is used when a response is required.
154      */

155     private CmdID clientSyncCmdId = null;
156
157     /**
158      * Returns the clientSyncCmdId property.
159      *
160      * @return the clientSyncCmdId property.
161      */

162     public CmdID getClientSyncCmdId() {
163         return this.clientSyncCmdId;
164     }
165
166     /**
167      * Sets the clientSyncCmdId property.
168      *
169      * @param clientSyncCmdId new value
170      */

171     public void setClientSyncCmdId(CmdID clientSyncCmdId) {
172         this.clientSyncCmdId = clientSyncCmdId;
173     }
174
175     /**
176      * The modification commands the server wants to sent to the client.
177      */

178     private AbstractCommand[] serverModifications = null;
179
180     /**
181      * Returns the serverModifications property.
182      *
183      * @return the serverModifications property.
184      */

185     public AbstractCommand[] getServerModifications() {
186         return this.serverModifications;
187     }
188
189     /**
190      * Sets the serverModifications property.
191      *
192      * @param serverModifications new value
193      */

194     public void setServerModifications(AbstractCommand[] serverModifications) {
195         this.serverModifications = serverModifications;
196     }
197
198     /**
199      * The status to be returned for the client sync command.
200      */

201     private Status[] clientModificationsStatus = null;
202
203     /**
204      * Returns the clientModificationsStatus property.
205      *
206      * @return the clientModificationsStatus property.
207      */

208     public Status[] getClientModificationsStatus() {
209         return this.clientModificationsStatus;
210     }
211
212     /**
213      * Sets the clientModificationsStatus property.
214      *
215      * @param clientModificationsStatus new value
216      */

217     public void setClientModificationsStatus(Status[] clientModificationsStatus) {
218         this.clientModificationsStatus = clientModificationsStatus;
219     }
220
221     /**
222      * Caches the commands sent by the client. It is set during the
223      * checking of the requirements.
224      */

225     private AbstractCommand[] clientCommands = null;
226
227     /**
228      * Returns the clientCommands property.
229      *
230      * @return the clientCommands property.
231      */

232     public AbstractCommand[] getClientCommands() {
233         return clientCommands;
234     }
235
236     /**
237      * Caches the SyncCommand sent by the client. It is set during the checking
238      * of requirements.
239      */

240     private Sync[] clientSyncCommands = null;
241
242     /**
243      * Returns the clientSyncCommands property.
244      *
245      * @return the clientSyncCommands property.
246      */

247     public Sync[] getClientSyncCommands() {
248         return this.clientSyncCommands;
249     }
250
251     /**
252      * Databases that the server wants to synchronize.
253      */

254     private Database[] databases = null;
255
256     /**
257      * Sets the databases property.
258      *
259      * @param databases new value
260      */

261     public void setDatabases(Database[] databases) {
262         this.databases = databases;
263     }
264
265     /**
266      * Returns the databases property.
267      *
268      * @return the databases property.
269      */

270     public Database[] getDatabases() {
271         return this.databases;
272     }
273     
274     /**
275      * The alert commands the server wants to sent to the client.
276      */

277     private Alert[] modificationsAlert = null;
278     
279     /**
280      * Returns the alertModifications property.
281      *
282      * @return the alertModifications property.
283      */

284     public Alert[] getModificationsAlert() {
285         return this.modificationsAlert;
286     }
287     
288     /**
289      * Sets the alertModifications property.
290      *
291      * @param alertModifications new value
292      */

293     public void setModificationsAlert(Alert[] modificationsAlert) {
294         this.modificationsAlert = modificationsAlert;
295     }
296
297     // ---------------------------------------------------------- Public methods
298

299     /** Checks that all requirements regarding the header of the initialization
300      * packet are respected.
301      * @throws ProtocolException if header requirements are not respected
302      */

303     public void checkHeaderRequirements() throws ProtocolException {
304         ClientModificationsRequirements.checkDTDVersion (syncHeader.getVerDTD() );
305         ClientModificationsRequirements.checkProtocolVersion(syncHeader.getVerProto() );
306         ClientModificationsRequirements.checkSessionId (syncHeader.getSessionID());
307         ClientModificationsRequirements.checkMessageId (syncHeader.getMsgID() );
308         ClientModificationsRequirements.checkTarget (syncHeader.getTarget() );
309         ClientModificationsRequirements.checkSource (syncHeader.getSource() );
310     }
311
312     /** Checks that all requirements regarding the body of the initialization
313      * packet are respected.
314      *
315      * NOTE: bullet 2 pag 34 is not clear. Ignored for now.
316      * @throws ProtocolException if body requirements are not respected
317      */

318     public void checkBodyRequirements() throws ProtocolException {
319         // NOTE: initializes the clientCommands property
320
clientCommands = (AbstractCommand[])syncBody.getCommands().toArray(new AbstractCommand[0]);
321
322         //
323
// If the server sent the device information to the client and requested
324
// a response, serverCapabilitiesCmdId contains the command id of the
325
// request command. A Status command with the same cmd id reference
326
// must exist.
327
//
328
checkServerCapabilitiesStatus();
329
330         //
331
// If the server requested the device capabilities of the client,
332
// clientCapabilitiesCmdId contains the command id of the Get command.
333
// A Results command with the same cmd id reference must exist.
334
//
335
checkClientCapabilitiesResult();
336
337         //
338
// The Sync command must exists
339
//
340
checkSyncCommand();
341     }
342
343     // ----------------------------------------------------------- getResponse()
344

345     /**
346      * Constructs a proper response message.<p>
347      * The sync package to the client has the following purposes:
348      * <ul>
349      * <li>To inform the client about the results of sync analysis.
350      * <li>To inform about all data modifications, which have happened in the
351      * server since the previous time when the server has sent the
352      * modifications to the client.
353      * </ul>
354      *
355      * @param msgId the msg id of the response
356      * @return the response message
357      *
358      * @throws ProtocolException in case of error or inconsistency
359      */

360     public SyncML getResponse(String JavaDoc msgId) throws ProtocolException {
361         ArrayList JavaDoc commandList = new ArrayList JavaDoc();
362
363         if (idGenerator == null) {
364             throw new NullPointerException JavaDoc("The id generator is null. Please set a value for idGenerator");
365         }
366
367         //
368
// Constructs all required response commands.
369
//
370
// NOTE: if NoResp is specified in the header element, than no
371
// response commands must be returned regardless NoResp is
372
// specified or not in subsequent commands
373
//
374
if (syncHeader.isNoResp() == false) {
375
376             TargetRef[] targetRefs = new TargetRef[] { new TargetRef(syncHeader.getTarget().getLocURI()) };
377             SourceRef[] sourceRefs = new SourceRef[] { new SourceRef(syncHeader.getSource().getLocURI()) };
378
379             Status statusCommand = new Status(
380                 idGenerator.next() ,
381                 syncHeader.getMsgID() ,
382                 "0" /* command ref */ ,
383                 "SyncHdr" /* see SyncML specs */ ,
384                 targetRefs ,
385                 sourceRefs ,
386                 null /* credential */ ,
387                 null /* challenge */ ,
388                 new Data(StatusCode.OK) ,
389                 new Item[0]
390             );
391
392             commandList.add(statusCommand);
393
394             //
395
// 2. The Status element MUST be included in SyncBody if requested by
396
// the client. It is now used to indicate the general status of
397
// the sync analysis and the status information related to data
398
// items sent by the client (e.g., a conflict has happened.).
399
//
400
for (int i=0; ( isFlag(FLAG_SYNC_STATUS_REQUIRED)
401                           && (clientModificationsStatus != null)
402                           && (i<clientModificationsStatus.length) ); ++i) {
403                 commandList.add(clientModificationsStatus[i]);
404             }
405         }
406         
407         //
408
// The Alert element, if present, MUST be included in SyncBody.
409
//
410
for (int i=0; ((modificationsAlert != null) && (i<modificationsAlert.length)); ++i) {
411             commandList.add(modificationsAlert[i]);
412         }
413
414         //
415
// 3. The Sync element MUST be included in SyncBody, if earlier there
416
// were no occurred errors, which could prevent the server to process
417
// the sync analysis and to send its modifications back to the client.
418
//
419
for (int i=0; ((serverModifications != null) && (i<serverModifications.length)); ++i) {
420             commandList.add(serverModifications[i]);
421         }
422
423         //
424
// Constructs return message
425
//
426
Target target = new Target(syncHeader.getSource().getLocURI(),
427                                    syncHeader.getSource().getLocName());
428         Source source = new Source(syncHeader.getTarget().getLocURI(),
429                                    syncHeader.getTarget().getLocName());
430         SyncHdr responseHeader = new SyncHdr (
431             getDTDVersion() ,
432             getProtocolVersion() ,
433             syncHeader.getSessionID(),
434             msgId ,
435             target ,
436             source ,
437             null /* response URI */ ,
438             false ,
439             null /* credentials */ ,
440             null /* meta data */
441         );
442
443         AbstractCommand[] commands = null;
444         int size = commandList.size();
445         if (size == 0) {
446             commands = new AbstractCommand[1];
447         } else {
448             commands = new AbstractCommand[size];
449         }
450         for (int i=0; i < size; i++) {
451             commands[i] = (AbstractCommand)commandList.get(i);
452         }
453
454         SyncBody responseBody = new SyncBody(
455             commands,
456             isFlag(FLAG_FINAL_MESSAGE) /* final */
457         );
458
459         try {
460             return new SyncML(responseHeader, responseBody);
461         } catch (RepresentationException e) {
462             //
463
// It should never happen !!!!
464
//
465
throw new ProtocolException("Unexpected error", e);
466         }
467     }
468
469     /**
470      * Create a Status command for the Sync sent by the client.
471      *
472      * <b>NOTE</b>: the protocol does not specify any information about the format
473      * and the content of this message. By now a dummy status command is created
474      * and returned.
475      *
476      * @return a StatusCommand object
477      */

478     public Status createSyncStatusCommand() {
479         return new Status(
480             idGenerator.next() ,
481             "0" /* message id; TO DO */ ,
482             clientSyncCmdId.getCmdID() ,
483             Sync.COMMAND_NAME ,
484             (TargetRef[])null /* target refs */,
485             (SourceRef[])null /* source refs */,
486             null /* credential */ ,
487             null /* chal */ ,
488             null /* Data */ ,
489             null /* items */
490         );
491
492     }
493
494     /** For the Sync element, there are the following requirements.
495      * <ul>
496      * <li> CmdID is required.
497      * <li> The response can be required for the Sync command. (See the Caching of Map Item,
498      * Chapter 2.3.1)
499      * <li> Target is used to specify the target database.
500      * <li> Source is used to specify the source database.
501      * </ul>
502      *
503      * 5. If there is any modification in the server after the previous sync,
504      * there are following requirements for the operational elements (e.g.,
505      * Replace, Delete, and Add 4 ) within the Sync element.
506      * <ul>
507      * <li> CmdID is required.
508      * <li> The response can be required for these operations.
509      * <li> Source MUST be used to define the temporary GUID (See Definitions)
510      * of the data item in the server if the operation is an addition.
511      * If the operation is not an addition, Source MUST NOT be included.
512      * <li> Target MUST be used to define the LUID (See Definitions) of the
513      * data item if the operation is not an addition. If the operation is
514      * an addition, Target MUST NOT be included.
515      * <li> The Data element inside Item is used to include the data itself if
516      * the operation is not a seletion.
517      * <li> The Type element of the MetaInf DTD MUST be included in the Meta
518      * element to indicate the type of the data item (E.g., MIME type).
519      * The Meta element inside an operation or inside an item can be used.
520      * </ul>
521      * @param db the database to be synchronized
522      * @return a Sync command
523      * @throws ProtocolException if any protocol requirement is not respected
524      */

525     public Sync createSyncCommand(Database db)
526     throws ProtocolException {
527         CmdID syncId = idGenerator.next();
528
529         AbstractCommand[] commands = null;
530
531         // if db.getMethod is One_Way_Sync_CLIENT
532
// no synccommand from Server to Client
533

534         if(db.getMethod() != AlertCode.ONE_WAY_FROM_CLIENT){
535             commands = prepareCommands(db);
536         }
537
538         return new Sync(
539             syncId ,
540             isFlag(FLAG_SYNC_RESPONSE_REQUIRED),
541             null , /* credentials */
542             db.getTarget() ,
543             db.getSource() ,
544             null , /* Meta */
545             0,
546             commands
547         );
548
549     }
550
551     /**
552      * Returns an array of synchronization commands (Add, Copy, Delete, Exec,
553      * Replace) based on the content of the given database.
554      *
555      * @param db the database to be synchronized
556      *
557      * @return an array of AbstractCommand
558      */

559     public AbstractCommand[] prepareCommands(Database db) {
560         ArrayList JavaDoc commands = new ArrayList JavaDoc();
561
562         Meta meta = new Meta();
563         meta.setType(db.getType());
564
565         Item[] items = null; // reused many times
566

567         //
568
// Add
569
//
570
items = db.getAddItems();
571         if (items != null) {
572             commands.add(
573                 new Add(
574                     idGenerator.next() ,
575                     isFlag(FLAG_MODIFICATIONS_RESPONSE_REQUIRED),
576                     null /* credentials */ ,
577                     meta ,
578                     items )
579             );
580         }
581
582         //
583
// Copy
584
//
585
items = db.getCopyItems();
586         if (items != null) {
587             commands.add(
588                 new Copy(
589                     idGenerator.next() ,
590                     isFlag(FLAG_MODIFICATIONS_RESPONSE_REQUIRED),
591                     null /* credentials */ ,
592                     meta ,
593                     items )
594             );
595         }
596
597         //
598
// Delete
599
//
600
items = db.getDeleteItems();
601         if (items != null) {
602             commands.add(
603                 new Delete(
604                     idGenerator.next() ,
605                     isFlag(FLAG_MODIFICATIONS_RESPONSE_REQUIRED),
606                     isFlag(FLAG_ARCHIVE_DATA) ,
607                     isFlag(FLAG_SOFT_DELETE) ,
608                     null /* credentials */ ,
609                     meta ,
610                     items )
611             );
612         }
613
614         //
615
// Exec
616
//
617
items = db.getExecItems();
618
619         for (int i=0; ((items != null) && (i<items.length)); ++i) {
620             commands.add(
621                 new Exec(
622                     idGenerator.next() ,
623                     isFlag(FLAG_MODIFICATIONS_RESPONSE_REQUIRED),
624                     null /* credentials */ ,
625                     items[i] )
626             );
627         }
628
629         //
630
// Replace
631
//
632
items = db.getReplaceItems();
633         if (items != null) {
634             commands.add(
635                 new Replace(
636                     idGenerator.next() ,
637                     isFlag(FLAG_MODIFICATIONS_RESPONSE_REQUIRED),
638                     null /* credentials */ ,
639                     meta ,
640                     items )
641             );
642         }
643
644         int size = commands.size();
645         AbstractCommand [] aCommands = new AbstractCommand[size];
646         for (int i=0; i < size; i++) {
647             aCommands[i] = (AbstractCommand)commands.get(i);
648         }
649         return aCommands;
650     }
651
652     // --------------------------------------------------------- Private methods
653

654     /**
655      * Checks if the requested status for server capabilities has been specified.
656      * <p>
657      *
658      * @throws ProtocolException
659      */

660     private void checkServerCapabilitiesStatus()
661     throws ProtocolException {
662         //
663
// If serverCapabilitiesCmdId is null no serverCapabilities status is required
664
//
665
if (serverCapabilitiesCmdId == null) return;
666
667         List JavaDoc list = ProtocolUtil.filterCommands(clientCommands ,
668                                                 Status.class ,
669                                                 serverCapabilitiesCmdId);
670
671         if (list.size() == 0) {
672             Object JavaDoc[] args = new Object JavaDoc[] { serverCapabilitiesCmdId.getCmdID() };
673             throw new ProtocolException(ClientModificationsRequirements.ERRMSG_MISSING_STATUS_COMMAND, args);
674         }
675
676         serverCapabilitiesStatus = (Status)list.get(0);
677     }
678
679     /**
680      * Checks if the result for client capabilities has been given.
681      * <p>
682      *
683      * @throws ProtocolException
684      */

685     private void checkClientCapabilitiesResult()
686     throws ProtocolException {
687         //
688
// If clientCapabilitiesCmdId is null no client capabilities were required
689
//
690
if (clientCapabilitiesCmdId == null) return;
691
692         List JavaDoc list = ProtocolUtil.filterCommands(clientCommands ,
693                                                 Results.class ,
694                                                 clientCapabilitiesCmdId);
695
696         if (list.size() == 0) {
697             Object JavaDoc[] args = new Object JavaDoc[] { clientCapabilitiesCmdId.getCmdID() };
698             throw new ProtocolException(ClientModificationsRequirements.ERRMSG_MISSING_RESULTS_COMMAND, args);
699         }
700
701         Results results = (Results)list.get(0);
702
703         ClientModificationsRequirements.checkCapabilities(
704             results,
705             ClientModificationsRequirements.CLIENT_CAPABILITIES
706         );
707
708         clientCapabilitiesResults = results;
709     }
710
711     /**
712      * Checks the Sync command.
713      * <p>Filters out the Sync Messages from client to Server with
714      * ONE_WAY_SYNC_SERVER
715      *
716      * @throws ProtocolException
717      */

718     private void checkSyncCommand()
719     throws ProtocolException {
720         List JavaDoc list = ProtocolUtil.filterCommands(clientCommands ,
721                                                 Sync.class);
722
723         if (list.size() == 0) {
724             clientSyncCommands = new Sync[0];
725             return;
726         }
727
728         clientSyncCommands = (Sync[])list.toArray(new Sync[list.size()]);
729     }
730
731 }
Popular Tags