001/* 002 * (C) Copyright 2006-2020 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 * Nuxeo - initial API and implementation 018 * Anahide Tchertchian 019 */ 020package org.nuxeo.runtime.model.impl; 021 022import static java.nio.charset.StandardCharsets.UTF_8; 023 024import java.io.ByteArrayInputStream; 025import java.io.IOException; 026import java.io.InputStream; 027import java.net.URL; 028import java.util.ArrayList; 029import java.util.List; 030 031import org.apache.commons.io.IOUtils; 032import org.apache.commons.lang3.StringUtils; 033import org.apache.logging.log4j.LogManager; 034import org.apache.logging.log4j.Logger; 035import org.nuxeo.runtime.RuntimeService; 036import org.nuxeo.runtime.RuntimeServiceException; 037import org.nuxeo.runtime.api.Framework; 038import org.nuxeo.runtime.model.ComponentManager; 039import org.nuxeo.runtime.model.ComponentName; 040import org.nuxeo.runtime.model.RegistrationInfo; 041import org.nuxeo.runtime.model.RuntimeContext; 042import org.nuxeo.runtime.model.StreamRef; 043import org.nuxeo.runtime.model.URLStreamRef; 044import org.nuxeo.runtime.osgi.OSGiRuntimeActivator; 045import org.nuxeo.runtime.osgi.OSGiRuntimeContext; 046import org.osgi.framework.Bundle; 047 048/** 049 * New behavior @since 9.2 As the runtime lifecycle changed there make no sense to unregister components by their own. 050 * <p> 051 * The component registry is either reset to a clean state or shutdown. 052 * <p> 053 * So methods like unregister by location used in tests make no sense. These methods are preserved yet for compatibility 054 * with some tests but may be removed in future. 055 * <p> 056 * Also when a context is destroyed we unregister all the components the context deployed. 057 * <p> 058 * This is also deprecated since the unregister component is deprecated too. 059 * <p> 060 * The code inside destroy method was removed too since the deployedFiles map was removed. 061 * <p> 062 * This map posed problems with the registry snapshot approaches since it was not kept in sync with the registry. 063 * <p> 064 * <p> 065 * If unregistering components will be removed completely don't forget to remove 066 * {@link ComponentManager#unregisterByLocation(String)} and the {@link ComponentRegistry#deployedFiles}. 067 * <p> 068 * <p> 069 * Features like studio or IDE which needs unregistering components must use their own mechanism. 070 * 071 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 072 */ 073public class DefaultRuntimeContext implements RuntimeContext { 074 075 private static final Logger log = LogManager.getLogger(DefaultRuntimeContext.class); 076 077 protected RuntimeService runtime; 078 079 /** 080 * The list of component names deployed by this context. 081 * 082 * @since 9.2 083 */ 084 protected List<ComponentName> components; 085 086 protected final ComponentDescriptorReader reader; 087 088 public DefaultRuntimeContext() { 089 this(Framework.getRuntime()); 090 } 091 092 public DefaultRuntimeContext(RuntimeService runtime) { 093 this.runtime = runtime; 094 this.components = new ArrayList<>(); 095 reader = new ComponentDescriptorReader(); 096 } 097 098 public void setRuntime(RuntimeService runtime) { 099 this.runtime = runtime; 100 } 101 102 @Override 103 public RuntimeService getRuntime() { 104 return runtime; 105 } 106 107 @Override 108 public ComponentName[] getComponents() { 109 return components.toArray(new ComponentName[0]); 110 } 111 112 @Override 113 public URL getResource(String name) { 114 return Thread.currentThread().getContextClassLoader().getResource(name); 115 } 116 117 @Override 118 public URL getLocalResource(String name) { 119 return Thread.currentThread().getContextClassLoader().getResource(name); 120 } 121 122 @Override 123 public Class<?> loadClass(String className) throws ClassNotFoundException { 124 return Thread.currentThread().getContextClassLoader().loadClass(className); 125 } 126 127 @Override 128 public RegistrationInfo deploy(URL url) throws IOException { 129 return deploy(new URLStreamRef(url)); 130 } 131 132 @Override 133 public RegistrationInfo deploy(StreamRef ref) throws IOException { 134 String name = ref.getId(); 135 RegistrationInfoImpl ri = createRegistrationInfo(ref); 136 log.debug("Deploying component from url {}", name); 137 ri.sourceId = name; 138 ri.context = this; 139 ri.xmlFileUrl = ref.asURL(); 140 if (ri.getBundle() != null) { 141 // this is an external component XML. 142 // should use the real owner bundle as the context. 143 Bundle bundle = OSGiRuntimeActivator.getInstance().getBundle(ri.getBundle()); 144 if (bundle != null) { 145 ri.context = new OSGiRuntimeContext(bundle); 146 } 147 } 148 runtime.getComponentManager().register(ri); 149 components.add(ri.getName()); 150 return ri; 151 } 152 153 @Override 154 public void undeploy(URL url) { 155 runtime.getComponentManager().unregisterByLocation(url.toString()); 156 } 157 158 @Override 159 public void undeploy(StreamRef ref) { 160 runtime.getComponentManager().unregisterByLocation(ref.getId()); 161 } 162 163 @Override 164 public boolean isDeployed(URL url) { 165 return runtime.getComponentManager().hasComponentFromLocation(url.toString()); 166 } 167 168 @Override 169 public boolean isDeployed(StreamRef ref) { 170 return runtime.getComponentManager().hasComponentFromLocation(ref.getId()); 171 } 172 173 @Override 174 public RegistrationInfo deploy(String location) { 175 URL url = getLocalResource(location); 176 if (url == null) { 177 throw new IllegalArgumentException("No local resources was found with this name: " + location); 178 } 179 try { 180 return deploy(url); 181 } catch (IOException e) { 182 throw new RuntimeServiceException("Cannot deploy: " + location, e); 183 } 184 } 185 186 @Override 187 public void undeploy(String location) { 188 URL url = getLocalResource(location); 189 if (url == null) { 190 throw new IllegalArgumentException("No local resources was found with this name: " + location); 191 } 192 undeploy(url); 193 } 194 195 @Override 196 public boolean isDeployed(String location) { 197 URL url = getLocalResource(location); 198 if (url != null) { 199 return isDeployed(url); 200 } else { 201 log.warn("No local resources was found with this name: {}", location); 202 return false; 203 } 204 } 205 206 @Override 207 public void destroy() { 208 ComponentManager mgr = runtime.getComponentManager(); 209 for (ComponentName cname : components) { 210 mgr.unregister(cname); 211 } 212 } 213 214 @Override 215 public Bundle getBundle() { 216 return null; 217 } 218 219 public RegistrationInfoImpl createRegistrationInfo(StreamRef ref) throws IOException { 220 String source; 221 try (InputStream stream = ref.getStream()) { 222 source = IOUtils.toString(stream, UTF_8); 223 } 224 if (StringUtils.isBlank(source)) { 225 throw new IOException("Empty registration from " + ref.getId()); 226 } 227 String expanded = Framework.expandVars(source); 228 RegistrationInfoImpl ri; 229 try (InputStream in = new ByteArrayInputStream(expanded.getBytes())) { 230 ri = createRegistrationInfo(in); 231 } catch (IOException e) { 232 throw new IOException( 233 String.format("Could not resolve registration from %s (%s)", ref.getId(), e.getMessage()), e); 234 } 235 if (ri == null || ri.getName() == null) { 236 throw new IOException("Could not resolve registration from " + ref.getId()); 237 } 238 return ri; 239 } 240 241 public RegistrationInfoImpl createRegistrationInfo(InputStream in) throws IOException { 242 return reader.read(this, in); 243 } 244 245}