The parameter bundle provides a comprehensive example that illustrates the following features:
Service factory
Asynchronous event handling
Permission checking
Performing privileged actions
In a Java run-time environment that does not support permissions, comment out the security-related code.
package com.acme.service.param; /** * This service allows a caller to store and retrieve parameters * in the form of a key/value pair. */ public interface ParameterService { /** * Stores a parameter. * @param key the name of the parameter. * @param value the value of the parameter. */ public void set(String key, String value); /** * Retrieves a parameter. */ public String get(String key); }
package com.acme.service.param; import java.io.IOException; import java.util.Properties; public interface ParameterAdmin { /** * Stores a parameter set for a bundle. * @param bundleLocation the location of the bundle. * @param props the parameter set. * @exception java.io.IOException if saving to the property file * fails. * @exception java.security.SecurityException if the caller does * not have AdminPermission and the Java runtime supports * permissions. */ public void set(String bundleLocation, Properties props) throws IOException; /** * Retrieves the parameter set for a bundle. * * @exception java.security.SecurityException if the caller does * not have AdminPermission and the Java runtime supports * permissions. */ public Properties get(String bundleLocation) throws IOException; }
package com.acme.impl.param; import java.util.*; import com.acme.service.param.ParameterService; class ParameterServiceImpl implements ParameterService { Properties props; public void set(String key, String value) { props.put(key, value); } public String get(String key) { return props.get(key); } ParameterServiceImpl(Properties initProps) { if (initProps != null) // copy initial parameters if any props = (Properties) initProps.clone(); else props = new Properties(); } }
package com.acme.impl.param; import java.io.IOException; import java.util.Properties; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceFactory; import org.osgi.framework.ServiceRegistration; class ParameterServiceFactory implements ServiceFactory { private ParameterStore store; ParameterServiceFactory(ParameterStore store) { this.store = store; } // A client bundle starts using ParameterService. public Object getService(Bundle bundle, ServiceRegistration reg) { try { return new ParameterServiceImpl(store.load(bundle)); } catch (IOException e) { // should log the exception } return new ParameterServiceImpl(null); } // The client bundle finishes using ParameterService. public void ungetService(Bundle bundle, ServiceRegistration reg, Object service) { ParameterServiceImpl ps = (ParameterServiceImpl) service; try { store.save(bundle, ps.props); } catch (IOException e) { // should log the exception } } }
package com.acme.impl.param; import java.util.Properties; import java.io.IOException; import org.osgi.framework.*; import com.acme.service.param.ParameterAdmin; class ParameterAdminImpl implements ParameterAdmin { private static AdminPermission adminPermission = new AdminPermission(); private BundleContext context; private ParameterStore store; ParameterAdminImpl(BundleContext ctxt, ParameterStore store) { this.context = ctxt; this.store = store; } public void set(String loc, Properties props) throws IOException { sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(adminPermission); } Bundle b = getBundle(loc); if (b == null) throw new IOException("Bundle (" + loc + ") not installed"); store.save(b, props); } public Properties get(String loc) throws IOException { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(adminPermission); } Bundle b = getBundle(loc); if (b == null) throw new IOException("Bundle (" + loc + ") not installed"); return store.load(b); } private Bundle getBundle(String loc) { Bundle[] bundles = context.getBundles(); for (int i=0; i<bundles.length; i++) { if (bundles[i].getLocation().equals(loc)) { return bundles[i]; } } return null; } }
package com.acme.impl.param; import java.io.*; import java.util.Properties; import java.security.*; import org.osgi.framework.*; class ParameterStore { static final String FILENAME = "parameters.properties"; private File dataRoot; ParameterStore(BundleContext ctxt) { this.dataRoot = ctxt.getDataFile(""); } synchronized void save(final Bundle b, final Properties props) throws IOException { try { AccessController.doPrivileged( new PrivilegedExceptionAction() { public Object run() throws IOException { String id = Long.toString(b.getBundleId()); File paramDir = new File(dataRoot, id); if (! paramDir.exists()) { if (! paramDir.mkdir()) throw new IOException( "Couldn't create dir: "+ paramDir); } File paramFile = new File(paramDir, FILENAME); FileOutputStream out = new FileOutputStream(paramFile); props.save(out, "Parameters for " + b.getLocation()); out.close(); return null; } }); } catch (PrivilegedActionException e) { throw (IOException) e.getException(); } } synchronized Properties load(final Bundle b) throws IOException { Properties props = null; try { props = (Properties) AccessController.doPrivileged( new PrivilegedExceptionAction() { public Object run() throws IOException { Properties p = new Properties(); String id = Long.toString(b.getBundleId()); File paramFile = new File(dataRoot, id + File.separator + FILENAME); if (!paramFile.exists()) return null; FileInputStream in = new FileInputStream(paramFile); p.load(in); in.close(); return p; } }); } catch (PrivilegedActionException e) { throw (IOException) e.getException(); } return props; } synchronized void clear(Bundle b) { final long id = b.getBundleId(); String[] ids = dataRoot.list(); for (int i=0; i<ids.length; i++) { try { if (id != Long.parseLong(ids[i])) continue; AccessController.doPrivileged( new PrivilegedAction() { public Object run() { File paramFile = new File(dataRoot, id + File.separator + FILENAME); paramFile.delete(); File paramDir = new File(dataRoot, Long.toString(id)); paramDir.delete(); return null; } }); break; } catch (NumberFormatException e) { } } } }
package com.acme.impl.param; import java.io.*; import org.osgi.framework.*; public class Activator implements BundleActivator { private ServiceRegistration reg; public void start(BundleContext ctxt) { final ParameterStore store = new ParameterStore(ctxt); // add a bundle listener listening to // uninstallation of client bundles ctxt.addBundleListener(new BundleListener() { public void bundleChanged(BundleEvent e) { // we are only interested in bundle uninstalled event if (e.getType() != BundleEvent.UNINSTALLED) return; store.clear(e.getBundle()); } }); reg = ctxt.registerService( "com.acme.service.param.ParameterService", new ParameterServiceFactory(store), null); ctxt.registerService( "com.acme.service.param.ParameterAdmin", new ParameterAdminImpl(ctxt, store), null); } public void stop(BundleContext ctxt) { if (reg != null) reg.unregister(); } }
Bundle-Activator: com.acme.impl.param.Activator Export-Package: com.acme.service.param
package com.acme.admin.param; import java.util.*; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import org.osgi.framework.*; import com.acme.service.param.ParameterAdmin; class AdminServlet extends HttpServlet { private BundleContext context; static final String TAG_NEW_PROP_NAME = "com.acme.admin.new.property.name"; static final String TAG_NEW_PROP_VALUE = "com.acme.admin.new.property.value"; static final String TAG_BUNDLE_ID = "com.acme.admin.bundle.id"; AdminServlet(BundleContext ctxt) { this.context = ctxt; } public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletOutputStream out = resp.getOutputStream(); resp.setContentType("text/html"); displayHeader(out); String idstr = req.getParameter("id"); if (idstr == null) { displayAllBundles(out); } else { long bundleId = Long.parseLong(idstr); displayForm(out, bundleId); } displayFooter(out); out.close(); } public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Properties props = new Properties(); String newPropName = null; String newPropVal = null; Enumeration propNames = req.getParameterNames(); while (propNames.hasMoreElements()) { String k = (String) propNames.nextElement(); String v = req.getParameter(k); if (TAG_NEW_PROP_NAME.equals(k)) newPropName = v; else if (TAG_NEW_PROP_VALUE.equals(k)) newPropVal = v; else if (! (TAG_BUNDLE_ID.equals(k))) { if (v.length() > 0) props.put(k, v); } } if (newPropName.length() > 0 && newPropVal.length() > 0) props.put(newPropName, newPropVal); ServiceReference ref = context.getServiceReference( "com.acme.service.param.ParameterAdmin"); ParameterAdmin admin = (ParameterAdmin) context.getService(ref); long id = Long.parseLong(req.getParameter(TAG_BUNDLE_ID)); Bundle b = context.getBundle(id); admin.set(b.getLocation(), props); context.ungetService(ref); resp.sendRedirect(Activator.SERVLET_ALIAS + "?id=" + id); } private void displayAllBundles(ServletOutputStream out) throws ServletException, IOException { Bundle[] bundles = context.getBundles(); if (bundles == null) { out.println("<i>No bundle is installed " + "in the framework.</i>"); } else { out.println("The following bundles are currently " + "installed in the framework: " + "<p> " + "<ul> "); for (int i=0; i<bundles.length; i++) out.println("<li>" + "<a href="" + Activator.SERVLET_ALIAS + "?id=" + bundles[i].getBundleId() + "">" + bundles[i].getLocation() + "</a> "); out.println("</ul> "); } } private void displayForm(ServletOutputStream out, long id) throws ServletException, IOException { Bundle b = context.getBundle(id); ServiceReference ref = context.getServiceReference( "com.acme.service.param.ParameterAdmin"); ParameterAdmin admin = (ParameterAdmin) context.getService(ref); Properties props = admin.get(b.getLocation()); context.ungetService(ref); out.println("<b>Bundle</b>: " + b.getLocation() + "<p> "); if (props == null) { out.println("<i>There is no property " + "for this bundle.</i>"); } out.println("<form action="" + Activator.SERVLET_ALIAS + "" method=post> "); out.println("<table border=0> " + "<tr><th align=left>Property Name</th>" + "<th align=left>Value</th></tr> "); if (props != null) for (Enumeration propNames = props.propertyNames(); propNames.hasMoreElements(); ) { String propName = (String) propNames.nextElement(); String propVal = props.getProperty(propName); out.println("<tr> " + "<td>" + propName + "</td>" + "<td><input name="" + propName + "" " + "value="" + propVal + "" size=" + (propVal.length() + 5) +"></td> " + "</tr> "); } out.println("<tr> " + "<td><input name="" + TAG_NEW_PROP_NAME + "" "+ "size=25></td>" + "<td><input name="" + TAG_NEW_PROP_VALUE + "" "+ "size=25></td> " + "</tr> "); out.println("</table><p> " + "<input type=hidden name="" + TAG_BUNDLE_ID + "" " + "value="" + id + ""> " + "<input type=submit value="Set"> " + "<input type=reset> " + "[<a href="" + Activator.SERVLET_ALIAS + "">Done</a>] " + "</form> "); } private void displayHeader(ServletOutputStream out) throws ServletException, IOException { out.println("<HTML> " + "<HEAD> " + "<TITLE>Bundle Parameter Configuration</TITLE> " + "</HEAD> " + "<BODY BGCOLOR=white> " + "<h2>Bundle Parameter Configuration</h2> " + "<p> "); } private void displayFooter(ServletOutputStream out) throws ServletException, IOException { out.println("</BODY> " + "</HTML>"); } }
package com.acme.admin.param; import java.net.*; import java.io.IOException; import javax.servlet.*; import javax.servlet.http.*; import org.osgi.framework.*; import org.osgi.service.http.*; import com.acme.service.param.ParameterAdmin; public class Activator implements BundleActivator { private HttpService http; final static String SERVLET_ALIAS = "/admin/parameters"; public void start(BundleContext context) throws Exception { ServiceReference ref = context.getServiceReference( "org.osgi.service.http.HttpService"); http = (HttpService) context.getService(ref); AdminServlet servlet = new AdminServlet(context); http.registerServlet(SERVLET_ALIAS, servlet, null, null); } public void stop(BundleContext context) throws Exception { if (http != null) { http.unregister(SERVLET_ALIAS); } } }
Bundle-Activator: com.acme.admin.param.Activator Import-Package: com.acme.service.param, org.osgi.service.http, javax.servlet, javax.servlet.http Bundle-Name: The Administrative Bundle Bundle-Description: This bundle can access and modify parameters for other bundles. It requires org.osgi.framework.AdminPermission, and provides a web user interface at http://<host>:8080/admin. Bundle-Vendor: Acme Systems, Inc. Bundle-Version: 1.0 Bundle-DocURL: http://www.acme.com/bundles/admin/index.html Bundle-ContactAddress: [email protected] Import-Service: com.acme.service.param.ParameterAdmin
package com.acme.admin.resolv; import java.util.*; import java.net.*; import java.io.*; import org.osgi.framework.*; /** * This class performs recursive bundle resolution. It parses a * bundle's Import-Package manifest header and installs/activates * the bundles that export the needed packages. */ class Facilitator { private BundleContext context; /** * The map that encapsulates the knowledge of given a package, * which bundle exports that package, and at what version. It maps * a package name to the exporting bundle's location and the * package version. */ private Properties packageExporterMap; // Flag a visited bundle to prevent following circular dependency // during recursion. private Hashtable visited = new Hashtable(); // Identify the unsatisfied bundle when resolution fails. // maps an unresolved bundle to a vector of missing package names private Hashtable unsatisfiedBundles = new Hashtable(); /** * The constructor. It reads the package exporter map from a * property file inside this bundle. */ Facilitator(BundleContext ctxt) throws IOException { this.context = ctxt; this.packageExporterMap = new Properties(); String resourceName = "/com/acme/admin/resolv/package-exporters.map"; InputStream in = this.getClass().getResourceAsStream(resourceName); packageExporterMap.load(in); } /** * Reset the internal state so that the resolution process can be * run anew. */ void reset() { visited.clear(); unsatisfiedBundles.clear(); } /** * Resolve a bundle. It follows this algorithm: (1) install the * bundle whose location is given, (2) parse the bundle's * Import-Package manifest header, (3) based on the packages it * needs to import, look up the package exporter map to find * out what additional bundles should be installed, * (4) recursively perform this sequence until all bundles are * resolved or a bundle cannot be resolved. * * @param urlstr the location string of the bundle to be resolved. * @return true if all bundles are resolved, false otherwise. If * not resolved, the unsatisfied bundle can be retrieved by * calling getUnsatisfiedBundle and the missing package name is * returned by getMissingPackage method. */ boolean resolve(String urlstr) throws BundleException { if (urlstr == null) return false; if (visited.get(urlstr) != null) { // if a bundle is processed, don't run in circles return true; } Bundle b = context.installBundle(urlstr); // flag that the bundle is processed visited.put(urlstr, Boolean.TRUE); Dictionary headers = b.getHeaders(); String imports = (String) headers.get("Import-Package"); boolean r = true; if (imports != null) { String[] loc = findExporters(imports, b); for (int i=0; i<loc.length; i++) { r = r & resolve(loc[i]); } } if (r) { b.start(); } return r; } /** * Return the names of the missing packages for the given bundle, * or null if all bundles are resolved. */ String[] getMissingPackages(Bundle b) { if (b == null) return null; Vector pkgsVec = (Vector) unsatisfiedBundles.get(b); String[] pkgs = null; if (pkgsVec != null && pkgsVec.size() > 0) { pkgs = new String[pkgsVec.size()]; pkgsVec.copyInto(pkgs); } return pkgs; } /** * Return the unsatisfied bundles, or an empty enumeration if * all bundles are resolved. */ Enumeration getUnsatisfiedBundles() { return unsatisfiedBundles.keys(); } /** * Find the exporters for the packages needed by the given bundle. * * @param imports the value of the Import-Package header. * @param b the bundle that defines the Import-Package header. * @return the locations of the bundles that export the needed * package at the compatible versions. */ private String[] findExporters(String imports, Bundle b) { StringTokenizer st = new StringTokenizer(imports, ","); Vector missingPkgs = null; Vector v = new Vector(st.countTokens()); while (st.hasMoreTokens()) { String token = st.nextToken().trim(); int semicolonPos = token.indexOf(';'), String pkgname = token; String importVersion = ""; if (semicolonPos != -1) { pkgname = token.substring(0, semicolonPos).trim(); int eqPos = token.indexOf('=', semicolonPos); importVersion = token.substring(eqPos + 1); } if (isFromClasspath(pkgname)) continue; String loc = (String) packageExporterMap.get(pkgname); if (loc != null) { String exportVersion = ""; int commaPos = loc.indexOf(','), if (commaPos != -1) { exportVersion = loc.substring(commaPos + 1); loc = loc.substring(0, commaPos); } if (! areVersionsCompatible(importVersion, exportVersion)) { loc = null; } } if (loc == null) { if (missingPkgs == null) missingPkgs = new Vector(); missingPkgs.addElement(pkgname); } v.addElement(loc); } String[] locations = new String[v.size()]; v.copyInto(locations); if (missingPkgs != null) unsatisfiedBundles.put(b, missingPkgs); return locations; } /** * Check if a package is on the CLASSPATH. */ private boolean isFromClasspath(String pkg) { URL u = ClassLoader.getSystemResource(pkg.replace('.','/') + "/"); return u != null; } /** * Check if a package version for export is equal to or greater * than the version required for importing the same package. * * @param impVerStr the package version string required for * import. * @param expVerStr the package version string for export. * @return true if the check passes, false otherwise. */ private boolean areVersionsCompatible(String impVerStr, String expVerStr) { StringTokenizer impst = new StringTokenizer(impVerStr, "."); StringTokenizer expst = new StringTokenizer(expVerStr, "."); try { while (impst.hasMoreTokens() && expst.hasMoreTokens()) { int iv = Integer.parseInt(impst.nextToken().trim()); int ev = Integer.parseInt(expst.nextToken().trim()); if (ev < iv) { // E.g., if import needs 1.4.5, export is 1.3.6, // then import > export and no need to scan // further because at the second component, 4 > 3 return false; } else if (ev > iv) { // E.g., if import needs 1.4.5, export is // 1.5.1, then import < export and no need to scan // further because at the second component, 4 < 5 return true; } } if (impst.hasMoreTokens()) { // E.g., if import needs 1.4.5, export is 1.4, then // import > export return false; } } catch (NumberFormatException e) { return false; } return true; } }
package com.acme.admin.resolv; import java.net.*; import java.io.*; import java.util.*; import org.osgi.framework.*; public class Activator implements BundleActivator { private ServerThread server; public void start(BundleContext context) throws IOException { server = new ServerThread(context); server.start(); } public void stop(BundleContext context) throws IOException { server.terminate(); } } class ServerThread extends Thread { private Facilitator facilitator; private ServerSocket ss; private boolean running = true; private Vector clientConnections = new Vector(16); ServerThread(BundleContext ctxt) throws IOException { this.ss = new ServerSocket(8082, 5); facilitator = new Facilitator(ctxt); } public void run() { try { while(running) { Socket s = ss.accept(); if (!running) { // server is being shut down break; } // accept a client connection ConnectionThread conn = this.new ConnectionThread(s); clientConnections.addElement(conn); // start a thread to handle it conn.start(); } ss.close(); ss = null; } catch (IOException e) { System.out.println(e); } } // terminate the server and clean up void terminate() throws IOException { // terminate each client connection for (int i=0; i<clientConnections.size(); i++) { ConnectionThread conn = (ConnectionThread) clientConnections.elementAt(i); conn.terminate(); } clientConnections.removeAllElements(); running = false; // force the server socket to return from accept if (ss != null) { ss.close(); } } class ConnectionThread extends Thread { private Socket socket; ConnectionThread(Socket s) { this.socket = s; } public void run() { PrintWriter out = null; try { InputStream is = socket.getInputStream(); OutputStream os = socket.getOutputStream(); BufferedReader in = new BufferedReader(new InputStreamReader(is)); out = new PrintWriter(os, true); out.print(" Bundle Resolution " + " Enter the bundle's URL: "); out.flush(); // get command from remote host String url = in.readLine(); facilitator.reset(); if (facilitator.resolve(url)) { out.println(" OK"); } else { Enumeration e = facilitator.getUnsatisfiedBundles(); while (e.hasMoreElements()) { Bundle b = (Bundle) e.nextElement(); String[] pkgs = facilitator.getMissingPackages(b); out.println("Bundle " + b.getLocation() + " needs the missing packages below:"); for (int i=0; i<pkgs.length; i++) { out.println(" " + pkgs[i]); } } } out.println(); } catch (BundleException e) { out.println(" " + e); Throwable t = e.getNestedException(); if (t != null) out.println("Nested exception: " + t); out.println(); } catch (IOException e) { } finally { try { if (socket != null) socket.close(); } catch(IOException e) {} clientConnections.removeElement(this); } } void terminate() throws IOException { // this will cause IOException to be raised from the // readLine call in the client thread socket.close(); socket = null; } } // ConnectionThread }
org.osgi.service.http=file:jes_path/bundles/http.jar javax.servlet=file:jes_path/bundles/servlet.jar, 2.1.1 javax.servlet.http=file:jes_path/bundles/servlet.jar, 2.1.1 org.osgi.service.log=file:jes_path/bundles/log.jar, 1.0 com.acme.service.param=file:/home/joe/bundles/param.jar
Bundle-Activator: com.acme.admin.resolv.Activator
3.144.232.189