KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > sync4j > syncclient > spap > ApplicationManager


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.syncclient.spap;
19
20 import java.io.*;
21 import java.sql.Timestamp JavaDoc;
22 import java.util.Date JavaDoc;
23 import java.util.Vector JavaDoc;
24 import java.util.Hashtable JavaDoc;
25 import java.util.Properties JavaDoc;
26 import java.util.Enumeration JavaDoc;
27 import java.util.jar.JarFile JavaDoc;
28 import java.util.zip.ZipEntry JavaDoc;
29
30 import sync4j.syncclient.common.StringTools;
31 import sync4j.syncclient.spdm.*;
32
33 import sync4j.syncclient.sps.common.DataStoreMetadata;
34 import sync4j.syncclient.spap.*;
35
36 /**
37  * This class is responsible for the management of the SyncClient Conduit
38  * applications. It performs the following tasks:
39  * <ul>
40  * <li>install new applications
41  * <li>remove (uninstall) applications
42  * <li>configure applications
43  * </ul>
44  *
45  * <b>Installation packages</b>
46  * <p>
47  * An installation package is a jar file containing a SyncClient Conduit
48  * application. It is structured as follows:
49  * <pre>
50  * - {application-description-file}.adf
51  * - {other packages and classes}
52  * - bin
53  * - {other binary files}
54  * </pre>
55  * For example:
56  * <pre>
57  * - soccerleagues.adf
58  * - com
59  * - funambol
60  * - soccerleagues
61  * - SoccerleaguesStoreManager.class
62  * - bin
63  * - Leagues.prc
64  * - Italy.pdb
65  * - England.pdb
66  * </pre>
67  *
68  * The package is first save as an asset, so that it can be handled by the
69  * application provisioning subsystem.
70  *
71  * @author Stefano Fornari
72  * @version $Id: ApplicationManager.java,v 1.2 2005/01/19 11:18:36 fabius Exp $
73  */

74 public class ApplicationManager {
75
76     // --------------------------------------------------------------- Constants
77

78     public static String JavaDoc[] ALLOWED_SYNC_MODES = new String JavaDoc[] {
79         "none", "slow", "two-way", "one-way", "refresh"
80     };
81
82     // ------------------------------------------------------------ Private Data
83

84     /**
85      * The application configuration management tree
86      */

87     private ManagementNode applicationConfigNode;
88
89
90     // ------------------------------------------------------------ Constructors
91

92     /**
93      * Creates a new instance of ApplicationManager. It stores the application
94      * configuration management tree and the executables directory for further
95      * use.
96      *
97      * @param applicationConfigNode application configuration management tree
98      *
99      */

100     public ApplicationManager(ManagementNode applicationConfigNode) {
101         this.applicationConfigNode = applicationConfigNode;
102
103     }
104
105     // ---------------------------------------------------------- Public methods
106

107     /**
108      * Install an application packaged in a jar/zip file as described in the class
109      * description.
110      * <p>
111      * The file is read and copied in the installation directory; then the DM
112      * structure required by the application provisioning subsystem is created
113      * so that the application looks like an installable asset.
114      *
115      * @param f the package file
116      *
117      * @throws ApplicationManagementException if an error occurs during installation
118      */

119     public Application install(File JavaDoc f)
120     throws ApplicationManagementException {
121         String JavaDoc xmlADF = null;
122         String JavaDoc applicationURI = null;
123         String JavaDoc adfFileName = null;
124
125         int l = 0;
126
127         Application app = null;
128         Asset asset = null;
129         try {
130             adfFileName = getADFNameFromJar(f);
131             xmlADF = readADFFromJar (f);
132             app = applicationFromADF(xmlADF);
133             asset = makeAsset(app, f);
134
135             AssetManager assetManager = AssetManager.getAssetManager();
136
137             assetManager.addAsset(asset);
138
139             assetManager.installAsset(asset.getId());
140             return app;
141         } catch (FileNotFoundException e) {
142             throw new ApplicationManagementException("Package " + f + " not found!", e);
143         } catch (IOException e) {
144             throw new ApplicationManagementException("Error reading package " + f, e);
145         } catch (AssetManagementException e) {
146             throw new ApplicationManagementException("Error storing the asset " + e, e);
147         }
148     }
149
150     /**
151      * Uninstall the application ideintified by the given application URI.
152      * It removes both the application classes and configuration files.
153      *
154      * @param app the application to unistall
155      *
156      * @throws ApplicationManagementException in case of errors
157      */

158     public void uninstall(Application app)
159     throws ApplicationManagementException {
160         Asset asset = null;
161         try {
162             asset = makeAsset(app, null);
163
164             AssetManager assetManager = AssetManager.getAssetManager();
165             assetManager.setAssetState(asset, "D");
166             assetManager.removeAsset(asset.getId());
167         } catch (AssetManagementException e) {
168             throw new ApplicationManagementException("Error storing the asset " + e, e);
169         }
170     }
171
172     /**
173      * Returns the installed applications.
174      *
175      * @return an array containing the installed applications
176      */

177     public Application[] getApplications()
178     throws ApplicationManagementException {
179         String JavaDoc applicationsSyncTmp = null;
180         String JavaDoc applicationKeyTmp = null;
181
182         ManagementNode[] applicationNodes = null;
183
184         ManagementNode[] sources = null;
185
186         try {
187
188             applicationNodes = applicationConfigNode.getChildren();
189
190             DataStoreMetadata dsmd;
191
192             Application[] applications = new Application[applicationNodes.length];
193             for (int i = 0; i < applicationNodes.length; i++) {
194                 applications[i] = new Application((String JavaDoc) applicationNodes[i].getNodeValue("/application", "applicationURI"));
195                 applications[i].setDisplayName((String JavaDoc) applicationNodes[i].getNodeValue("/application", "applicationDisplayName"));
196                 applications[i].setAuthor((String JavaDoc) applicationNodes[i].getNodeValue("/application", "applicationAuthor"));
197                 applications[i].setDescription((String JavaDoc) applicationNodes[i].getNodeValue("/application", "applicationDescription"));
198                 applications[i].setVersion((String JavaDoc) applicationNodes[i].getNodeValue("/application", "applicationVersion"));
199                 applications[i].setAssetId((String JavaDoc) applicationNodes[i].getNodeValue("/application", "assetId"));
200                 applications[i].setSync(
201                     Boolean.valueOf((String JavaDoc) applicationNodes[i].getNodeValue("/application", "sync")).booleanValue()
202                 );
203
204                 sources = applicationNodes[i].getChildNode("spds/sources").getChildren();
205                 for (int j = 0; j < sources.length; j++) {
206                     dsmd = new DataStoreMetadata((String JavaDoc)sources[j].getValue("sourceURI"));
207                     dsmd.setDisplayName((String JavaDoc)sources[j].getValue("name"));
208                     dsmd.setDefaultSync((String JavaDoc)sources[j].getValue("sync"));
209                     dsmd.setSyncModes(StringTools.split((String JavaDoc)sources[j].getValue("syncModes")));
210
211                     applications[i].addDataStoreMetadata(dsmd);
212                 }
213             }
214
215             return applications;
216         } catch (Exception JavaDoc e) {
217             throw new ApplicationManagementException("Error loading applications parameters: " + e, e);
218         }
219     }
220
221
222     /**
223      * Creates an application object from the ADF file. An exception is thrown
224      * if any error is found and the application object cannot be created.
225      *
226      * @param adf the adf content
227      *
228      * @throws ApplicationManagementException if there is something wrong with
229      * the ADF
230      */

231     public static Application applicationFromADF(String JavaDoc adf)
232     throws ApplicationManagementException {
233         try{
234             //
235
// First the header
236
//
237
String JavaDoc header = getXMLTagValue(adf, "header");
238
239             if (StringTools.isEmpty(header)) {
240                 throw new ApplicationManagementException("Missing <header>...</header> in ADF");
241             }
242
243             //
244
// Check thet all mandatory fields are specified
245
//
246
String JavaDoc[] mandatoryFields = new String JavaDoc[] {
247                 "application-name", "application-creator-id", "application-datastore-type",
248                 "application-display-name", "application-description", "application-support-url",
249                 "application-support-email", "store-manager-package", "content-id"
250             };
251
252             String JavaDoc value;
253             for (int i=0; i<mandatoryFields.length; ++i) {
254                 value = getXMLTagValue(header, mandatoryFields[i]);
255
256                 if (StringTools.isEmpty(value)) {
257                     throw new ApplicationManagementException(
258                         "Missing <" + mandatoryFields[i] + ">...</" + mandatoryFields[i] + "> in ADF"
259                     );
260                 }
261             }
262
263             //
264
// Here header is ok, set Application properties
265
//
266
Application app = new Application(getXMLTagValue(header, "application-name"));
267
268             app.setDisplayName (getXMLTagValue(header, "application-display-name" ));
269             app.setCreatorId (getXMLTagValue(header, "application-creator-id" ));
270             app.setDataStoreType (getXMLTagValue(header, "application-datastore-type"));
271             app.setContentId (getXMLTagValue(header, "content-id" ));
272             app.setDescription (getXMLTagValue(header, "application-description" ));
273             app.setSupportUrl (getXMLTagValue(header, "application-support-url" ));
274             app.setSupportEmail (getXMLTagValue(header, "application-support-email" ));
275             app.setStoreManagerPkg (getXMLTagValue(header, "store-manager-package" ));
276             app.setAuthor (getXMLTagValue(header, "application-author" ));
277             app.setVersion (getXMLTagValue(header, "application-version" ));
278
279             //
280
// Now datastores
281
//
282
Vector JavaDoc datastores;
283             Vector JavaDoc xmlADFVector = new Vector JavaDoc();
284
285             DataStoreMetadata md;
286
287             xmlADFVector.addElement(adf);
288
289             datastores = getXMLTag(xmlADFVector, "datastore");
290
291             String JavaDoc[] syncModes;
292             String JavaDoc defaultSync;
293             int l = datastores.size();
294             for (int i=0; i < l; ++i) {
295                 md = new DataStoreMetadata(getXMLTagValue((String JavaDoc) datastores.elementAt(i) , "name"));
296
297                 md.setDisplayName(getXMLTagValue((String JavaDoc) datastores.elementAt(i), "display-name"));
298                 syncModes = getSyncModes(getXMLTagValue((String JavaDoc) datastores.elementAt(i), "sync-modes"), ALLOWED_SYNC_MODES);
299                 md.setSyncModes(syncModes);
300                 defaultSync = getXMLTagValue((String JavaDoc) datastores.elementAt(i), "default-sync");
301                 checkSyncMode(defaultSync, syncModes);
302                 md.setDefaultSync(defaultSync);
303
304                 //
305
// For backward compatibility (with conduit 1.2), if soft-sort
306
// is not specified, it is set to true
307
//
308
try {
309                     value = getXMLTagValue((String JavaDoc) datastores.elementAt(i), "soft-sort");
310                 } catch (Exception JavaDoc e) {
311                     value = "true";
312                 }
313                 md.setSoftSort(new Boolean JavaDoc(value).booleanValue());
314
315                 //
316
// For backward compatibility (with conduit 1.2), if store-volume
317
// is not specified, it is set to the empty string
318
//
319
try {
320                     value = getXMLTagValue((String JavaDoc) datastores.elementAt(i), "store-volume");
321                 } catch (Exception JavaDoc e) {
322                     value = "";
323                 }
324                 md.setStoreVolume(value);
325
326                 app.addDataStoreMetadata(md);
327             }
328
329             return app;
330
331         } catch (Throwable JavaDoc t) {
332             throw new ApplicationManagementException("Unexpected error: " + t, t);
333         }
334     }
335
336     /**
337      * Creates an application object from an asset. An exception is thrown
338      * if any error is found and the application object cannot be created.
339      *
340      * @param asset the asset from which create the application
341      *
342      * @throws ApplicationManagementException if there is something wrong with
343      * the asset
344      */

345     public static Application applicationFromAsset(Asset asset)
346     throws ApplicationManagementException {
347         Application a = new Application(asset.getName());
348
349         a.setUri (asset.getName() );
350         a.setAuthor (asset.getManufacturer() );
351         a.setAssetId (asset.getId() );
352         a.setVersion (asset.getNewVersion().getVersion());
353         a.setDescription (asset.getDescription() );
354         a.setDisplayName (asset.getName() );
355         a.setDataStoresMetadata( new Vector JavaDoc() );
356
357         return a;
358     }
359
360     /**
361      * Creates an Asset object from an Application and its package.
362      *
363      * @param app the application object
364      * @param packageFile the package file - NULL
365      *
366      * @throws ApplicationManagementException if something gets wrong
367      */

368     public static Asset makeAsset(Application app, File JavaDoc packageFile)
369     throws ApplicationManagementException {
370         try{
371             Properties JavaDoc p = new Properties JavaDoc();
372
373             //
374
// If no asset id is provided (for instance during installation),
375
// the current time millis is taken, otherwise (for instance during
376
// uninstall) the one in the Application object is taken
377
//
378
if (StringTools.isEmpty(app.getAssetId())) {
379                 p.put(Asset.PROPERTY_ID, String.valueOf(System.currentTimeMillis()));
380             } else {
381                 p.put(Asset.PROPERTY_ID, app.getAssetId());
382             }
383             p.put(Asset.PROPERTY_NAME, app.getUri());
384             p.put(Asset.PROPERTY_MANUFACTURER, app.getAuthor());
385             p.put(Asset.PROPERTY_DESCRIPTION, app.getDescription());
386             p.put(Asset.PROPERTY_STATE, "U");
387
388             p.put(
389                 AssetVersion.PROPERTY_VERSION,
390                 app.getVersion()
391             );
392             p.put(
393                 AssetVersion.PROPERTY_RELEASE_DATE,
394                 new Date JavaDoc(System.currentTimeMillis()).toString()
395             );
396             p.put(AssetVersion.PROPERTY_RELEASE_NOTES, "");
397             if (packageFile != null) {
398                 p.put(
399                     AssetVersion.PROPERTY_URL,
400                     packageFile.toURL().toString()
401                 );
402                 p.put(
403                     AssetVersion.PROPERTY_SIZE_ASSET_FILE,
404                     String.valueOf(packageFile.length())
405                 );
406             }
407             p.put(
408                 AssetVersion.PROPERTY_INSTALL_PROGRAM,
409                 "sync4j.syncclient.spap.installer.SPSInstaller.class"
410             );
411             p.put(
412                 AssetVersion.PROPERTY_UNINSTALL_PROGRAM,
413                 "sync4j.syncclient.spap.installer.SPSInstaller.class"
414             );
415             p.put(
416                 AssetVersion.PROPERTY_NEED_UNINSTALL_PREV,
417                 "true"
418             );
419
420             return new Asset(p, true);
421         } catch (Throwable JavaDoc t) {
422             throw new ApplicationManagementException("Unexpected error: " + t, t);
423         }
424     }
425
426
427     // --------------------------------------------------------- Private methods
428

429     /**
430      * Make a String[] by tags find with search.
431      *
432      * @param xmlInput tags about search
433      * @param tag to find
434      * @return find tags
435      **/

436     private static Vector JavaDoc getXMLTag(Vector JavaDoc xmlInput, String JavaDoc tag)
437         throws ApplicationManagementException {
438
439         Vector JavaDoc xmlReturn = new Vector JavaDoc();
440
441         String JavaDoc xmlInputTag = null;
442
443         String JavaDoc startTag = null;
444         String JavaDoc endTag = null;
445
446         int i = 0;
447
448         startTag = "<" + tag + ">";
449         endTag = "</" + tag + ">";
450
451         for (int j=0; j < xmlInput.size(); j++) {
452
453             xmlInputTag = (String JavaDoc) xmlInput.elementAt(j);
454
455             try {
456
457                 while (xmlInputTag.indexOf(startTag) != -1) {
458                     xmlReturn.addElement(xmlInputTag.substring(xmlInputTag.indexOf(startTag) + startTag.length(), xmlInputTag.indexOf(endTag)));
459                     xmlInputTag = xmlInputTag.substring(xmlInputTag.indexOf(endTag) + endTag.length());
460                     i++;
461                 }
462
463             } catch (StringIndexOutOfBoundsException JavaDoc e) {
464                 throw new ApplicationManagementException(
465                     "Error getting the value of <" + tag +">"
466                 );
467             }
468
469         }
470
471         return xmlReturn;
472
473     }
474
475     /**
476      * Make a String by value of <i>tag</i>.
477      *
478      * @param xml xml msg
479      * @param tag tag to find
480      * @return tag value
481      **/

482     private static String JavaDoc getXMLTagValue(String JavaDoc xml, String JavaDoc tag)
483         throws ApplicationManagementException {
484
485         String JavaDoc startTag = null;
486         String JavaDoc endTag = null;
487         String JavaDoc value = null;
488
489         startTag = "<" + tag + ">";
490         endTag = "</" + tag + ">";
491
492         try {
493             value = xml.substring(xml.indexOf(startTag) + startTag.length(), xml.indexOf(endTag));
494         } catch (StringIndexOutOfBoundsException JavaDoc e) {
495             throw new ApplicationManagementException(
496                 "Error getting the value of <" + tag +">"
497             );
498         }
499
500         value = value.trim();
501
502         while (value.indexOf("\n") != -1) {
503             value = value.substring(0, value.indexOf("\n")) + value.substring(value.indexOf("\n") + 1);
504         }
505
506         return value.trim();
507
508     }
509
510     /**
511      * Finds the Adpplication Descriptor File in the given jar file. If no
512      * ADF is found, an ApplicationManagementException is thrown.
513      *
514      * @param f the jar file
515      *
516      * @return the ADF file name
517      *
518      * @throws ApplicationMAnagementException if no adf is found.
519      */

520     private String JavaDoc getADFNameFromJar(File JavaDoc f)
521     throws ApplicationManagementException {
522         try {
523             JarFile JavaDoc jarFile = null;
524
525             Enumeration JavaDoc zipEntries = null;
526
527             ZipEntry JavaDoc zipEntry = null;
528
529             boolean findADF = false;
530
531             jarFile = new JarFile JavaDoc(f);
532
533             zipEntries = (new JarFile JavaDoc (f)).entries();
534
535             String JavaDoc adfFileName;
536             while (zipEntries.hasMoreElements()) {
537                 zipEntry = (ZipEntry JavaDoc) zipEntries.nextElement();
538
539                 adfFileName = zipEntry.getName();
540
541                 if(adfFileName.endsWith(".adf")) {
542                     return adfFileName;
543                 }
544             }
545         } catch (IOException e) {
546             throw new ApplicationManagementException(
547                 "Error reading the jar file " + f + ": " + e, e
548             );
549         }
550
551         throw new ApplicationManagementException(
552             "No Application Definition File (.adf) found in " + f
553         );
554     }
555
556     /**
557      * Reads ADF file from JAR
558      *
559      * @param f jar file
560      * @return ADF file
561      **/

562     private String JavaDoc readADFFromJar(File JavaDoc f)
563     throws ApplicationManagementException, IOException {
564
565         JarFile JavaDoc jarFile = null;
566
567         Enumeration JavaDoc zipEntries = null;
568
569         ZipEntry JavaDoc zipEntry = null;
570
571         boolean findADF = false;
572
573         jarFile = new JarFile JavaDoc(f);
574
575         zipEntries = (new JarFile JavaDoc (f)).entries();
576
577         String JavaDoc adfFileName;
578         while (zipEntries.hasMoreElements()) {
579
580             zipEntry = (ZipEntry JavaDoc) zipEntries.nextElement();
581
582             adfFileName = zipEntry.getName();
583
584             if(adfFileName.endsWith(".adf")) {
585                 findADF = true;
586                 break;
587             }
588
589         }
590
591         if (!findADF) {
592             throw new ApplicationManagementException (
593                 "No Application Description File (.adf) found in package " + f
594             );
595         }
596
597         return read (jarFile.getInputStream(zipEntry));
598
599     }
600
601    /**
602      * Reads the content of the given input stream.
603      *
604      * @param is the input stream
605      **/

606     private String JavaDoc read(InputStream is) throws IOException {
607         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
608
609         try {
610             byte[] buf = new byte[1024];
611
612             int nbyte = -1;
613             while ((nbyte = is.read(buf)) >= 0) {
614                 sb.append(new String JavaDoc(buf, 0, nbyte));
615             }
616         } finally {
617             is.close();
618         }
619
620         return sb.toString();
621     }
622
623     /**
624      * Splits the comma separated sync modes given into a correspondig string
625      * array. Each element is checked with the given allowed elements and if one of
626      * the element to check is not in the superset, an IllegalArgumentException
627      * is thrown.
628      *
629      * @param the comma separated list of sync modes
630      * @param allowed the allowed superset
631      *
632      * @return the sync modes as a string arrey
633      *
634      * @throws IllegalArgumentException in case of one of the given value is not
635      * listed in <i>ALLOWED_SYNC_MODES</i>
636      */

637     private static String JavaDoc[] getSyncModes(String JavaDoc s, String JavaDoc[] allowed)
638     throws IllegalArgumentException JavaDoc {
639         String JavaDoc[] modes = StringTools.split(s);
640
641         for (int i=0; i<modes.length; ++i) {
642             checkSyncMode(modes[i], allowed);
643         }
644
645         return modes;
646     }
647
648     /**
649      * Checks if the given mode is one of the allowed ones.
650      *
651      * @param mode the mode to check
652      * @param allowed the allowed superset
653      *
654      * @throws IllegalArgumentException if mode is not in allowed[]
655      */

656     private static void checkSyncMode(String JavaDoc mode, String JavaDoc[] allowed) {
657         for(int i=0; ((allowed != null) && (i<allowed.length)); ++i) {
658             if (allowed[i].equals(mode)) {
659                 return;
660             }
661         }
662
663         throw new IllegalArgumentException JavaDoc(
664             "Invalid sync mode '" +
665             mode +
666             "'; it must be one of (" +
667             StringTools.join(allowed) +
668             ")"
669         );
670     }
671
672 }
Popular Tags