001/* 002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Bogdan Stefanescu 018 * Ian Smith 019 * Florent Guillaume 020 */ 021 022package org.nuxeo.runtime.osgi; 023 024import java.util.IllegalFormatException; 025import java.util.LinkedList; 026import java.util.List; 027 028import org.apache.commons.lang.StringUtils; 029import org.apache.commons.logging.Log; 030import org.apache.commons.logging.LogFactory; 031import org.osgi.framework.Bundle; 032import org.osgi.framework.BundleContext; 033import org.osgi.framework.BundleEvent; 034import org.osgi.framework.SynchronousBundleListener; 035 036/** 037 * @author Bogdan Stefanescu 038 * @author Ian Smith 039 * @author Florent Guillaume 040 */ 041public class OSGiComponentLoader implements SynchronousBundleListener { 042 043 private static final Log log = LogFactory.getLog(OSGiComponentLoader.class); 044 045 private final OSGiRuntimeService runtime; 046 047 public OSGiComponentLoader(OSGiRuntimeService runtime) { 048 this.runtime = runtime; 049 install(); 050 } 051 052 public void install() { 053 BundleContext ctx = runtime.getBundleContext(); 054 ctx.addBundleListener(this); 055 Bundle[] bundles = ctx.getBundles(); 056 int mask = Bundle.STARTING | Bundle.ACTIVE; 057 for (Bundle bundle : bundles) { 058 String name = bundle.getSymbolicName(); 059 runtime.bundles.put(name, bundle); 060 int state = bundle.getState(); 061 bundleDebug("Install bundle: %s " + bundleStateAsString(state), name); 062 if ((state & mask) != 0) { // check only resolved bundles 063 if (OSGiRuntimeService.getComponentsList(bundle) != null) { 064 bundleDebug("Install bundle: %s component list: " + OSGiRuntimeService.getComponentsList(bundle), 065 name); 066 // check only bundles containing nuxeo comp. 067 try { 068 runtime.createContext(bundle); 069 } catch (RuntimeException e) { 070 // don't raise this exception, 071 // we want to isolate bundle errors from other bundles 072 log.warn("Failed to load components for bundle: " + name, e); 073 } 074 } else { 075 bundleDebug("Install bundle: %s has no components", name); 076 } 077 } else { 078 bundleDebug("Install bundle: %s is not STARTING " + "or ACTIVE, so no context was created", name); 079 } 080 } 081 } 082 083 public void uninstall() { 084 runtime.getBundleContext().removeBundleListener(this); 085 } 086 087 @Override 088 public void bundleChanged(BundleEvent event) { 089 String name = event.getBundle().getSymbolicName(); 090 int type = event.getType(); 091 092 bundleDebug("Bundle changed: %s " + bundleEventAsString(type), name); 093 try { 094 Bundle bundle = event.getBundle(); 095 String componentsList = OSGiRuntimeService.getComponentsList(bundle); 096 switch (type) { 097 case BundleEvent.INSTALLED: 098 runtime.bundles.put(bundle.getSymbolicName(), bundle); 099 break; 100 case BundleEvent.UNINSTALLED: 101 runtime.bundles.remove(bundle.getSymbolicName()); 102 break; 103 case BundleEvent.STARTING: 104 case BundleEvent.LAZY_ACTIVATION: 105 if (componentsList != null) { 106 bundleDebug("Bundle changed: %s STARTING with components: " + componentsList, name); 107 runtime.createContext(bundle); 108 } else { 109 bundleDebug("Bundle changed: %s STARTING with no components", name); 110 } 111 break; 112 case BundleEvent.STOPPED: 113 case BundleEvent.UNRESOLVED: 114 if (componentsList != null) { 115 bundleDebug("Bundle changed: %s STOPPING with components: " + componentsList, name); 116 runtime.destroyContext(bundle); 117 } else { 118 bundleDebug("Bundle changed: %s STOPPING with no components", name); 119 } 120 break; 121 } 122 } catch (RuntimeException e) { 123 log.error(e, e); 124 } 125 } 126 127 /** 128 * Used for generating good debug info. Convert bit vector into printable string. 129 * 130 * @param state bitwise-or of UNINSTALLED, INSTALLED, RESOLVED, STARTING, STOPPING, and ACTIVE 131 * @return printable version of bits that are on 132 */ 133 public static String bundleStateAsString(int state) { 134 List<String> list = new LinkedList<String>(); 135 if ((state & Bundle.UNINSTALLED) != 0) { 136 list.add("UNINSTALLED"); 137 } 138 if ((state & Bundle.INSTALLED) != 0) { 139 list.add("INSTALLED"); 140 } 141 if ((state & Bundle.RESOLVED) != 0) { 142 list.add("RESOLVED"); 143 } 144 if ((state & Bundle.STARTING) != 0) { 145 list.add("STARTING"); 146 } 147 if ((state & Bundle.STOPPING) != 0) { 148 list.add("STOPPING"); 149 } 150 if ((state & Bundle.ACTIVE) != 0) { 151 list.add("ACTIVE"); 152 } 153 return '[' + StringUtils.join(list, ',') + ']'; 154 } 155 156 /** 157 * Used for generating good debug info. Convert event type into printable string. 158 * 159 * @param eventType INSTALLED, STARTED,STOPPED, UNINSTALLED,UPDATED 160 * @return printable version of event type 161 */ 162 public static String bundleEventAsString(int eventType) { 163 switch (eventType) { 164 case BundleEvent.INSTALLED: 165 return "INSTALLED"; 166 case BundleEvent.STARTED: 167 return "STARTED"; 168 case BundleEvent.STARTING: 169 return "STARTING"; 170 case BundleEvent.STOPPED: 171 return "STOPPED"; 172 case BundleEvent.UNINSTALLED: 173 return "UNINSTALLED"; 174 case BundleEvent.UPDATED: 175 return "UPDATED"; 176 case BundleEvent.LAZY_ACTIVATION: 177 return "LAZY_ACTIVATION"; 178 case BundleEvent.RESOLVED: 179 return "RESOLVED"; 180 case BundleEvent.UNRESOLVED: 181 return "UNRESOLVED"; 182 case BundleEvent.STOPPING: 183 return "STOPPING"; 184 default: 185 return "UNKNOWN_OSGI_EVENT_TYPE_" + eventType; 186 } 187 } 188 189 /** 190 * Prints out a debug message for debugging bundles. 191 * 192 * @param msg the debug message with a %s in it which will be replaced by the component name 193 * @param name the component name 194 */ 195 public static void bundleDebug(String msg, String name) { 196 if (log.isDebugEnabled()) { 197 try { 198 msg = String.format(msg, name); 199 } catch (IllegalFormatException e) { 200 // don't fail for this 201 } 202 log.debug(msg); 203 } 204 } 205 206}