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