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 <kleturc@nuxeo.com> 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 // check only resolved bundles 061 if ((state & mask) != 0) { 062 // check only bundles containing nuxeo components 063 if (OSGiRuntimeService.getComponentsList(bundle) != null) { 064 log.debug("Install bundle: {} component list: {}", name, 065 OSGiRuntimeService.getComponentsList(bundle)); 066 try { 067 runtime.createContext(bundle); 068 } catch (RuntimeException e) { 069 // only log error to keep on deploying other bundles 070 log.error(e, 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 // only log error to keep on deploying other bundles 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}