|
1 /* |
|
2 * Copyright (c) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 package com.nokia.mj.impl.installer.jadjarmatcher; |
|
20 |
|
21 import com.nokia.mj.impl.fileutils.FileUtility; |
|
22 import com.nokia.mj.impl.installer.integrityservice.IntegrityService; |
|
23 import com.nokia.mj.impl.installer.utils.FileUtils; |
|
24 import com.nokia.mj.impl.installer.utils.InstallerException; |
|
25 import com.nokia.mj.impl.installer.utils.JadReader; |
|
26 import com.nokia.mj.impl.installer.utils.Log; |
|
27 import com.nokia.mj.impl.utils.Attribute; |
|
28 import com.nokia.mj.impl.utils.InstallerDetailedErrorMessage; |
|
29 import com.nokia.mj.impl.utils.InstallerErrorMessage; |
|
30 import com.nokia.mj.impl.utils.JarManifestReader; |
|
31 import com.nokia.mj.impl.utils.OtaStatusCode; |
|
32 |
|
33 import java.io.IOException; |
|
34 import java.util.Hashtable; |
|
35 import java.util.Vector; |
|
36 |
|
37 /** |
|
38 * JadJarMatcher offers services for finding Jar when Jad filename is known, |
|
39 * and finding Jad when Jar filename is known. JadJarMatcher only searches |
|
40 * files from local folders. |
|
41 * |
|
42 * @author Nokia Corporation |
|
43 * @version $Rev: 9457 $ |
|
44 */ |
|
45 public class JadJarMatcherBase |
|
46 { |
|
47 /** IntegrityService instance to be used when matching. */ |
|
48 static IntegrityService iIntegrityService = null; |
|
49 |
|
50 /*** ----------------------------- PUBLIC ------------------------------ */ |
|
51 |
|
52 /** |
|
53 * Set IntegrityService instance to be used when matching. |
|
54 */ |
|
55 public static void setIntegrityService(IntegrityService aIs) |
|
56 { |
|
57 iIntegrityService = aIs; |
|
58 } |
|
59 |
|
60 /** |
|
61 * Searches for Jad file matching to given Jar file. |
|
62 * |
|
63 * @param aJar Jar file name. |
|
64 * @return JadJarFile containing matching file names. |
|
65 * @throws InstallerException if error occurs. |
|
66 */ |
|
67 public static JadJarFile findJad(String aJar) |
|
68 { |
|
69 Log.log("JadJarMatcherBase: finding Jad for " + aJar); |
|
70 JadJarFile result = null; |
|
71 if (aJar != null) |
|
72 { |
|
73 result = getJarAttributes(aJar); |
|
74 if (result.iJadFilename != null) |
|
75 { |
|
76 // Jad file was found from inside midlet message package, |
|
77 // no need to find it. |
|
78 Log.log("JadJarMatcherBase: Found jad from midlet message"); |
|
79 return result; |
|
80 } |
|
81 try |
|
82 { |
|
83 findJad(result, listFiles(aJar, new String[] { ".jad" })); |
|
84 } |
|
85 catch (IOException ioe) |
|
86 { |
|
87 InstallerException.internalError |
|
88 ("Finding Jad failed for " + aJar, ioe); |
|
89 } |
|
90 if (result.iJadFilename == null) |
|
91 { |
|
92 // Jad was not found from the same directory where Jar is. |
|
93 Log.log("JadJarMatcherBase: Jad not found from the same folder"); |
|
94 } |
|
95 } |
|
96 return result; |
|
97 } |
|
98 |
|
99 /** |
|
100 * Searches for Jar file matching to given Jad file. |
|
101 * |
|
102 * @param aJad Jad file name. |
|
103 * @param aJadCharset Character encoding used in Jad file. |
|
104 * @return JadJarFile containing matching file names. |
|
105 * @throws InstallerException if error occurs. |
|
106 */ |
|
107 public static JadJarFile findJar(String aJad, String aJadCharset) |
|
108 { |
|
109 Log.log("JadJarMatcherBase: finding Jar for " + aJad); |
|
110 JadJarFile result = null; |
|
111 if (aJad != null) |
|
112 { |
|
113 result = getJadAttributes(aJad, aJadCharset); |
|
114 try |
|
115 { |
|
116 findJar(result, listFiles(aJad, new String[] { ".jar", ".dcf", ".dm" })); |
|
117 } |
|
118 catch (IOException ioe) |
|
119 { |
|
120 InstallerException.internalError |
|
121 ("Finding Jar failed for " + aJad, ioe); |
|
122 } |
|
123 if (result.iJarFilename == null) |
|
124 { |
|
125 // Jar was not found from the same directory where Jad is. |
|
126 Log.log("JadJarMatcherBase: Jar not found from the same folder"); |
|
127 } |
|
128 } |
|
129 return result; |
|
130 } |
|
131 |
|
132 /*** ---------------------------- PROTECTED --------------------------- */ |
|
133 |
|
134 /** |
|
135 * Returns IntegrityService instance to be used when matching. |
|
136 */ |
|
137 protected static IntegrityService getIntegrityService() |
|
138 { |
|
139 return iIntegrityService; |
|
140 } |
|
141 |
|
142 /** |
|
143 * Reads jad attributes from given file using given character encoding. |
|
144 * |
|
145 * @param aJad Jad file name. |
|
146 * @param aJadCharset Character encoding used in Jad file. |
|
147 * @return JadJarFile Jad file name and its attributes. |
|
148 * @throws InstallerException if error occurs. |
|
149 */ |
|
150 protected static JadJarFile getJadAttributes( |
|
151 String aJad, String aJadCharset) |
|
152 { |
|
153 JadJarFile result = new JadJarFile(); |
|
154 try |
|
155 { |
|
156 result.iJadFilename = aJad; |
|
157 result.iJadAttributes = JadReader.getAttributes(aJad, aJadCharset); |
|
158 if (result.iJadAttributes == null) |
|
159 { |
|
160 throw new IOException("No Jad attributes found"); |
|
161 } |
|
162 } |
|
163 catch (IOException ioe) |
|
164 { |
|
165 Log.logError("Exception while getting jad attributes: " + ioe); |
|
166 throw new InstallerException |
|
167 (InstallerErrorMessage.INST_CORRUPT_PKG, null, |
|
168 InstallerDetailedErrorMessage.INTERNAL_ERROR, |
|
169 new String[] { "Reading Jad failed: " + aJad }, |
|
170 OtaStatusCode.INVALID_DESCRIPTOR, ioe); |
|
171 } |
|
172 return result; |
|
173 } |
|
174 |
|
175 /** |
|
176 * Reads jar attributes from given aJar file. Also checks if the |
|
177 * attributes indicate that this jar file is a midlet message package, |
|
178 * and if that's the case then extracts jad and jar files and |
|
179 * initializes the returned JadJarFile accordingly. |
|
180 * |
|
181 * @param aJar Jar file name. |
|
182 * @return JadJarFile Jar file name. |
|
183 * @throws InstallerException if error occurs. |
|
184 */ |
|
185 protected static JadJarFile getJarAttributes(String aJar) |
|
186 { |
|
187 JadJarFile result = new JadJarFile(); |
|
188 try |
|
189 { |
|
190 result.iJarFilename = aJar; |
|
191 result.iJarAttributes = JarManifestReader.getAttributes(aJar); |
|
192 if (result.iJarAttributes == null) |
|
193 { |
|
194 throw new IOException("No Manifest attributes found"); |
|
195 } |
|
196 } |
|
197 catch (IOException ioe) |
|
198 { |
|
199 Log.logError("Exception while getting jar attributes: " + ioe); |
|
200 throw new InstallerException |
|
201 (InstallerErrorMessage.INST_CORRUPT_PKG, null, |
|
202 InstallerDetailedErrorMessage.INTERNAL_ERROR, |
|
203 new String[] { "Reading Manifest failed: " + aJar }, |
|
204 OtaStatusCode.INVALID_JAR, ioe); |
|
205 } |
|
206 // Check if jar file is a midlet message package. |
|
207 if (MidletMessageHandler.checkMidletMessage(result)) |
|
208 { |
|
209 MidletMessageHandler.handleMidletMessage(aJar, result); |
|
210 } |
|
211 return result; |
|
212 } |
|
213 |
|
214 /** |
|
215 * Searches given aFiles for a match to Jar specified in aJadJarFile. |
|
216 * If a match is found, updates Jad details in aJadJarFile object. |
|
217 */ |
|
218 protected static void findJad(JadJarFile aJadJarFile, FileList aFiles) |
|
219 throws IOException |
|
220 { |
|
221 MatchData jarMatch = |
|
222 MatchData.getMatchData(aJadJarFile.iJarAttributes); |
|
223 Log.log("JadJarMatcherBase: matching " + aJadJarFile.iJarFilename + |
|
224 " (" + jarMatch + ")"); |
|
225 if (aFiles.iFilenames.length > 1) |
|
226 { |
|
227 // Sort files so that the file which has the same |
|
228 // basename as the jar file will be matched first. |
|
229 aFiles.sortByBasename(aJadJarFile.iJarFilename); |
|
230 } |
|
231 Log.log("JadJarMatcherBase: Found " + aFiles.iFilenames.length + |
|
232 " files:\n" + aFiles.toString()); |
|
233 for (int i = 0; i < aFiles.iFilenames.length; i++) |
|
234 { |
|
235 aJadJarFile.iJadAttributes = |
|
236 matchJad(jarMatch, aFiles.iFilenames[i]); |
|
237 if (aJadJarFile.iJadAttributes != null) |
|
238 { |
|
239 aJadJarFile.iJadFilename = aFiles.iFilenames[i]; |
|
240 break; |
|
241 } |
|
242 } |
|
243 } |
|
244 |
|
245 /** |
|
246 * Searches given aFiles for a match to Jad specified in aJadJarFile. |
|
247 * If a match is found, updates Jar details in aJadJarFile object. |
|
248 */ |
|
249 protected static void findJar(JadJarFile aJadJarFile, FileList aFiles) |
|
250 throws IOException |
|
251 { |
|
252 MatchData jadMatch = |
|
253 MatchData.getMatchData(aJadJarFile.iJadAttributes); |
|
254 Log.log("JadJarMatcherBase: matching " + aJadJarFile.iJadFilename + |
|
255 " (" + jadMatch + ")"); |
|
256 if (aFiles.iFilenames.length > 1) |
|
257 { |
|
258 // Sort files so that the file which has the same |
|
259 // basename as the jad file will be matched first. |
|
260 aFiles.sortByBasename(aJadJarFile.iJadFilename); |
|
261 // Sort files so that the file specified in |
|
262 // MIdlet-Jar-URL attribute will be matched first. |
|
263 String jarUrl = getMidletJarUrl(aJadJarFile); |
|
264 if (jarUrl != null) |
|
265 { |
|
266 aFiles.sortByName(jarUrl, 0); |
|
267 } |
|
268 } |
|
269 Log.log("JadJarMatcherBase: Found " + aFiles.iFilenames.length + |
|
270 " files:\n" + aFiles.toString()); |
|
271 for (int i = 0; i < aFiles.iFilenames.length; i++) |
|
272 { |
|
273 aJadJarFile.iJarAttributes = |
|
274 matchJar(jadMatch, aFiles.iFilenames[i]); |
|
275 if (aJadJarFile.iJarAttributes != null) |
|
276 { |
|
277 aJadJarFile.iJarFilename = aFiles.iFilenames[i]; |
|
278 break; |
|
279 } |
|
280 } |
|
281 } |
|
282 |
|
283 /** |
|
284 * Returns a Hashtable of attributes read from Jar specified by |
|
285 * given aFilename if attributes in Jar match to given aJarMatch |
|
286 * data. If data does not match, this method returns null. |
|
287 */ |
|
288 protected static Hashtable matchJad(MatchData aJarMatch, String aFilename) |
|
289 { |
|
290 Hashtable attrs = null; |
|
291 try |
|
292 { |
|
293 attrs = JadReader.getAttributes(aFilename); |
|
294 } |
|
295 catch (IOException ioe) |
|
296 { |
|
297 // Not a Jad file ==> no match. |
|
298 Log.log("JadJarMatcherBase: " + ioe.toString()); |
|
299 } |
|
300 MatchData jadMatch = null; |
|
301 if (attrs != null) |
|
302 { |
|
303 jadMatch = MatchData.getMatchData(attrs); |
|
304 if (jadMatch.equals(aJarMatch)) |
|
305 { |
|
306 Log.log("JadJarMatcherBase: match " + aFilename); |
|
307 return attrs; |
|
308 } |
|
309 } |
|
310 Log.log("JadJarMatcherBase: no match " + aFilename + |
|
311 " (" + jadMatch + ")"); |
|
312 return null; |
|
313 } |
|
314 |
|
315 /** |
|
316 * Returns a Hashtable of attributes read from Jad specified by |
|
317 * given aFilename if attributes in Jad match to given aJadMatch |
|
318 * data. If data does not match, this method returns null. |
|
319 */ |
|
320 protected static Hashtable matchJar(MatchData aJadMatch, String aFilename) |
|
321 { |
|
322 Hashtable attrs = null; |
|
323 try |
|
324 { |
|
325 attrs = JarManifestReader.getAttributes(aFilename); |
|
326 } |
|
327 catch (IOException ioe) |
|
328 { |
|
329 // Not a Jar file ==> no match. |
|
330 Log.log("JadJarMatcherBase: " + ioe.toString()); |
|
331 } |
|
332 MatchData jarMatch = null; |
|
333 if (attrs != null) |
|
334 { |
|
335 jarMatch = MatchData.getMatchData(attrs); |
|
336 if (jarMatch.equals(aJadMatch)) |
|
337 { |
|
338 Log.log("JadJarMatcherBase: match " + aFilename); |
|
339 return attrs; |
|
340 } |
|
341 } |
|
342 Log.log("JadJarMatcherBase: no match " + aFilename + |
|
343 " (" + jarMatch + ")"); |
|
344 return null; |
|
345 } |
|
346 |
|
347 /** |
|
348 * Returns the value of MIDlet-Jar-URL attribute from given JadJarFile |
|
349 * object or null if the attribute does not exist. |
|
350 */ |
|
351 protected static String getMidletJarUrl(JadJarFile aJadJarFile) |
|
352 { |
|
353 if (aJadJarFile == null || aJadJarFile.iJadAttributes == null) |
|
354 { |
|
355 return null; |
|
356 } |
|
357 return getAttributeValue(aJadJarFile.iJadAttributes, "MIDlet-Jar-URL"); |
|
358 } |
|
359 |
|
360 /*** ----------------------------- PACKAGE ---------------------------- */ |
|
361 /*** ----------------------------- PRIVATE ---------------------------- */ |
|
362 |
|
363 /** |
|
364 * Returns an array of file names with specified extensions |
|
365 * from the same directory where given file exists. |
|
366 * Extension comparison is case insensitive. |
|
367 * |
|
368 * @param aFile files will be returned from the directory where |
|
369 * this file is located |
|
370 * @param aExts allowed extensions for returned files |
|
371 * @return FileList object containing arrays of file realted info |
|
372 * @returns IOException if an I/O error occurs |
|
373 */ |
|
374 private static FileList listFiles(String aFile, String[] aExts) throws IOException |
|
375 { |
|
376 String name = FileUtils.getName(aFile); |
|
377 String path = aFile.substring(0, aFile.length()-name.length()); |
|
378 if (path == null || path.length() == 0) |
|
379 { |
|
380 path = "."; |
|
381 //} else { |
|
382 // Else branch is not needed when FileUtility is used. |
|
383 // Add "." to make sure that CDC File understands that |
|
384 // path is directory. |
|
385 //path = path + "."; |
|
386 } |
|
387 for (int i = 0; i < aExts.length; i++) |
|
388 { |
|
389 aExts[i] = (aExts[i] == null? "": aExts[i].toLowerCase()); |
|
390 } |
|
391 FileUtility[] files = new FileUtility(path).listFiles(); |
|
392 Vector filenames = new Vector(); |
|
393 Vector fileTimestamps = new Vector(); |
|
394 for (int i = 0; i < files.length; i++) |
|
395 { |
|
396 // Leave out directories and files with incorrect extension |
|
397 // from the returned file list. |
|
398 String filename = FileUtility.getCanonicalPath( |
|
399 files[i].getAbsolutePath()); |
|
400 if (files[i].isFile() && |
|
401 extMatches(filename.toLowerCase(), aExts)) |
|
402 { |
|
403 filenames.addElement(filename); |
|
404 fileTimestamps.addElement(new Long(files[i].lastModified())); |
|
405 } |
|
406 } |
|
407 String[] resultFilenames = new String[filenames.size()]; |
|
408 long[] resultTimestamps = new long[fileTimestamps.size()]; |
|
409 for (int i = 0; i < filenames.size(); i++) |
|
410 { |
|
411 resultFilenames[i] = (String)filenames.elementAt(i); |
|
412 resultTimestamps[i] = ((Long)fileTimestamps.elementAt(i)).longValue(); |
|
413 } |
|
414 // Construct a FileList object to be returned and sort it by time |
|
415 // distance from the file where listing was started from. |
|
416 FileList fileList = new FileList(resultFilenames, null, resultTimestamps); |
|
417 fileList.initTimeDistances((new FileUtility(aFile)).lastModified()); |
|
418 fileList.sortByTimeDistance(); |
|
419 return fileList; |
|
420 } |
|
421 |
|
422 /** |
|
423 * Returns true if given file has one of the specified extensions, |
|
424 * false otherwise. Comparison is case sensitive. |
|
425 */ |
|
426 private static boolean extMatches(String aFile, String[] aExts) |
|
427 { |
|
428 boolean result = false; |
|
429 for (int i = 0; i < aExts.length; i++) |
|
430 { |
|
431 if (aFile.endsWith(aExts[i])) |
|
432 { |
|
433 result = true; |
|
434 break; |
|
435 } |
|
436 } |
|
437 return result; |
|
438 } |
|
439 |
|
440 /** |
|
441 * Returns requested attribute value from given Hashtable, |
|
442 * or null if attribute does not exist. |
|
443 */ |
|
444 private static String getAttributeValue( |
|
445 Hashtable aAttributes, String aName) |
|
446 { |
|
447 Attribute attr = (Attribute)aAttributes.get(aName); |
|
448 if (attr != null && attr.getValue() != null) |
|
449 { |
|
450 return attr.getValue().trim(); |
|
451 } |
|
452 return null; |
|
453 } |
|
454 |
|
455 protected static class MatchData |
|
456 { |
|
457 private String iName = null; |
|
458 private String iVendor = null; |
|
459 private String iVersion = null; |
|
460 |
|
461 private MatchData() |
|
462 { |
|
463 } |
|
464 |
|
465 private MatchData(String aName, String aVendor, String aVersion) |
|
466 { |
|
467 iName = aName; |
|
468 iVendor = aVendor; |
|
469 iVersion = aVersion; |
|
470 } |
|
471 |
|
472 public static MatchData getMatchData(Hashtable aAttributes) |
|
473 { |
|
474 return new MatchData( |
|
475 getAttributeValue(aAttributes, "MIDlet-Name"), |
|
476 getAttributeValue(aAttributes, "MIDlet-Vendor"), |
|
477 getAttributeValue(aAttributes, "MIDlet-Version")); |
|
478 } |
|
479 |
|
480 public int hashCode() |
|
481 { |
|
482 int result = 0; |
|
483 if (iName != null) |
|
484 { |
|
485 result += iName.hashCode(); |
|
486 } |
|
487 if (iVendor != null) |
|
488 { |
|
489 result += iVendor.hashCode(); |
|
490 } |
|
491 if (iVersion != null) |
|
492 { |
|
493 result += iVersion.hashCode(); |
|
494 } |
|
495 return result; |
|
496 } |
|
497 |
|
498 public boolean equals(Object aObj) |
|
499 { |
|
500 if (!(aObj instanceof MatchData)) |
|
501 { |
|
502 return false; |
|
503 } |
|
504 MatchData md = (MatchData)aObj; |
|
505 if (equals(this.iName, md.iName) && |
|
506 equals(this.iVendor, md.iVendor) && |
|
507 equals(this.iVersion, md.iVersion)) |
|
508 { |
|
509 return true; |
|
510 } |
|
511 return false; |
|
512 } |
|
513 |
|
514 public String toString() |
|
515 { |
|
516 StringBuffer buf = new StringBuffer(); |
|
517 buf.append("Name: " + iName); |
|
518 buf.append(", Vendor: " + iVendor); |
|
519 buf.append(", Version: " + iVersion); |
|
520 return buf.toString(); |
|
521 } |
|
522 |
|
523 private static boolean equals(String aStr1, String aStr2) |
|
524 { |
|
525 if ((aStr1 == null && aStr2 == null) || |
|
526 (aStr1 != null && aStr1.equals(aStr2))) |
|
527 { |
|
528 return true; |
|
529 } |
|
530 return false; |
|
531 } |
|
532 } |
|
533 |
|
534 /*** ----------------------------- NATIVE ----------------------------- */ |
|
535 } |