Java tutorial
/* * 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.webconsole.internal.compendium; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.Array; import java.util.Arrays; import java.util.Dictionary; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.SortedMap; import java.util.SortedSet; import java.util.StringTokenizer; import java.util.TreeMap; import java.util.TreeSet; import java.util.Vector; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.felix.webconsole.internal.Util; import org.apache.felix.webconsole.internal.servlet.OsgiManager; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONWriter; import org.osgi.framework.Bundle; import org.osgi.framework.Constants; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; import org.osgi.framework.Version; import org.osgi.service.cm.Configuration; import org.osgi.service.cm.ConfigurationAdmin; import org.osgi.service.cm.ManagedService; import org.osgi.service.cm.ManagedServiceFactory; import org.osgi.service.metatype.AttributeDefinition; import org.osgi.service.metatype.ObjectClassDefinition; /** * The <code>ConfigManager</code> TODO */ public class ConfigManager extends ConfigManagerBaseServ { private static class PlaceholderConfiguration implements Configuration { private final String factoryPid; private String bundleLocation; PlaceholderConfiguration(String factoryPid) { this.factoryPid = factoryPid; } public void delete() { // dummy configuration cannot be deleted } public String getBundleLocation() { return bundleLocation; } public String getFactoryPid() { return factoryPid; } public String getPid() { return SAVED_PID; } public Dictionary getProperties() { // dummy configuration has no properties return null; } public void setBundleLocation(String bundleLocation) { this.bundleLocation = bundleLocation; } public void update() { // dummy configuration cannot be updated } public void update(Dictionary properties) { // dummy configuration cannot be updated } } private static final String PID_FILTER = "pidFilter"; public static final String CONFIG_NAME = "configMgr"; public static final String LABEL = "Configuration"; public static final String SERVICE_PID = "pid"; public static final String factoryPID = "factoryPid"; private static final String SAVED_PID = "[Temporary PID replaced by real PID upon save]"; private static final int TAIL_LENGTH = 5; private static final int STRING_FORWARD = 1; private void addConfigurationInfo(Configuration config, JSONWriter json, String locale) throws JSONException { if (config.getFactoryPid() != null) { json.key(factoryPID); json.value(config.getFactoryPid()); } String currLocation; if (config.getBundleLocation() == null) { currLocation = "None"; } else { Bundle currBundle = this.getBundle(config.getBundleLocation()); Dictionary headersBook = currBundle.getHeaders(locale); String name = (String) headersBook.get(Constants.BUNDLE_NAME); if (name == null) { currLocation = currBundle.getSymbolicName(); } else { currLocation = name + " (" + currBundle.getSymbolicName() + ")"; } Version currVersion = Version.parseVersion((String) headersBook.get(Constants.BUNDLE_VERSION)); currLocation += ", Version " + currVersion.toString(); } json.key("bundleLocation"); json.value(currLocation); } private String applyConfiguration(HttpServletRequest comingRequest, ConfigurationAdmin ca, String pid) throws IOException { if (comingRequest.getParameter("delete") != null) { // only delete if the PID is not our place holder if (!SAVED_PID.equals(pid)) { // TODO: should log this here !! Configuration config = ca.getConfiguration(pid, null); config.delete(); } return comingRequest.getHeader("Referer"); } String factoryPid = comingRequest.getParameter(ConfigManager.factoryPID); Configuration tempConfig = null; String propertyList = comingRequest.getParameter("propertylist"); if (propertyList == null) { String propertiesString = comingRequest.getParameter("properties"); if (propertiesString != null) { byte[] propBytes = propertiesString.getBytes("ISO-8859-1"); ByteArrayInputStream bin = new ByteArrayInputStream(propBytes); Properties props = new Properties(); props.load(bin); tempConfig = getConfiguration(ca, pid, factoryPid); tempConfig.update(props); } } else { tempConfig = getConfiguration(ca, pid, factoryPid); Dictionary propsBook = tempConfig.getProperties(); if (propsBook == null) { propsBook = new Hashtable(); } Map attrDefMap = this.getAttributeDefinitionMap(tempConfig, null); if (attrDefMap != null) { StringTokenizer propTokens = new StringTokenizer(propertyList, ","); while (propTokens.hasMoreTokens()) { String propName = propTokens.nextToken(); AttributeDefinition currAttrDef = (AttributeDefinition) attrDefMap.get(propName); if (currAttrDef == null || (currAttrDef.getCardinality() == 0 && currAttrDef.getType() == AttributeDefinition.STRING)) { String currPropName = comingRequest.getParameter(propName); if (currPropName != null) { propsBook.put(propName, currPropName); } } else if (currAttrDef.getCardinality() == 0) { // scalar of non-string String curProp = comingRequest.getParameter(propName); if (curProp != null) { try { propsBook.put(propName, this.getType(currAttrDef.getType(), curProp)); } catch (NumberFormatException nfe) { // don't care } } } else { // array or vector of any type Vector tempVec = new Vector(); String[] properties = comingRequest.getParameterValues(propName); if (properties != null) { for (int i = 0; i < properties.length; i++) { try { tempVec.add(this.getType(currAttrDef.getType(), properties[i])); } catch (NumberFormatException nfe) { // don't care } } } // but ensure size int maxSize = Math.abs(currAttrDef.getCardinality()); if (tempVec.size() > maxSize) { tempVec.setSize(maxSize); } if (currAttrDef.getCardinality() < 0) { // keep the vector propsBook.put(propName, tempVec); } else { // convert to an array propsBook.put(propName, this.getArray(currAttrDef.getType(), tempVec)); } } } } tempConfig.update(propsBook); } // redirect to the new configuration (if existing) return (tempConfig != null) ? tempConfig.getPid() : ""; } private void configForm(JSONWriter jsonWriter, String inputPid, Configuration config, String pidFilter, String locale) throws JSONException { jsonWriter.key(ConfigManager.SERVICE_PID); jsonWriter.value(inputPid); if (pidFilter != null) { jsonWriter.key(PID_FILTER); jsonWriter.value(pidFilter); } Dictionary propsDictionary = null; ObjectClassDefinition classDef; if (config != null) { propsDictionary = config.getProperties(); classDef = this.getObjectClassDefinition(config, locale); } else { classDef = this.getObjectClassDefinition(inputPid, locale); } propsDictionary = this.mergeWithMetaType(propsDictionary, classDef, jsonWriter); if (propsDictionary != null) { jsonWriter.key("title"); jsonWriter.value(inputPid); jsonWriter.key("description"); jsonWriter.value( "Please enter configuration properties for this configuration in the field below. This configuration has no associated description"); jsonWriter.key("propertylist"); jsonWriter.value("properties"); jsonWriter.key("properties"); jsonWriter.object(); for (Enumeration propEnum = propsDictionary.keys(); propEnum.hasMoreElements();) { Object nextElement = propEnum.nextElement(); // ignore well known special properties if (!nextElement.equals(Constants.SERVICE_PID) && !nextElement.equals(Constants.SERVICE_DESCRIPTION) && !nextElement.equals(Constants.SERVICE_ID) && !nextElement.equals(Constants.SERVICE_RANKING) && !nextElement.equals(ConfigurationAdmin.SERVICE_FACTORYPID) && !nextElement.equals(Constants.SERVICE_VENDOR) && !nextElement.equals(ConfigurationAdmin.SERVICE_BUNDLELOCATION)) { jsonWriter.key(String.valueOf(nextElement)); jsonWriter.value(propsDictionary.get(nextElement)); } } jsonWriter.endObject(); } if (config != null) { this.addConfigurationInfo(config, jsonWriter, locale); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // let's check for a json request final String currInfo = request.getPathInfo(); if (currInfo.endsWith(".json")) { response.setContentType("application/json"); response.setCharacterEncoding("UTF-8"); // after last slash and without extension String tempPid = currInfo.substring(currInfo.lastIndexOf('/') + 1, currInfo.length() - TAIL_LENGTH); // check whether the pid is actually a filter for the selection // of configurations to display String pidFilter = request.getParameter(PID_FILTER); if (pidFilter == null) { pidFilter = tempPid; } try { getBundleContext().createFilter(pidFilter); // if the pidFilter was set from the pid, clear the pid if (tempPid == pidFilter) { tempPid = null; } } catch (InvalidSyntaxException ex) { pidFilter = null; } final ConfigurationAdmin currConfigAdmin = this.getConfigurationAdmin(); final Locale currLocale = getLocale(request); final String localeInfo = (currLocale != null) ? currLocale.toString() : null; final PrintWriter printWriter = response.getWriter(); try { printWriter.write("["); final SortedMap tempServices = this.getServices(tempPid, pidFilter, localeInfo, false); final Iterator tempIter = tempServices.keySet().iterator(); boolean moreConfig = false; while (tempIter.hasNext()) { final String servicePid = tempIter.next().toString(); final Configuration config = this.getConfigurationWithPid(currConfigAdmin, servicePid); if (config != null) { if (moreConfig) { printWriter.print(','); } this.printJsonConfig(printWriter, servicePid, config, pidFilter, localeInfo); moreConfig = true; } } printWriter.write("]"); } catch (InvalidSyntaxException ex) { // this should not happend as we checked the filter before } // nothing more to do return; } super.doGet(request, response); } protected void doPost(HttpServletRequest comingrequest, HttpServletResponse response) throws IOException { // needed multiple times below String tempPid = comingrequest.getParameter(ConfigManager.SERVICE_PID); if (tempPid == null) { String pathInfo = comingrequest.getPathInfo(); tempPid = pathInfo.substring(pathInfo.lastIndexOf('/') + STRING_FORWARD); } // the filter to select part of the configurations String pidFilterParam = comingrequest.getParameter(PID_FILTER); final ConfigurationAdmin currConfigAdmin = this.getConfigurationAdmin(); // ignore this request if the pid and/or configuration admin is missing if (tempPid == null || currConfigAdmin == null) { // should log this here !! return; } // the configuration to operate on (to be created or "missing") Configuration tempConfig = null; // should actually apply the configuration before redirecting if (comingrequest.getParameter("create") != null) { tempConfig = new PlaceholderConfiguration(tempPid); // ca.createFactoryConfiguration( // pid, null ); tempPid = tempConfig.getPid(); } else if (comingrequest.getParameter("apply") != null) { String redirectAddr = applyConfiguration(comingrequest, currConfigAdmin, tempPid); if (redirectAddr != null) { if (pidFilterParam != null) { redirectAddr += "?" + PID_FILTER + "=" + pidFilterParam; } this.sendRedirectURL(comingrequest, response, redirectAddr); } return; } if (tempConfig == null) { tempConfig = getConfigurationWithPid(currConfigAdmin, tempPid); } // send the result response.setContentType("application/json"); response.setCharacterEncoding("UTF-8"); final Locale requestLocale = getLocale(comingrequest); final String localeInfo = (requestLocale != null) ? requestLocale.toString() : null; printJsonConfig(response.getWriter(), tempPid, tempConfig, pidFilterParam, localeInfo); } private Object getArray(int type, Vector values) { int totalSize = values.size(); // short cut for string array if (type == AttributeDefinition.STRING) { return values.toArray(new String[totalSize]); } Object tempArray; switch (type) { case AttributeDefinition.FLOAT: tempArray = new float[totalSize]; case AttributeDefinition.LONG: tempArray = new long[totalSize]; case AttributeDefinition.INTEGER: tempArray = new int[totalSize]; case AttributeDefinition.SHORT: tempArray = new short[totalSize]; case AttributeDefinition.BOOLEAN: tempArray = new boolean[totalSize]; case AttributeDefinition.BYTE: tempArray = new byte[totalSize]; case AttributeDefinition.CHARACTER: tempArray = new char[totalSize]; case AttributeDefinition.DOUBLE: tempArray = new double[totalSize]; default: // unexpected, but assume string tempArray = new String[totalSize]; } for (int i = 0; i < totalSize; i++) { Array.set(tempArray, i, values.get(i)); } return tempArray; } private Configuration getConfiguration(ConfigurationAdmin ca, String pid, String factoryPid) throws IOException { if (factoryPid != null && (pid == null || pid.equals(SAVED_PID))) { return ca.createFactoryConfiguration(factoryPid, null); } return ca.getConfiguration(pid, null); } private Configuration getConfigurationWithPid(ConfigurationAdmin configAdmin, String comingPid) { if (configAdmin != null && comingPid != null) { try { // we use listConfigurations to not create configuration // objects persistently without the user providing actual // configuration String filter = "(" + Constants.SERVICE_PID + "=" + comingPid + ")"; Configuration[] filterConfigs = configAdmin.listConfigurations(filter); if (filterConfigs != null && filterConfigs.length > 0) { return filterConfigs[0]; } } catch (InvalidSyntaxException synEx) { // should print message } catch (IOException ioEx) { // should print message } } // fallback to no configuration at all return null; } private SortedMap getServices(String inputService, String currServiceFilter, String localeInfo, boolean ocdRequired) throws InvalidSyntaxException { // sorted map of options SortedMap currOptFactory = new TreeMap(String.CASE_INSENSITIVE_ORDER); // find all ManagedServiceFactories to get the factoryPIDs ServiceReference[] currServRefList = this.getBundleContext().getServiceReferences(inputService, currServiceFilter); for (int i = 0; currServRefList != null && i < currServRefList.length; i++) { Object pidObject = currServRefList[i].getProperty(Constants.SERVICE_PID); if (pidObject instanceof String) { String tempPid = (String) pidObject; String workerName; final ObjectClassDefinition classDef = this.getObjectClassDefinition(currServRefList[i].getBundle(), tempPid, localeInfo); if (classDef != null) { workerName = classDef.getName() + " ("; workerName += tempPid + ")"; } else { workerName = tempPid; } if (!ocdRequired || classDef != null) { currOptFactory.put(tempPid, workerName); } } } return currOptFactory; } public String getServletCapital() { return LABEL; } public String getServletLabel() { return CONFIG_NAME; } /** * @throws NumberFormatException * If the value cannot be converted to a number and type * indicates a numeric type */ private Object getType(int type, String value) { switch (type) { case AttributeDefinition.BYTE: return Byte.valueOf(value); case AttributeDefinition.CHARACTER: char c = (value.length() > 0) ? value.charAt(0) : 0; return new Character(c); case AttributeDefinition.BOOLEAN: return Boolean.valueOf(value); case AttributeDefinition.LONG: return Long.valueOf(value); case AttributeDefinition.INTEGER: return Integer.valueOf(value); case AttributeDefinition.SHORT: return Short.valueOf(value); case AttributeDefinition.DOUBLE: return Double.valueOf(value); case AttributeDefinition.FLOAT: return Float.valueOf(value); default: // includes AttributeDefinition.STRING return value; } } private void listConfigurations(PrintWriter printWriter, ConfigurationAdmin inputConfigAdmin, String inputPidFilter, String localeInfo) { try { // start with ManagedService instances SortedMap currOptionsPlain = getServices(ManagedService.class.getName(), inputPidFilter, localeInfo, true); // add in existing configuration (not duplicating ManagedServices) Configuration[] tempConfigsList = inputConfigAdmin.listConfigurations(inputPidFilter); for (int i = 0; tempConfigsList != null && i < tempConfigsList.length; i++) { // ignore configuration object if an entry already exists in the // map String tempPid = tempConfigsList[i].getPid(); if (currOptionsPlain.containsKey(tempPid)) { continue; } // insert and entry for the pid ObjectClassDefinition classDef = this.getObjectClassDefinition(tempConfigsList[i], localeInfo); String tempName; if (classDef != null) { tempName = classDef.getName() + " ("; tempName += tempPid + ")"; } else { tempName = tempPid; } if (classDef != null) { currOptionsPlain.put(tempPid, tempName); } } printOptionsForm(printWriter, currOptionsPlain, "configSelection_pid", "configure", "Configure"); } catch (Exception ex) { // write a message or ignore } } private void listFactoryConfigurations(PrintWriter printWriter, ConfigurationAdmin configAdmin, String pidFilter, String localeInfo) { try { SortedMap optionsFactory = getServices(ManagedServiceFactory.class.getName(), pidFilter, localeInfo, true); printOptionsForm(printWriter, optionsFactory, "configSelection_factory", "create", "Create"); } catch (Exception ex) { // write a message or ignore } } private Dictionary mergeWithMetaType(Dictionary propsDictionary, ObjectClassDefinition classDef, JSONWriter jsonWriter) throws JSONException { if (propsDictionary == null) { propsDictionary = new Hashtable(); } if (classDef != null) { jsonWriter.key("title"); jsonWriter.value(classDef.getName()); if (classDef.getDescription() != null) { jsonWriter.key("description"); jsonWriter.value(classDef.getDescription()); } AttributeDefinition[] currAttrDef = classDef.getAttributeDefinitions(ObjectClassDefinition.ALL); if (currAttrDef != null) { JSONArray propertyList = new JSONArray(); for (int i = 0; i < currAttrDef.length; i++) { jsonWriter.key(currAttrDef[i].getID()); jsonWriter.object(); Object idValue = propsDictionary.get(currAttrDef[i].getID()); if (idValue == null) { idValue = currAttrDef[i].getDefaultValue(); if (idValue == null) { if (currAttrDef[i].getCardinality() == 0) { idValue = ""; } else { idValue = new String[0]; } } } jsonWriter.key("name"); jsonWriter.value(currAttrDef[i].getName()); jsonWriter.key("type"); if (currAttrDef[i].getOptionLabels() != null && currAttrDef[i].getOptionLabels().length > 0) { jsonWriter.object(); jsonWriter.key("labels"); jsonWriter.value(Arrays.asList(currAttrDef[i].getOptionLabels())); jsonWriter.key("values"); jsonWriter.value(Arrays.asList(currAttrDef[i].getOptionValues())); jsonWriter.endObject(); } else { jsonWriter.value(currAttrDef[i].getType()); } if (currAttrDef[i].getCardinality() == 0) { // scalar if (idValue instanceof Vector) { idValue = ((Vector) idValue).get(0); } else if (idValue.getClass().isArray()) { idValue = Array.get(idValue, 0); } jsonWriter.key("value"); jsonWriter.value(idValue); } else { if (idValue instanceof Vector) { idValue = new JSONArray((Vector) idValue); } else if (idValue.getClass().isArray()) { idValue = new JSONArray(Arrays.asList((Object[]) idValue)); } else { JSONArray tmp = new JSONArray(); tmp.put(idValue); idValue = tmp; } jsonWriter.key("values"); jsonWriter.value(idValue); } if (currAttrDef[i].getDescription() != null) { jsonWriter.key("description"); jsonWriter.value(currAttrDef[i].getDescription()); } jsonWriter.endObject(); propertyList.put(currAttrDef[i].getID()); } jsonWriter.key("propertylist"); jsonWriter.value(propertyList); } // nothing more to display propsDictionary = null; } return propsDictionary; } private void printJsonConfig(PrintWriter printWriter, String pid, Configuration config, String pidFilter, String localeInfo) { JSONWriter jsonWriter = new JSONWriter(printWriter); if (pid != null) { try { jsonWriter.object(); this.configForm(jsonWriter, pid, config, pidFilter, localeInfo); jsonWriter.endObject(); } catch (Exception ex) { // add message } } } private void printOptionsForm(PrintWriter printWriter, SortedMap inputOptions, String formId, String submitMethod, String submitLabel) { SortedSet tempSet = new TreeSet(); for (Iterator entryIter = inputOptions.entrySet().iterator(); entryIter.hasNext();) { Entry tempEntry = (Entry) entryIter.next(); tempSet.add(tempEntry.getValue().toString() + Character.MAX_VALUE + tempEntry.getKey().toString()); } printWriter.println( "<select class='select' name='pid' id='" + formId + "' onChange='" + submitMethod + "();'>"); for (Iterator treeIter = tempSet.iterator(); treeIter.hasNext();) { String nextEntry = (String) treeIter.next(); int specChar = nextEntry.indexOf(Character.MAX_VALUE); String partialEntry = nextEntry.substring(0, specChar); String entryKey = nextEntry.substring(specChar + STRING_FORWARD); printWriter.print("<option value='" + entryKey + "'>"); printWriter.print(partialEntry); printWriter.println("</option>"); } printWriter.println("</select>"); printWriter.println(" "); printWriter.println("<input class='submit' type='button' value='" + submitLabel + "' onClick='" + submitMethod + "();' />"); } /** * @see org.apache.felix.webconsole.AbstractWebConsolePluginServlet#renderServletRequest(javax.servlet.http.HttpServletRequest, * javax.servlet.http.HttpServletResponse) */ public void renderServletRequest(HttpServletRequest request, HttpServletResponse response) throws IOException { // extract the configuration pid from the request path String tempPid = request.getPathInfo().substring(this.getServletLabel().length() + 1); if (tempPid.length() == 0) { tempPid = null; } else { tempPid = tempPid.substring(tempPid.lastIndexOf('/') + STRING_FORWARD); } // same procedure in doGet String currPidFilter = request.getParameter(PID_FILTER); if (currPidFilter == null) { currPidFilter = tempPid; } if (currPidFilter != null) { try { getBundleContext().createFilter(currPidFilter); // if the pidFilter was set from the pid, clear the pid if (tempPid == currPidFilter) { tempPid = null; } } catch (InvalidSyntaxException ex) { // its ok, if the pid is just a single PID currPidFilter = null; } } final ConfigurationAdmin currConfigAdmin = this.getConfigurationAdmin(); final Locale tempLocale = getLocale(request); final String localeInfo = (tempLocale != null) ? tempLocale.toString() : null; final PrintWriter printWriter = response.getWriter(); final String attrAppRoot = (String) request.getAttribute(OsgiManager.OSGI_APP_ROOT); printWriter.println( "<script src='" + attrAppRoot + "/res/ui/configmanager.js' language='JavaScript'></script>"); printWriter.println("<table class='content' cellpadding='0' cellspacing='0' width='100%'>"); if (currConfigAdmin == null) { printWriter.println("<tr class='content' id='configField'>"); printWriter.println("<td class='content'> </th>"); printWriter.println("<td class='content'>"); printWriter.print("Configuration Admin Service not available"); } else { printWriter.println("<tr class='content' id='configField'>"); printWriter.println("<td class='content'>Configurations</th>"); printWriter.println("<td class='content'>"); this.listConfigurations(printWriter, currConfigAdmin, currPidFilter, localeInfo); printWriter.println("</td>"); printWriter.println("</tr>"); printWriter.println("<tr class='content' id='factoryField'>"); printWriter.println("<td class='content'>Factory Configurations</th>"); printWriter.println("<td class='content'>"); this.listFactoryConfigurations(printWriter, currConfigAdmin, currPidFilter, localeInfo); } printWriter.println("</td>"); printWriter.println("</tr>"); printWriter.println("</table>"); // if a configuration is addressed, display it immediately final Configuration currConfig; if (request.getParameter("create") != null && tempPid != null) { currConfig = new PlaceholderConfiguration(tempPid); tempPid = currConfig.getPid(); } else { currConfig = getConfigurationWithPid(currConfigAdmin, tempPid); } if (tempPid != null) { Util.startScript(printWriter); printWriter.println("var configuration="); printJsonConfig(printWriter, tempPid, currConfig, currPidFilter, localeInfo); printWriter.println(";"); printWriter.println("displayConfigForm(configuration);"); Util.endScript(printWriter); } } }