| /* |
| * 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)); |
| } |
| } |