blob: 00f7c862e1c857a5a339e02305e9eabf12f3cda2 [file] [log] [blame]
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.felix.atomos.utils.core;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UncheckedIOException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.StringTokenizer;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.apache.felix.atomos.utils.api.Context;
import org.apache.felix.atomos.utils.api.FileType;
import org.apache.felix.atomos.utils.api.Launcher;
import org.apache.felix.atomos.utils.api.RegisterServiceCall;
import org.apache.felix.atomos.utils.api.plugin.BundleActivatorPlugin;
import org.apache.felix.atomos.utils.api.plugin.ClassPlugin;
import org.apache.felix.atomos.utils.api.plugin.ComponentDescription;
import org.apache.felix.atomos.utils.api.plugin.ComponentMetaDataPlugin;
import org.apache.felix.atomos.utils.api.plugin.FileCollectorPlugin;
import org.apache.felix.atomos.utils.api.plugin.FileHandlerPlugin;
import org.apache.felix.atomos.utils.api.plugin.FinalPlugin;
import org.apache.felix.atomos.utils.api.plugin.JarPlugin;
import org.apache.felix.atomos.utils.api.plugin.MethodPlugin;
import org.apache.felix.atomos.utils.api.plugin.RegisterServicepPlugin;
import org.apache.felix.atomos.utils.api.plugin.SubstratePlugin;
import org.apache.felix.atomos.utils.core.scr.mock.EmptyBundeLogger;
import org.apache.felix.atomos.utils.core.scr.mock.PathBundle;
import org.apache.felix.scr.impl.logger.BundleLogger;
import org.apache.felix.scr.impl.metadata.ComponentMetadata;
import org.apache.felix.scr.impl.xml.XmlHandler;
import org.osgi.framework.Constants;
public class LauncherImpl implements Launcher
{
public static List<Class<?>> loadClasses(List<Path> paths, URLClassLoader cl)
{
return paths.stream().map(p -> {
try
{
return new JarFile(p.toFile());
}
catch (IOException e)
{
throw new UncheckedIOException(e);
}
}).flatMap(j -> j.stream()).filter(e -> !e.isDirectory()).filter(
e -> e.getName().endsWith(".class")).filter(
e -> !e.getName().endsWith("module-info.class")).map(e -> {
try
{
String name = e.getName().replace("/", ".").substring(0,
e.getName().length() - 6);
return cl.loadClass(name);
}
catch (NoClassDefFoundError | ClassNotFoundException e1)
{
// happened when incomplete classpath
}
return null;
}).filter(Objects::nonNull).collect(Collectors.toList());
}
private static List<ComponentDescription> readComponentDescription(JarFile jar)
throws Exception
{
BundleLogger logger = new EmptyBundeLogger();
List<ComponentMetadata> list = new ArrayList<>();
Attributes attributes = jar.getManifest().getMainAttributes();
String descriptorLocations = attributes.getValue("Service-Component");// ComponentConstants.SERVICE_COMPONENT);
if (descriptorLocations != null)
{
StringTokenizer st = new StringTokenizer(descriptorLocations, ", ");
while (st.hasMoreTokens())
{
String descriptorLocation = st.nextToken();
try (
InputStream stream = jar.getInputStream(
jar.getEntry(descriptorLocation));
BufferedReader in = new BufferedReader(
new InputStreamReader(stream, "UTF-8")))
{
XmlHandler handler = new XmlHandler(new PathBundle(jar), logger, true,
true);
final SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
final SAXParser parser = factory.newSAXParser();
parser.parse(stream, handler);
list.addAll(handler.getComponentMetadataList());
}
}
}
List<ComponentDescription> cds = list.stream().map(cmd -> {
cmd.validate();
return new ComponentDescriptionImpl(cmd);
}).collect(Collectors.toList());
return cds;
}
private Collection<SubstratePlugin<?>> plugins = null;
private LauncherImpl()
{
}
LauncherImpl(Collection<SubstratePlugin<?>> plugins)
{
this();
this.plugins = plugins;
}
@Override
public Context execute()
{
return execute(new ContextImpl());
}
@Override
public Context execute(Context context)
{
//CollectFiles
orderdPluginsBy(FileCollectorPlugin.class)//
.peek(System.out::println)//
.forEachOrdered(plugin -> plugin.collectFiles(context));//
//Visit all files with type
orderdPluginsBy(FileHandlerPlugin.class)//
.peek(System.out::println)//
.forEachOrdered(plugin -> {
//for each FileType
List.of(FileType.values()).forEach(fileType -> {
context.getFiles(fileType)//
.forEach(path -> plugin.handleFile(context, path, fileType));
});
});//
List<Path> artifacts = context.getFiles(FileType.ARTIFACT).collect(
Collectors.toList());
URL[] urls = artifacts.stream().map(p -> {
try
{
return p.toUri().toURL();
}
catch (MalformedURLException e1)
{
throw new UncheckedIOException(e1);
}
}).toArray(URL[]::new);
List<JarFile> jarFiles = new ArrayList<>();
try (URLClassLoader classLoader = URLClassLoader.newInstance(urls, null))
{
List<Class<?>> classes = loadClasses(artifacts, classLoader);
List<JarPlugin<?>> jarPlugins = new ArrayList<>();//collector had compile issues on ojdk compiler
orderdPluginsBy(JarPlugin.class).forEachOrdered(jarPlugins::add);
jarPlugins.forEach(plugin -> plugin.preJars(context));
for (Path p : artifacts)
{
jarFiles.add(new JarFile(p.toFile()));
}
jarFiles.forEach(
j -> jarPlugins.forEach(p -> p.initJar(j, context, classLoader)));
jarFiles.forEach(j -> {
jarPlugins.forEach(p -> p.doJar(j, context, classLoader));
try
{
processJar(j, context, classLoader);
}
catch (IOException e)
{
e.printStackTrace();
}
});
jarPlugins.forEach(plugin -> plugin.postJars(context));
List<ClassPlugin<?>> classPlugins = new ArrayList<>();
orderdPluginsBy(ClassPlugin.class).forEachOrdered(classPlugins::add);
List<MethodPlugin<?>> methodPlugins = new ArrayList<>();
orderdPluginsBy(MethodPlugin.class).forEachOrdered(methodPlugins::add);
if (!classPlugins.isEmpty() || !methodPlugins.isEmpty())
{
for (Class<?> c : classes)
{
classPlugins.forEach(p -> p.doClass(c, context));
if (!methodPlugins.isEmpty())
{
try
{
Method[] methods = c.getDeclaredMethods();
if (methods != null)
{
for (Method m : methods)
{
methodPlugins.forEach(p -> p.doMethod(m, context));
}
}
}
catch (NoClassDefFoundError e)
{
//e.printStackTrace(); //TODO Log
System.out.println("incomplete classpath: " + c);
}
}
}
}
orderdPluginsBy(FinalPlugin.class).forEachOrdered(p -> p.doFinal(context));
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
jarFiles.forEach(f -> {
try
{
f.close();
}
catch (IOException e)
{
// ignore
}
});
}
return context;
}
private void processJar(JarFile j, Context context, URLClassLoader classLoader)
throws IOException
{
Attributes attributes = j.getManifest().getMainAttributes();
String bundleActivatorClassName = attributes.getValue(
org.osgi.framework.Constants.BUNDLE_ACTIVATOR);
if (bundleActivatorClassName == null)
{
bundleActivatorClassName = attributes.getValue(
Constants.EXTENSION_BUNDLE_ACTIVATOR);
}
if (bundleActivatorClassName != null)
{
Class<?> bundleActivatorClass;
try
{
bundleActivatorClass = classLoader.loadClass(
bundleActivatorClassName.trim());
orderdPluginsBy(BundleActivatorPlugin.class)//
.peek(System.out::println)//
.forEachOrdered(plugin -> plugin.doBundleActivator(
bundleActivatorClass, context, classLoader));
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
}
try
{
List<ComponentMetaDataPlugin<?>> cmdP = new ArrayList<>();
orderdPluginsBy(ComponentMetaDataPlugin.class).forEachOrdered(cmdP::add);
List<ComponentDescription> cds = readComponentDescription(j);
for (ComponentDescription cd : cds)
{
cmdP.forEach(plugin -> {
plugin.doComponentMetaData(cd, context, classLoader);
});
}
}
catch (Exception e)
{
e.printStackTrace();
}
List<RegisterServicepPlugin<?>> rscP = new ArrayList<>();//
orderdPluginsBy(RegisterServicepPlugin.class).forEachOrdered(rscP::add);
List<RegisterServiceCall> rscs = context.getRegisterServiceCalls();
for (RegisterServiceCall rsc : rscs)
{
rscP.forEach(plugin -> {
plugin.doRegisterServiceCall(rsc, context, classLoader);
});
}
}
List<SubstratePlugin<?>> getPlugins()
{
return List.copyOf(plugins);
}
private <T extends SubstratePlugin<?>> Stream<T> orderdPluginsBy(Class<T> clazz)
{
return plugins.stream()//
.filter(clazz::isInstance)//
.map(clazz::cast)//
.sorted((p1, p2) -> p1.ranking(clazz) - p2.ranking(clazz));
}
}