/**
* Copyright 2010 JBoss Inc
*
* 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.drools.osgi.test.utils;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import org.apache.log4j.Logger;
import org.osgi.framework.Constants;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
/**
* Find a packaged and unpackaged artifact from Eclipse Workspace and Eclipse Target areas.
* Does not use groupId to resolve bundles.
*
* @author Frederic Conrotte
*/
public class EclipseArtifactFinder {
private static final Logger log = Logger.getLogger(EclipseArtifactFinder.class);
private static final String s_PROP_WORKSPACE_AREA = "eclipse.workspace.area";
private static final String s_PROP_TARGET_AREA = "eclipse.target.area";
private Set<Plugin> m_WorkspacePlugins = new HashSet<Plugin>();
private Set<Plugin> m_TargetPlugins = new HashSet<Plugin>();
private static final String FILE_SCHEME = "file:///";
private static final String REFERENCE_PROTOCOL = "reference";
private final FileFilter m_DirectoryFilter = new FileFilter() {
public boolean accept(File file) {
return file.isDirectory();
}
};
private final FileFilter m_JARFileFilter = new FileFilter() {
public boolean accept(File file) {
String fileExtension = getExtension(file.getName());
return file.isFile() && "jar".equals(fileExtension);
}
private String getExtension(String fileName) {
String ext;
int dotPlace = fileName.lastIndexOf('.');
if (dotPlace >= 0)
ext = fileName.substring( dotPlace + 1 );
else
ext = "";
return ext;
}
};
private final FileFilter m_ManifestDirectoryFilter = new FileFilter() {
public boolean accept(File file) {
return file.isDirectory() && "META-INF".equalsIgnoreCase(file.getName());
}
};
private FilenameFilter m_ManifestFilter = new FilenameFilter(){
public boolean accept(File file, String aName) {
return "MANIFEST.MF".equals(aName);
}
};
/**
* Quick model for an Eclipse plugin
*/
private class Plugin
{
private String m_BundleSymbolicName, m_BundleVersion;
private Resource m_Path;
private boolean m_IsExploded;
public Plugin(String aBundleSymbolicName, String aVersion, Resource aPath, boolean isExploded)
{
m_BundleSymbolicName = aBundleSymbolicName;
m_BundleVersion = aVersion;
m_Path = aPath;
m_IsExploded = isExploded;
}
public boolean isExploded() {
return m_IsExploded;
}
public boolean match(String artifactId, String version)
{
return m_BundleSymbolicName.equals(artifactId) && m_BundleVersion.startsWith(version);
}
public String getBundleSymbolicName() {
return m_BundleSymbolicName;
}
public void setBundleSymbolicName(String aBundleSymbolicName) {
m_BundleSymbolicName = aBundleSymbolicName;
}
public String getBundleVersion() {
return m_BundleVersion;
}
public void setBundleVersion(String aBundleVersion) {
m_BundleVersion = aBundleVersion;
}
public Resource getPath() {
return m_Path;
}
public void setPath(Resource aPath) {
m_Path = aPath;
}
@Override
public boolean equals(Object aOther) {
if (aOther instanceof Plugin == false)
return false;
if (this == aOther)
return true;
Plugin rhs = (Plugin) aOther;
return m_BundleSymbolicName.equals(rhs.getBundleSymbolicName()) && m_BundleVersion.equals(rhs.getBundleVersion()) && m_Path.equals(rhs.getPath());
}
@Override
public int hashCode() {
return m_BundleSymbolicName.hashCode() + m_BundleVersion.hashCode() + m_Path.hashCode();
}
@Override
public String toString() {
return m_BundleSymbolicName + " " + m_BundleVersion;
}
}
public Resource findArtifact(String aArtifactId, String aVersion) throws IOException {
if (m_WorkspacePlugins.isEmpty()) {
File folder = getEclipseWorkspace();
if (folder != null)
importPluginFromFolder(folder, m_WorkspacePlugins);
}
if (m_TargetPlugins.isEmpty()) {
File[] folders = getEclipseTarget();
for (File folder : folders)
importPluginFromFolder(folder, m_TargetPlugins);
}
for (Plugin plugin : m_WorkspacePlugins) {
if (plugin.match(aArtifactId, aVersion))
{
if (plugin.isExploded())
return getExplodedPluginResource(plugin);
else
return getJARPluginResource(plugin);
}
}
for (Plugin plugin : m_TargetPlugins) {
if (plugin.match(aArtifactId, aVersion))
{
if (plugin.isExploded())
return getExplodedPluginResource(plugin);
else
return getJARPluginResource(plugin);
}
}
return null;
}
private void importPluginFromFolder(File folder, Set<Plugin> plugins) throws IOException {
log.info("Importing plugins from folder " + folder.getAbsolutePath());
Set<Resource> eclipseProjects = new HashSet<Resource>();
// Scan plugins exploded as unpacked JAR directories
for (File projectFolder : folder.listFiles(m_DirectoryFilter)) {
eclipseProjects.add(new FileSystemResource(projectFolder));
}
for (Resource resource : eclipseProjects) {
Manifest man = getManifestFromProject(resource);
if (man != null) {
addPlugin(plugins, resource, man, true);
} else {
// this is not a project folder, so descend to find potential nested modules
List<String> list = Arrays.asList( ((FileSystemResource)resource).getFile().list() );
for ( String str : list ) {
// do not process eclipse proejcts at the moment
if ( str.contains( "drools-eclipse" ) || str.contains( "osgi-bundles" ) ) {
list = null;
}
}
if ( list != null && list.contains("pom.xml")) {
importPluginFromFolder(((FileSystemResource)resource).getFile(), plugins);
}
}
}
Set<Resource> packagedBundles = new HashSet<Resource>();
// Scan plugins provided as JAR files
for (File jarFile : folder.listFiles(m_JARFileFilter))
packagedBundles.add(new FileSystemResource(jarFile));
for (Resource resource : packagedBundles) {
Manifest man = getManifestFromJAR(resource);
if (man != null)
addPlugin(plugins, resource, man, false);
}
}
private void addPlugin(Set<Plugin> plugins, Resource resource, Manifest man, boolean isExploded) {
// read the manifest
Attributes attrs = man.getMainAttributes();
String symbolicName = attrs.getValue(Constants.BUNDLE_SYMBOLICNAME);
if (symbolicName != null)
{
symbolicName = symbolicName.replace("singleton:=true", "");
symbolicName = symbolicName.trim().replace(";", "");
String version = attrs.getValue(Constants.BUNDLE_VERSION);
if (symbolicName != null && version != null)
plugins.add(new Plugin(symbolicName, version, resource, isExploded));
}
}
private Resource getJARPluginResource(Plugin aPlugin) throws IOException {
return new FileSystemResource(aPlugin.getPath().getFile());
}
private Resource getExplodedPluginResource(Plugin plugin) throws IOException {
URL url = new URL(REFERENCE_PROTOCOL, null, FILE_SCHEME + plugin.getPath().getFile().getCanonicalPath() + File.separator + "target" + File.separator + "classes");
return new UnpackedOSGiBundleResource(url);
}
/**
* Return an Eclipse project's Manifest
* @param aResource an Eclipse project resource path
* @return The project Manifest, null if none exist
* @throws IOException
*/
private Manifest getManifestFromProject(Resource aResource) throws IOException {
try {
for (File manifestFolder : aResource.getFile().listFiles(m_ManifestDirectoryFilter)) {
for (File manifestFile : manifestFolder.listFiles(m_ManifestFilter))
return new Manifest(new FileInputStream(manifestFile));
}
} catch (IOException aEx) {
log.error("Problem reading MANIFEST.MF from resource" + aResource.getFilename());
throw aEx;
}
return null;
}
/**
* Return an Eclipse bundle's JAR Manifest
* @param aResource an Eclipse JAR path
* @return The JAR Manifest
* @throws IOException
*/
private Manifest getManifestFromJAR(Resource aResource) throws IOException {
JarFile jar = new JarFile(aResource.getFile());
return jar.getManifest();
}
private File getEclipseWorkspace() {
String workspaceAreaProp = System.getProperty(s_PROP_WORKSPACE_AREA, "../..");
try {
System.out.println( "workspace area: " + new File(workspaceAreaProp).getCanonicalPath() );
} catch ( IOException e ) {
throw new RuntimeException("Unable to set path");
}
if (workspaceAreaProp != null)
return new File(workspaceAreaProp);
else
return null;
}
private File[] getEclipseTarget() {
List<File> result = new ArrayList<File>();
Properties props = System.getProperties();
for (Iterator<Object> iterator = props.keySet().iterator(); iterator.hasNext();) {
String prop = (String) iterator.next();
if (prop.startsWith(s_PROP_TARGET_AREA))
{
System.out.println( "target area: " + System.getProperty(prop) );
File f = new File(System.getProperty(prop));
if ( !f.isDirectory() ) {
throw new IllegalStateException(s_PROP_TARGET_AREA + " not set.");
}
result.add(f);
}
}
if (result.isEmpty()) {
File f = new File( "../plugins" );
if ( !f.isDirectory() ) {
throw new IllegalStateException(s_PROP_TARGET_AREA + " not set.");
}
result.add( f );
}
if (result.size() == 0)
throw new IllegalStateException(s_PROP_TARGET_AREA + " not set.");
else
return result.toArray(new File[]{});
}
}