blob: 2c0a44e379d2da9f8b8f754d2e8785f3b70349b0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to You 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.sling.feature.launcher.extensions.connect.impl;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.CodeSource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import org.apache.sling.feature.Artifact;
import org.apache.sling.feature.ArtifactId;
import org.apache.sling.feature.Feature;
import org.apache.sling.feature.io.IOUtils;
import org.apache.sling.feature.launcher.impl.launchers.FrameworkLauncher;
import org.apache.sling.feature.launcher.spi.LauncherPrepareContext;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleReference;
import org.osgi.framework.Constants;
import org.osgi.framework.FrameworkUtil;
public class PojoSRLauncher extends FrameworkLauncher
{
@Override
public void prepare(LauncherPrepareContext context, ArtifactId frameworkId, Feature app) throws Exception
{
context.addAppJar(context.getArtifactFile(frameworkId));
String filter = "";
if (frameworkId.getArtifactId().equals("org.apache.felix.connect"))
{
for (List<Artifact> bundles : app.getBundles().getBundlesByStartOrder().values())
{
for (Artifact artifact : bundles)
{
URL url = context.getArtifactFile(artifact.getId());
try (JarFile jar = IOUtils.getJarFileFromURL(url, true, null)) {
Manifest mf = jar.getManifest();
String bsn = mf.getMainAttributes() != null ? mf.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME) : null;
if (bsn != null)
{
String cp = mf.getMainAttributes().getValue(Constants.BUNDLE_CLASSPATH);
if (cp != null)
{
for (String entry : cp.split(","))
{
int idx = entry.indexOf(';');
if (idx != -1)
{
entry = entry.substring(0, idx);
}
entry = entry.trim();
if (!entry.isEmpty() && !entry.equals("."))
{
JarEntry content = jar.getJarEntry(entry);
if (content != null && !content.isDirectory())
{
try
{
String urlString = "jar:" + IOUtils.getFileFromURL(getJarFileURL(url, content), true, null).toURI().toURL() + "!/";
context.addAppJar(new URL(urlString));
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
}
else
{
context.addAppJar(url);
filter += "(" + Constants.BUNDLE_SYMBOLICNAME + "=" + bsn + ")";
}
}
}
else
{
context.addAppJar(url);
filter += "(" + Constants.BUNDLE_SYMBOLICNAME + "=" + bsn + ")";
}
}
else {
context.addAppJar(url);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
if (!filter.isEmpty()) {
filter = "(|" + filter + ")";
app.getFrameworkProperties().put("pojosr.filter", filter);
}
}
}
private URL getJarFileURL(URL jar, JarEntry entry) throws Exception {
String urlString = jar.toExternalForm();
if (jar.getProtocol().equals("jar")) {
return new URL(urlString + (urlString.endsWith("!/") ? entry.getName() : "!/" + entry.getName()));
}
else {
return new URL("jar:" + urlString + "!/" + entry.getName());
}
}
@Override
protected String getFrameworkRunnerClass()
{
return PojoSRRunner.class.getName();
}
@Override
public LauncherClassLoader createClassLoader()
{
LauncherClassLoader cl = new PojoSRLauncherClassLoader();
cl.addURL(PojoSRLauncher.class.getProtectionDomain().getCodeSource().getLocation());
return cl;
}
private class PojoSRLauncherClassLoader extends LauncherClassLoader implements BundleReference {
@Override
public Bundle getBundle()
{
try
{
return (Bundle) loadClass(FrameworkUtil.class.getName()).getField("BUNDLE").get(null);
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException
{
return (name.startsWith("org.osgi.framework.") && !name.startsWith(FrameworkUtil.class.getName())) ||
name.startsWith("org.osgi.resource.") ?
PojoSRLauncher.class.getClassLoader().loadClass(name) :
define(name);
}
private Class define(String name) throws ClassNotFoundException
{
URL resource = findResource(name.replace(".", "/" ) + ".class");
if (resource != null) {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try (InputStream input = resource.openStream() ) {
byte[] b = new byte[64 * 1024];
for (int i = input.read(b);i != -1; i = input.read(b)) {
buffer.write(b, 0, i);
}
return super.defineClass(name, buffer.toByteArray(),0, buffer.size(),
new CodeSource(new URL(resource.toExternalForm().substring(0, resource.toExternalForm().lastIndexOf("!/") + 2)), (java.security.cert.Certificate[]) null));
}
catch (IOException e)
{
throw new ClassNotFoundException(e.getMessage());
}
}
else {
throw new ClassNotFoundException(name);
}
}
public void indexURLs()
{
if (init.compareAndSet(false, true))
{
for (URL url : super.getURLs())
{
try (JarFile jar = IOUtils.getJarFileFromURL(url, true, null))
{
for (Enumeration<JarEntry> entries = jar.entries(); entries.hasMoreElements(); )
{
JarEntry entry = entries.nextElement();
String[] paths;
if (entry.isDirectory())
{
paths = new String[]{entry.getName(), entry.getName().substring(0, entry.getName().length() - 1),
"/" + entry.getName(), "/" + entry.getName().substring(0, entry.getName().length() - 1)};
}
else
{
paths = new String[]{entry.getName(), "/" + entry.getName()};
}
for (String path : paths)
{
resourcesIDX.compute(path, (key, value) ->
{
if (value == null)
{
value = new ArrayList<>();
}
try
{
value.add(getJarFileURL(url, entry));
}
catch (Exception e)
{
e.printStackTrace();
}
return value;
});
}
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
private volatile AtomicBoolean init = new AtomicBoolean(false);
private final ConcurrentHashMap<String, List<URL>> resourcesIDX = new ConcurrentHashMap<String, List<URL>>();
@Override
public URL findResource(String name)
{
indexURLs();
List<URL> urls = resourcesIDX.getOrDefault(name, Collections.emptyList());
return urls.isEmpty() ? null : urls.get(0);
}
@Override
public Enumeration<URL> findResources(String name) throws IOException
{
indexURLs();
return Collections.enumeration(resourcesIDX.getOrDefault(name, Collections.emptyList()));
}
}
}