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