001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.midp.content;
028:
029: import java.util.Vector;
030: import java.util.Enumeration;
031:
032: import javax.microedition.content.ContentHandlerException;
033: import javax.microedition.content.ActionNameMap;
034:
035: import com.sun.midp.installer.InvalidJadException;
036:
037: /**
038: * Support for parsing attributes and installing from the
039: * manifest or application descriptors.
040: */
041: final class RegistryInstaller {
042: /** Attribute prefix for ContentHandler attributes. */
043: private static final String CH_PREFIX = "MicroEdition-Handler-";
044:
045: /** Attribute suffix for ContentHandler ID attribute. */
046: private static final String CH_ID_SUFFIX = "-ID";
047:
048: /** Attribute suffix for ContentHandler visibility attribute. */
049: private static final String CH_ACCESS_SUFFIX = "-Access";
050:
051: /** Parced handlers to be installed. */
052: private Vector instHandlers;
053:
054: /** Old handlers to be removed. */
055: private Vector remHandlers;
056:
057: /**
058: * Parse the ContentHandler attributes and check for errors.
059: * <ul>
060: * <li> Parse attributes into set of ContentHandlers.
061: * <li> If none, return
062: * <li> Check for permission to install handlers
063: * <li> Check each for simple invalid arguments
064: * <li> Check each for MIDlet is registered
065: * <li> Check each for conflicts with other application registrations
066: * <li> Find any current registrations
067: * <li> Remove current dynamic registrations from set to be removed
068: * <li> Check and resolve any conflicts between static and curr dynamic
069: * </ul>
070: * @param appl the AppProxy context with one or more applications
071: * @return number of handlers prepared for installation.
072: * @exception IllegalArgumentException if there is no classname field,
073: * or if there are more than five comma separated fields on the line.
074: * @exception NullPointerException if missing components
075: * @exception ContentHandlerException if handlers are ambiguous
076: * @exception ClassNotFoundException if an application class cannot be found
077: * @exception SecurityException if not allowed to register
078: */
079: int preInstall(AppProxy appl) throws ContentHandlerException,
080: ClassNotFoundException {
081: int i, j, sz;
082: int suiteId = appl.getStorageId();
083: ContentHandlerImpl[] chs;
084:
085: /*
086: * Check for any CHAPI attributes;
087: * if so, then the MIDlet suite must have permission.
088: */
089: remHandlers = new Vector();
090: instHandlers = parseAttributes(appl);
091:
092: /*
093: * Remove all static registrations.
094: */
095: chs = RegistryStore.forSuite(suiteId);
096: sz = (chs == null ? 0 : chs.length);
097: for (i = 0; i < sz; i++) {
098: if (chs[i] == null)
099: continue;
100: if (chs[i].registrationMethod != ContentHandlerImpl.REGISTERED_STATIC) {
101: // Verify dynamic handler.
102: try {
103: // is it a valid application?
104: appl.verifyApplication(chs[i].classname);
105: // is there new handler to replace this one?
106: for (j = 0; j < instHandlers.size(); j++) {
107: ContentHandlerImpl handler = (ContentHandlerImpl) instHandlers
108: .elementAt(j);
109: if (handler.classname.equals(chs[i].classname)) {
110: throw new Throwable(
111: "Replace dynamic handler");
112: }
113: }
114: // The handler remains.
115: continue;
116: } catch (Throwable t) {
117: // Pass down to remove handler
118: }
119: }
120:
121: // Remove handler -- either [static] or [replaced] or [invalid]
122: remHandlers.addElement(chs[i]);
123: chs[i] = null;
124: }
125:
126: /* Verify new registrations */
127: for (i = 0; i < instHandlers.size(); i++) {
128: ContentHandlerImpl handler = (ContentHandlerImpl) instHandlers
129: .elementAt(i);
130:
131: // Verify ID ...
132: // ... look through Registry
133: ContentHandlerImpl[] conf = RegistryStore
134: .findConflicted(handler.ID);
135: if (conf != null) {
136: for (j = 0; j < conf.length; j++) {
137: if (conf[j].storageId != suiteId
138: || !willRemove(conf[j].ID))
139: throw new ContentHandlerException(
140: "Content Handler ID: " + handler.ID,
141: ContentHandlerException.AMBIGUOUS);
142: }
143: }
144:
145: // ... look through newbies
146: j = i;
147: while (j-- > 0) {
148: ContentHandlerImpl other = (ContentHandlerImpl) instHandlers
149: .elementAt(j);
150: if (handler.ID.startsWith(other.ID)
151: || other.ID.startsWith(handler.ID)) {
152: throw new ContentHandlerException(
153: "Content Handler ID: " + handler.ID,
154: ContentHandlerException.AMBIGUOUS);
155: }
156: }
157:
158: // Check permissions for each new handler
159: appl.checkRegisterPermission("register");
160: }
161:
162: return instHandlers.size();
163: }
164:
165: private boolean willRemove(String ID) {
166: Enumeration en = remHandlers.elements();
167: while (en.hasMoreElements()) {
168: ContentHandlerImpl handler = (ContentHandlerImpl) en
169: .nextElement();
170: if (handler.ID.equals(ID))
171: return true;
172: }
173: return false;
174: }
175:
176: /**
177: * Parse the ContentHandler attributes and check for errors.
178: *
179: * @param appl the AppProxy context with one or more applications
180: *
181: * @return a Vector of the ContentHandlers parsed from the attributes
182: *
183: * @exception IllegalArgumentException if there is no classname field,
184: * or if there are more than five comma separated fields on the line.
185: * @exception NullPointerException if missing components
186: * @exception ContentHandlerException if there are conflicts between
187: * content handlers
188: * @exception ClassNotFoundException if an application class cannot be found
189: */
190: private static Vector parseAttributes(AppProxy appl)
191: throws ContentHandlerException, ClassNotFoundException {
192: Vector handlers = new Vector();
193: for (int index = 1;; index++) {
194: String sindex = Integer.toString(index);
195: String handler_n = CH_PREFIX.concat(sindex);
196: String value = appl.getProperty(handler_n);
197: if (value == null) {
198: break;
199: }
200: String[] types = null;
201: String[] suffixes = null;
202: String[] actions = null;
203: String[] locales = null;
204: String classname;
205: String[] fields = split(value, ',');
206:
207: switch (fields.length) {
208: case 5: // Has locales
209: locales = split(fields[4], ' ');
210: // Fall through
211: case 4: // Has actions
212: actions = split(fields[3], ' ');
213: // Fall through
214: case 3: // Has suffixes
215: suffixes = split(fields[2], ' ');
216: // Fall through
217: case 2: // Has types
218: // Parse out the types (if any)
219: types = split(fields[1], ' ');
220: // Fall through
221: case 1: // Has classname
222: classname = fields[0];
223: if (classname != null && classname.length() > 0) {
224: // Has non-empty classname
225: break;
226: }
227: // No classname, fall through to throw exception
228: case 0: // no nothing; error
229: default: // too many fields, error
230: throw new IllegalArgumentException(
231: "Too many or too few fields");
232: }
233:
234: // Get the application info for this new class;
235: // Throws ClassNotFoundException or IllegalArgumentException
236: AppProxy newAppl = appl.forClass(classname);
237:
238: ActionNameMap[] actionnames = parseActionNames(actions,
239: locales, handler_n, newAppl);
240:
241: // Parse the ID if any and the Access attribute
242: String idAttr = handler_n.concat(CH_ID_SUFFIX);
243: String id = newAppl.getProperty(idAttr);
244: String visAttr = handler_n.concat(CH_ACCESS_SUFFIX);
245: String visValue = newAppl.getProperty(visAttr);
246: String[] accessRestricted = split(visValue, ' ');
247:
248: // Default the ID if not supplied
249: if (id == null) {
250: // Generate a unique ID based on the MIDlet suite
251: id = newAppl.getApplicationID();
252: }
253:
254: // Now create the handler
255: ContentHandlerImpl handler = new ContentHandlerImpl(types,
256: suffixes, actions, actionnames, id,
257: accessRestricted, newAppl.getAuthority());
258:
259: // Fill in the non-public fields
260: handler.classname = classname;
261: handler.storageId = newAppl.getStorageId();
262: handler.appname = newAppl.getApplicationName();
263: handler.version = newAppl.getVersion();
264:
265: /* Check new registration does not conflict with others. */
266: for (int i = 0; i < handlers.size(); i++) {
267: ContentHandlerImpl curr = (ContentHandlerImpl) handlers
268: .elementAt(i);
269: if (curr.classname.equals(handler.classname)) {
270: handlers.insertElementAt(handler, i);
271: handler = null;
272: break;
273: }
274: }
275: if (handler != null) { // not yet inserted
276: handlers.addElement(handler);
277: }
278: }
279: return handlers;
280: }
281:
282: /**
283: * Scan the available properties for the locale specific
284: * attribute names and parse and The actionname maps for
285: * each.
286: * @param actions the actions parsed for the handler
287: * @param locales the list of locales to check for action names
288: * @param prefix the prefix of the current handler attribute name
289: * @param appl the AppProxy context with one or more applications
290: * @return an array of ActionNameMap's
291: * @exception IllegalArgumentException if locale is missing
292: */
293: private static ActionNameMap[] parseActionNames(String[] actions,
294: String[] locales, String prefix, AppProxy appl) {
295: if (locales == null || locales.length == 0) {
296: return null;
297: }
298: prefix = prefix.concat("-");
299: Vector maps = new Vector();
300: for (int i = 0; i < locales.length; i++) {
301: String localeAttr = prefix.concat(locales[i]);
302: String localeValue = appl.getProperty(localeAttr);
303: if (localeValue == null) {
304: throw new IllegalArgumentException("missing locale");
305: }
306: String[] actionnames = split(localeValue, ',');
307: ActionNameMap map = new ActionNameMap(actions, actionnames,
308: locales[i]);
309: maps.addElement(map);
310: }
311: if (maps.size() > 0) {
312: ActionNameMap[] result = new ActionNameMap[maps.size()];
313: maps.copyInto(result);
314: return result;
315: } else {
316: return null;
317: }
318: }
319:
320: /**
321: * Split the values in a field by delimiter and return a string array.
322: * @param string the string containing the values
323: * @param delim the delimiter that separates the values
324: * @return a String array of the values; must be null
325: */
326: static String[] split(String string, char delim) {
327: String[] ret = ContentHandlerImpl.ZERO_STRINGS;
328: if (string != null) {
329: Vector values = getDelimSeparatedValues(string, delim);
330: ret = new String[values.size()];
331: values.copyInto(ret);
332: }
333: return ret;
334: }
335:
336: /**
337: * Create a vector of values from a string containing delimiter separated
338: * values. The values cannot contain the delimiter. The output values will
339: * be trimmed of whitespace. The vector may contain zero length strings
340: * where there are 2 delimiters in a row or a comma at the end of the input
341: * string.
342: *
343: * @param input input string of delimiter separated values
344: * @param delim the delimiter separating values
345: * @return vector of string values.
346: */
347: private static Vector getDelimSeparatedValues(String input,
348: char delim) {
349: Vector output = new Vector(5, 5);
350: int len;
351: int start;
352: int end;
353:
354: input = input.trim();
355: len = input.length();
356: if (len == 0) {
357: return output;
358: }
359:
360: for (start = end = 0; end < len;) {
361: // Skip leading spaces and control chars
362: while (start < len && (input.charAt(start) <= ' ')) {
363: start += 1;
364: }
365:
366: // Scan for end delimiter (tab also if delim is space)
367: for (end = start; end < len; end++) {
368: char c = input.charAt(end);
369: if (c == delim || (c == '\t' && delim == ' ')) {
370: output.addElement(input.substring(start, end)
371: .trim());
372: start = end + 1;
373: break;
374: }
375: }
376: }
377:
378: end = len;
379: output.addElement(input.substring(start, end).trim());
380:
381: return output;
382: }
383:
384: /**
385: * Performs static installation (registration) the application
386: * to handle the specified type and to provide a set of actions.
387: *
388: * @exception InvalidJadException if there is a content handlers
389: * IDs conflict
390: */
391: void install() {
392: int i, sz;
393:
394: // Remove static and conflicted handlers.
395: sz = (remHandlers == null ? 0 : remHandlers.size());
396: for (i = 0; i < sz; i++) {
397: ContentHandlerImpl handler = (ContentHandlerImpl) remHandlers
398: .elementAt(i);
399: RegistryStore.unregister(handler.getID());
400: }
401:
402: // Install new handlers.
403: sz = (instHandlers == null ? 0 : instHandlers.size());
404: for (i = 0; i < sz; i++) {
405: ContentHandlerImpl handler = (ContentHandlerImpl) instHandlers
406: .elementAt(i);
407: RegistryStore.register(handler);
408: if (AppProxy.LOG_INFO) {
409: AppProxy.getCurrent().logInfo(
410: "Register: " + handler.classname + ", id: "
411: + handler.getID());
412: }
413: }
414: }
415:
416: /**
417: * Performs static uninstallation (unregistration) of the application.
418: *
419: * @param suiteId suite ID to be unregistered
420: * @param update flag indicated whether the given suite is about remove or
421: * update
422: */
423: static void uninstallAll(int suiteId, boolean update) {
424: ContentHandlerImpl[] chs = RegistryStore.forSuite(suiteId);
425: for (int i = 0; i < chs.length; i++) {
426: if (!update
427: || chs[i].registrationMethod == ContentHandlerImpl.REGISTERED_STATIC) {
428: RegistryStore.unregister(chs[i].getID());
429: }
430: }
431: }
432:
433: }
|