/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.felix.mosgi.jmx.agent.mx4j.loading; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.List; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import javax.management.loading.MLet; /** * The parser for MLet files, as specified in the JMX documentation. * This parser is case insensitive regards to the MLet tags: MLET is equal to mlet and to MLet. * This parser also supports XML-style comments in the file. * * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a> * @version $Revision: 1.1.1.1 $ */ public class MLetParser { public static final String OPEN_COMMENT = "<!--"; public static final String CLOSE_COMMENT = "-->"; public static final String OPEN_BRACKET = "<"; public static final String CLOSE_BRACKET = ">"; public static final String MLET_TAG = "MLET"; public static final String CODE_ATTR = "CODE"; public static final String OBJECT_ATTR = "OBJECT"; public static final String ARCHIVE_ATTR = "ARCHIVE"; public static final String CODEBASE_ATTR = "CODEBASE"; public static final String NAME_ATTR = "NAME"; public static final String VERSION_ATTR = "VERSION"; public static final String ARG_TAG = "ARG"; public static final String TYPE_ATTR = "TYPE"; public static final String VALUE_ATTR = "VALUE"; private MLet mlet; /** * Creates a new MLetParser */ public MLetParser() { } /** * Creates a new MLetParser * @param mlet The MLet used to resolve classes specified in the ARG tags. */ public MLetParser(MLet mlet) { this.mlet = mlet; } /** * Parses the given content, that must contains a valid MLet file. * * @param content The content to parse * @return A list of {@link MLetTag}s * @throws MLetParseException If the content is not a valid MLet file */ public List parse(String content) throws MLetParseException { if (content == null) throw new MLetParseException("MLet file content cannot be null"); // Strip comments content = stripComments(content.trim()); content = convertToUpperCase(content); ArrayList mlets = parseMLets(content); if (mlets.size() < 1) throw new MLetParseException("MLet file is empty"); ArrayList mletTags = new ArrayList(); for (int i = 0; i < mlets.size(); ++i) { String mletTag = (String)mlets.get(i); MLetTag tag = parseMLet(mletTag); mletTags.add(tag); } return mletTags; } private MLetTag parseMLet(String content) throws MLetParseException { MLetTag tag = new MLetTag(); parseMLetAttributes(tag, content); parseMLetArguments(tag, content); return tag; } private ArrayList parseMLets(String content) throws MLetParseException { ArrayList list = new ArrayList(); int start = 0; int current = -1; while ((current = findOpenTag(content, start, MLET_TAG)) >= 0) { int end = findCloseTag(content, current + 1, MLET_TAG, true); if (end < 0) throw new MLetParseException("MLET tag not closed at index: " + current); String mlet = content.substring(current, end); list.add(mlet); start = end + 1; } return list; } private void parseMLetArguments(MLetTag tag, String content) throws MLetParseException { int start = 0; int current = -1; while ((current = findOpenTag(content, start, ARG_TAG)) >= 0) { int end = findCloseTag(content, current + 1, ARG_TAG, false); if (end < 0) throw new MLetParseException("ARG tag not closed"); String arg = content.substring(current, end); int type = arg.indexOf(TYPE_ATTR); if (type < 0) throw new MLetParseException("Missing TYPE attribute"); int value = arg.indexOf(VALUE_ATTR); if (value < 0) throw new MLetParseException("Missing VALUE attribute"); String className = findAttributeValue(arg, type, TYPE_ATTR); tag.addArg(className, convertToObject(className, findAttributeValue(arg, value, VALUE_ATTR))); start = end + 1; } } private void parseMLetAttributes(MLetTag tag, String content) throws MLetParseException { int end = content.indexOf(CLOSE_BRACKET); String attributes = content.substring(0, end); // Find mandatory attributes int archive = -1; int object = -1; int code = -1; archive = attributes.indexOf(ARCHIVE_ATTR); if (archive < 0) throw new MLetParseException("Missing ARCHIVE attribute"); code = attributes.indexOf(CODE_ATTR); object = attributes.indexOf(OBJECT_ATTR); if (code < 0 && object < 0) throw new MLetParseException("Missing CODE or OBJECT attribute"); if (code > 0 && object > 0) throw new MLetParseException("CODE and OBJECT attributes cannot be both present"); if (code >= 0) tag.setCode(findAttributeValue(attributes, code, CODE_ATTR)); else tag.setObject(findAttributeValue(attributes, object, OBJECT_ATTR)); tag.setArchive(findAttributeValue(attributes, archive, ARCHIVE_ATTR)); // Look for optional attributes int codebase = attributes.indexOf(CODEBASE_ATTR); if (codebase >= 0) tag.setCodeBase(findAttributeValue(attributes, codebase, CODEBASE_ATTR)); int name = attributes.indexOf(NAME_ATTR); if (name >= 0) { String objectName = findAttributeValue(attributes, name, NAME_ATTR); try { tag.setName(new ObjectName(objectName)); } catch (MalformedObjectNameException x) { throw new MLetParseException("Invalid ObjectName: " + objectName); } } int version = attributes.indexOf(VERSION_ATTR); if (version >= 0) tag.setVersion(findAttributeValue(attributes, version, VERSION_ATTR)); } private String findAttributeValue(String content, int start, String attribute) throws MLetParseException { int equal = content.indexOf('=', start); if (equal < 0) throw new MLetParseException("Missing '=' for attribute"); // Ensure no garbage if (!attribute.equals(content.substring(start, equal).trim())) throw new MLetParseException("Invalid attribute"); int begin = content.indexOf('"', equal + 1); if (begin < 0) throw new MLetParseException("Missing quotes for attribute value"); // Ensure no garbage if (content.substring(equal + 1, begin).trim().length() != 0) throw new MLetParseException("Invalid attribute value"); int end = content.indexOf('"', begin + 1); if (end < 0) throw new MLetParseException("Missing quote for attribute value"); return content.substring(begin + 1, end).trim(); } private int findOpenTag(String content, int start, String tag) { String opening = new StringBuffer(OPEN_BRACKET).append(tag).toString(); return content.indexOf(opening, start); } private int findCloseTag(String content, int start, String tag, boolean strictSyntax) { int count = 1; do { int close = content.indexOf(CLOSE_BRACKET, start); if (close < 0) { return -1; } int open = content.indexOf(OPEN_BRACKET, start); if (open >= 0 && close > open) { ++count; } else { --count; if (count == 0) { // Either I found the closing bracket of the open tag, // or the closing tag if (!strictSyntax || (strictSyntax && content.charAt(close - 1) == '/')) { // Found the closing tag return close + 1; } else { // Found the closing bracket of the open tag, go for the full closing tag String closing = new StringBuffer(OPEN_BRACKET).append("/").append(tag).append(CLOSE_BRACKET).toString(); close = content.indexOf(closing, start); if (close < 0) return -1; else return close + closing.length(); } } } start = close + 1; } while (true); } private String stripComments(String content) throws MLetParseException { StringBuffer buffer = new StringBuffer(); int start = 0; int current = -1; while ((current = content.indexOf(OPEN_COMMENT, start)) >= 0) { int end = content.indexOf(CLOSE_COMMENT, current + 1); if (end < 0) throw new MLetParseException("Missing close comment tag at index: " + current); String stripped = content.substring(start, current); buffer.append(stripped); start = end + CLOSE_COMMENT.length(); } String stripped = content.substring(start, content.length()); buffer.append(stripped); return buffer.toString(); } private String convertToUpperCase(String content) throws MLetParseException { StringBuffer buffer = new StringBuffer(); int start = 0; int current = -1; while ((current = content.indexOf("\"", start)) >= 0) { int end = content.indexOf("\"", current + 1); if (end < 0) throw new MLetParseException("Missing closing quote at index: " + current); String converted = content.substring(start, current).toUpperCase(); buffer.append(converted); String quoted = content.substring(current, end + 1); buffer.append(quoted); start = end + 1; } String converted = content.substring(start, content.length()).toUpperCase(); buffer.append(converted); return buffer.toString(); } private Object convertToObject(String clsName, String value) throws MLetParseException { try { if (clsName.equals("boolean") || clsName.equals("java.lang.Boolean")) return Boolean.valueOf(value); else if (clsName.equals("byte") || clsName.equals("java.lang.Byte")) return Byte.valueOf(value); else if (clsName.equals("char") || clsName.equals("java.lang.Character")) { char ch = 0; if (value.length() > 0) ch = value.charAt(0); return new Character(ch); } else if (clsName.equals("short") || clsName.equals("java.lang.Short")) return Short.valueOf(value); else if (clsName.equals("int") || clsName.equals("java.lang.Integer")) return Integer.valueOf(value); else if (clsName.equals("long") || clsName.equals("java.lang.Long")) return Long.valueOf(value); else if (clsName.equals("float") || clsName.equals("java.lang.Float")) return Float.valueOf(value); else if (clsName.equals("double") || clsName.equals("java.lang.Double")) return Double.valueOf(value); else if (clsName.equals("java.lang.String")) return value; else if (mlet != null) { try { Class cls = mlet.loadClass(clsName); Constructor ctor = cls.getConstructor(new Class[]{String.class}); return ctor.newInstance(new Object[]{value}); } catch (Exception ignored) { } } } catch (NumberFormatException x) { throw new MLetParseException("Invalid value: " + value); } return null; } }