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