diff options
Diffstat (limited to 'sca-java-2.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main')
7 files changed, 1745 insertions, 0 deletions
diff --git a/sca-java-2.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/java/org/apache/tuscany/sca/tools/maven/compiler/ClassLoaderNameEnvironment.java b/sca-java-2.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/java/org/apache/tuscany/sca/tools/maven/compiler/ClassLoaderNameEnvironment.java new file mode 100644 index 0000000000..4b3f84edf4 --- /dev/null +++ b/sca-java-2.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/java/org/apache/tuscany/sca/tools/maven/compiler/ClassLoaderNameEnvironment.java @@ -0,0 +1,195 @@ +/* + * 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.tuscany.sca.tools.maven.compiler; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; +import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; +import org.eclipse.jdt.internal.compiler.env.INameEnvironment; +import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; + +/** + * An implementation of INameEnvironment based on a ClassLoader. + * + * @version $Rev: $ $Date: $ + */ +class ClassLoaderNameEnvironment implements INameEnvironment { + private final static char fileSeparator = System.getProperty("file.separator").charAt(0); + + private ClassLoader classLoader; + private List<String> sourceLocations; + private Map<String, File> sourceFiles; + + ClassLoaderNameEnvironment(ClassLoader classLoader, List<String> sourceLocations) { + this.classLoader = classLoader; + this.sourceLocations = sourceLocations; + sourceFiles = new HashMap<String, File>(); + } + + public NameEnvironmentAnswer findType(char[][] compoundTypeName) { + StringBuffer className = new StringBuffer(); + for (char[] name: compoundTypeName) { + if (className.length() != 0) { + className.append('.'); + } + className.append(name); + } + return nameAnswer(className.toString()); + } + + public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName) { + StringBuffer className = new StringBuffer(); + for (char[] name: packageName) { + if (className.length() != 0) { + className.append('.'); + } + className.append(name); + } + if (className.length() != 0) { + className.append('.'); + } + className.append(typeName); + return nameAnswer(className.toString()); + } + + public boolean isPackage(char[][] parentPackageName, char[] packageName) { + StringBuffer fullPackageName = new StringBuffer(); + if (parentPackageName != null) { + for (char[] name: parentPackageName) { + if (fullPackageName.length() != 0) { + fullPackageName.append('.'); + } + fullPackageName.append(name); + } + } + if (fullPackageName.length() != 0) { + fullPackageName.append('.'); + } + fullPackageName.append(packageName); + return isPackage(fullPackageName.toString()); + } + + public void cleanup() { + } + + /** + * Returns the source file for the given class name. + * + * @param className + * @return + */ + private File sourceFile(String className) { + File sourceFile = (File)sourceFiles.get(className); + if (sourceFile != null) { + return sourceFile; + } + String sourceName = className.replace('.', fileSeparator) + ".java"; + sourceFile = sourceFileInSourceLocations(sourceName); + sourceFiles.put(className, sourceFile); + return sourceFile; + } + + /** + * Returns the source file for the given source path relative to the source locations. + * + * @param className + * @return + */ + private File sourceFileInSourceLocations(String relativePath) { + for (String sourceLocation : sourceLocations) { + File sourceFile = new File(sourceLocation, relativePath); + if (sourceFile.exists()) { + return sourceFile; + } + } + return null; + } + + /** + * Returns true if the given name is a package name. + * + * @param name + * @return + */ + private boolean isPackage(String name) { + if (sourceFile(name) != null) { + return false; + } + String resourceName = '/' + name.replace('.', '/') + ".class"; + InputStream is = classLoader.getResourceAsStream(resourceName); + if (is == null) { + return true; + } else { + try { + is.close(); + } catch (IOException e) {} + return false; + } + } + + /** + * Find the NameAnswer for by the given class name. + * + * @param className + * @return + */ + private NameEnvironmentAnswer nameAnswer(String className) { + try { + File sourceFile = sourceFile(className); + if (sourceFile != null) { + ICompilationUnit compilationUnit = new FileCompilationUnit(sourceFile.getAbsolutePath(), className); + return new NameEnvironmentAnswer(compilationUnit, null); + } + + String resourceName = className.replace('.', '/') + ".class"; + InputStream is = classLoader.getResourceAsStream(resourceName); + if (is == null) { + return null; + } + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + byte[] b = new byte[2048]; + for (;;) { + int n = is.read(b); + if (n <= 0) { + break; + } + bos.write(b, 0, n); + } + byte[] classBytes = bos.toByteArray(); + + ClassFileReader classFileReader = new ClassFileReader(classBytes, className.toCharArray(), true); + return new NameEnvironmentAnswer(classFileReader, null); + + } catch (IOException e) { + throw new IllegalArgumentException(e); + } catch (ClassFormatException e) { + throw new IllegalArgumentException(e); + } + } + +} diff --git a/sca-java-2.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/java/org/apache/tuscany/sca/tools/maven/compiler/CompilerRequestor.java b/sca-java-2.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/java/org/apache/tuscany/sca/tools/maven/compiler/CompilerRequestor.java new file mode 100644 index 0000000000..3df4b4d602 --- /dev/null +++ b/sca-java-2.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/java/org/apache/tuscany/sca/tools/maven/compiler/CompilerRequestor.java @@ -0,0 +1,114 @@ +/* + * 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.tuscany.sca.tools.maven.compiler; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.List; + +import org.codehaus.plexus.compiler.CompilerError; +import org.eclipse.jdt.core.compiler.IProblem; +import org.eclipse.jdt.internal.compiler.ClassFile; +import org.eclipse.jdt.internal.compiler.CompilationResult; +import org.eclipse.jdt.internal.compiler.ICompilerRequestor; + +class CompilerRequestor implements ICompilerRequestor { + private String outputDirectory; + private boolean showWarnings; + private List<CompilerError> compilerErrors; + + public CompilerRequestor(String outputDirectory, boolean showWarnings, List<CompilerError> compilerErrors) { + this.outputDirectory = outputDirectory; + this.showWarnings = showWarnings; + this.compilerErrors = compilerErrors; + } + + public void acceptResult(CompilationResult result) { + boolean hasErrors = false; + if (result.hasProblems()) { + + // Convert JDT IProblems into plexus CompilerErrors + for (IProblem problem: result.getProblems()) { + if (problem.isWarning()) { + if (showWarnings) { + compilerErrors.add(new CompilerError(new String(problem.getOriginatingFileName()), + false, + problem.getSourceLineNumber(), + problem.getSourceStart(), + problem.getSourceLineNumber(), + problem.getSourceEnd(), + problem.getMessage())); + } + + } else if (problem.isError()) { + hasErrors = true; + compilerErrors.add(new CompilerError(new String(problem.getOriginatingFileName()), + true, + problem.getSourceLineNumber(), + problem.getSourceStart(), + problem.getSourceLineNumber(), + problem.getSourceEnd(), + problem.getMessage())); + + } + } + } + + // Write the class files + if (!hasErrors) { + ClassFile[] classFiles = result.getClassFiles(); + for (ClassFile classFile: classFiles) { + + // Create file and parent directories + StringBuffer className = new StringBuffer(); + for (char[] name: classFile.getCompoundName()) { + if (className.length() != 0) { + className.append('.'); + } + className.append(name); + } + File file = new File(outputDirectory, className.toString().replace('.', '/') + ".class"); + if (!file.getParentFile().exists()) { + file.getParentFile().mkdirs(); + } + + // Write class file contents + FileOutputStream fos = null; + try { + fos = new FileOutputStream(file); + fos.write(classFile.getBytes()); + } catch (FileNotFoundException e) { + throw new IllegalArgumentException(e); + } catch (IOException e) { + throw new IllegalArgumentException(e); + } finally { + if (fos != null) { + try { + fos.close(); + } catch (IOException e) {} + } + } + } + } + } + +}
\ No newline at end of file diff --git a/sca-java-2.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/java/org/apache/tuscany/sca/tools/maven/compiler/FileCompilationUnit.java b/sca-java-2.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/java/org/apache/tuscany/sca/tools/maven/compiler/FileCompilationUnit.java new file mode 100644 index 0000000000..08ed7f034e --- /dev/null +++ b/sca-java-2.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/java/org/apache/tuscany/sca/tools/maven/compiler/FileCompilationUnit.java @@ -0,0 +1,88 @@ +/* + * 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.tuscany.sca.tools.maven.compiler; + +import java.io.CharArrayWriter; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.StringTokenizer; + +import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; + +/** + * An implementation of ICompilationUnit that wraps a File. + * + * @version $Rev: $ $Date: $ + */ +class FileCompilationUnit implements ICompilationUnit { + private final static char fileSeparator = System.getProperty("file.separator").charAt(0); + private String className; + private String sourceFile; + + FileCompilationUnit(String sourceFile, String className) { + this.className = className; + this.sourceFile = sourceFile; + } + + public char[] getContents() { + try { + InputStreamReader reader = new InputStreamReader(new FileInputStream(sourceFile)); + CharArrayWriter writer = new CharArrayWriter(); + char[] b = new char[2048]; + for (;;) { + int n = reader.read(b); + if (n <= 0) { + break; + } + writer.write(b, 0, n); + } + return writer.toCharArray(); + + } catch (FileNotFoundException e) { + throw new IllegalArgumentException(e); + } catch (IOException e) { + throw new IllegalArgumentException(e); + } + } + + public char[] getFileName() { + return (className.replace('.', fileSeparator) + ".java").toCharArray(); + } + + public char[] getMainTypeName() { + int dot = className.lastIndexOf('.'); + if (dot > 0) { + return className.substring(dot + 1).toCharArray(); + } + return className.toCharArray(); + } + + public char[][] getPackageName() { + StringTokenizer tokens = new StringTokenizer(className, "."); + char[][] packageName = new char[tokens.countTokens() - 1][]; + for (int i = 0; i < packageName.length; i++) { + String token = tokens.nextToken(); + packageName[i] = token.toCharArray(); + } + return packageName; + } +}
\ No newline at end of file diff --git a/sca-java-2.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/java/org/apache/tuscany/sca/tools/maven/compiler/JavaCompiler.java b/sca-java-2.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/java/org/apache/tuscany/sca/tools/maven/compiler/JavaCompiler.java new file mode 100644 index 0000000000..f628a38e95 --- /dev/null +++ b/sca-java-2.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/java/org/apache/tuscany/sca/tools/maven/compiler/JavaCompiler.java @@ -0,0 +1,212 @@ +/* + * 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.tuscany.sca.tools.maven.compiler; + +import static org.codehaus.plexus.compiler.CompilerOutputStyle.ONE_OUTPUT_FILE_PER_INPUT_FILE; +import static org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies.proceedWithAllProblems; +import static org.eclipse.jdt.internal.compiler.impl.CompilerOptions.GENERATE; +import static org.eclipse.jdt.internal.compiler.impl.CompilerOptions.IGNORE; +import static org.eclipse.jdt.internal.compiler.impl.CompilerOptions.OPTION_Encoding; +import static org.eclipse.jdt.internal.compiler.impl.CompilerOptions.OPTION_LineNumberAttribute; +import static org.eclipse.jdt.internal.compiler.impl.CompilerOptions.OPTION_LocalVariableAttribute; +import static org.eclipse.jdt.internal.compiler.impl.CompilerOptions.OPTION_ReportDeprecation; +import static org.eclipse.jdt.internal.compiler.impl.CompilerOptions.OPTION_Source; +import static org.eclipse.jdt.internal.compiler.impl.CompilerOptions.OPTION_SourceFileAttribute; +import static org.eclipse.jdt.internal.compiler.impl.CompilerOptions.OPTION_TargetPlatform; +import static org.eclipse.jdt.internal.compiler.impl.CompilerOptions.WARNING; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import org.apache.tuscany.sca.tools.maven.compiler.osgi.BundleResolver; +import org.apache.tuscany.sca.tools.maven.compiler.osgi.BundleUtil; +import org.codehaus.plexus.compiler.AbstractCompiler; +import org.codehaus.plexus.compiler.CompilerConfiguration; +import org.codehaus.plexus.compiler.CompilerError; +import org.codehaus.plexus.compiler.CompilerException; +import org.eclipse.jdt.internal.compiler.Compiler; +import org.eclipse.jdt.internal.compiler.ICompilerRequestor; +import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; +import org.eclipse.jdt.internal.compiler.env.INameEnvironment; +import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; +import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; +import org.eclipse.osgi.service.resolver.BundleDescription; +import org.osgi.framework.BundleException; + +/** + * A custom Plexus Java compiler plugin that uses the Eclipse compiler. + * + * @version $Rev: $ $Date: $ + */ +public class JavaCompiler extends AbstractCompiler { + + public JavaCompiler() { + super(ONE_OUTPUT_FILE_PER_INPUT_FILE, ".java", ".class", null); + } + + public List<CompilerError> compile(CompilerConfiguration configuration) throws CompilerException { + BundleResolver stateController = new BundleResolver(getLogger()); + + getLogger().info("Invoking Tuscany Eclipse JDT compiler"); + + List<URL> urls = new ArrayList<URL>(); + try { + for (String entry : (List<String>)configuration.getClasspathEntries()) { + urls.add(new File(entry).toURI().toURL()); + } + } catch (MalformedURLException e) { + throw new CompilerException(e.getMessage(), e); + } + + ClassLoader classLoader = new URLClassLoader(urls.toArray(new URL[urls.size()])); + + // Determine compiler configuration + Map<String, String> settings = new HashMap<String, String>(); + String sourceVersion = configuration.getSourceVersion(); + if (sourceVersion != null && sourceVersion.length() != 0) { + settings.put(OPTION_Source, sourceVersion); + } + String targetVersion = configuration.getTargetVersion(); + if (targetVersion != null && targetVersion.length() != 0) { + settings.put(OPTION_TargetPlatform, targetVersion); + } + settings.put(OPTION_LineNumberAttribute, GENERATE); + settings.put(OPTION_SourceFileAttribute, GENERATE); + if (configuration.isDebug()) { + settings.put(OPTION_LocalVariableAttribute, GENERATE); + } + if (configuration.getSourceEncoding() != null && !(configuration.getSourceEncoding().length() == 0)) { + settings.put(OPTION_Encoding, configuration.getSourceEncoding()); + } + if (configuration.isShowDeprecation()) { + settings.put(OPTION_ReportDeprecation, WARNING); + } else { + settings.put(OPTION_ReportDeprecation, IGNORE); + } + + // Create a compiler + List<CompilerError> compilerErrors = new ArrayList<CompilerError>(); + INameEnvironment nameEnvironment = + new ClassLoaderNameEnvironment(classLoader, configuration.getSourceLocations()); + ICompilerRequestor requestor = + new CompilerRequestor(configuration.getOutputLocation(), configuration.isShowWarnings(), compilerErrors); + Compiler compiler = + new Compiler(nameEnvironment, proceedWithAllProblems(), new CompilerOptions(settings), requestor, + new DefaultProblemFactory(Locale.getDefault())); + + // Create compilation units for the source files + List<FileCompilationUnit> compilationUnits = new ArrayList<FileCompilationUnit>(); + + // Go over the input source locations + List<String> sourceLocations = (List<String>)configuration.getSourceLocations(); + for (String sourceLocation : sourceLocations) { + + // Exclude nested source locations + List<String> excludeLocations = new ArrayList<String>(); + for (String nestedLocation : sourceLocations) { + if (nestedLocation != sourceLocation && nestedLocation.startsWith(sourceLocation)) { + excludeLocations.add(nestedLocation); + } + } + + // List source files in each source location + for (String sourceFile : (Set<String>)getSourceFilesForSourceRoot(configuration, sourceLocation)) { + + // Exclude files from excluded nested locations + boolean excluded = false; + for (String excludeLocation : excludeLocations) { + if (sourceFile.startsWith(excludeLocation)) { + excluded = true; + } + } + if (!excluded) { + + // Create a compilation unit for the source file + FileCompilationUnit compilationUnit = + new FileCompilationUnit(sourceFile, makeClassName(sourceFile, sourceLocation)); + compilationUnits.add(compilationUnit); + } + } + } + + // Compile all the compilation units + getLogger().info("Compiling " + compilationUnits.size() + " to " + configuration.getOutputLocation()); + compiler.compile((ICompilationUnit[])compilationUnits.toArray(new ICompilationUnit[compilationUnits.size()])); + + // getLogger().info(configuration.getCustomCompilerArguments().toString()); + boolean osgi = "true".equals(configuration.getCustomCompilerArguments().get("-osgi")); + File proj = new File(configuration.getOutputLocation()); + + String symbol = null; + try { + // Don't trigger the resolution for test-compile + if (!"test-classes".equals(proj.getName())) { + symbol = BundleUtil.getBundleSymbolicName(proj); + } + } catch (IOException e1) { + symbol = null; + } + + if (osgi && symbol != null) { + getLogger().info("Resolving OSGi bundle: "+symbol); + for (String entry : (List<String>)configuration.getClasspathEntries()) { + try { + File cp = new File(entry); + if (cp.exists()) { + stateController.addBundle(cp); + } + } catch (BundleException e) { + getLogger().error(e.getMessage(), e); + } + } + + stateController.resolveState(); + BundleDescription b = stateController.getBundleDescription(proj); + if (b != null) { + try { + stateController.assertResolved(b); + getLogger().info("OSGi bundle is resolved: " + b.getSymbolicName()); + } catch (BundleException e) { + stateController.analyzeErrors(b); + if(getLogger().isDebugEnabled()) { + getLogger().debug(stateController.reportErrors(b)); + } + // FIXME: For now, only a warning is reported + // throw new CompilerException(e.getMessage(), e); + } + } + } + + return compilerErrors; + } + + public String[] createCommandLine(CompilerConfiguration config) throws CompilerException { + return null; + } +} diff --git a/sca-java-2.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/java/org/apache/tuscany/sca/tools/maven/compiler/osgi/BundleResolver.java b/sca-java-2.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/java/org/apache/tuscany/sca/tools/maven/compiler/osgi/BundleResolver.java new file mode 100644 index 0000000000..06f26f7e65 --- /dev/null +++ b/sca-java-2.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/java/org/apache/tuscany/sca/tools/maven/compiler/osgi/BundleResolver.java @@ -0,0 +1,490 @@ +/* + * 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.tuscany.sca.tools.maven.compiler.osgi; + +import static org.eclipse.osgi.service.resolver.ResolverError.IMPORT_PACKAGE_USES_CONFLICT; +import static org.eclipse.osgi.service.resolver.ResolverError.MISSING_FRAGMENT_HOST; +import static org.eclipse.osgi.service.resolver.ResolverError.MISSING_IMPORT_PACKAGE; +import static org.eclipse.osgi.service.resolver.ResolverError.MISSING_REQUIRE_BUNDLE; +import static org.eclipse.osgi.service.resolver.ResolverError.REQUIRE_BUNDLE_USES_CONFLICT; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Dictionary; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.logging.Logger; +import org.eclipse.osgi.service.resolver.BundleDescription; +import org.eclipse.osgi.service.resolver.BundleSpecification; +import org.eclipse.osgi.service.resolver.ExportPackageDescription; +import org.eclipse.osgi.service.resolver.HostSpecification; +import org.eclipse.osgi.service.resolver.ImportPackageSpecification; +import org.eclipse.osgi.service.resolver.ResolverError; +import org.eclipse.osgi.service.resolver.State; +import org.eclipse.osgi.service.resolver.StateObjectFactory; +import org.eclipse.osgi.service.resolver.VersionConstraint; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleException; +import org.osgi.framework.Constants; + +public class BundleResolver { + private static final String PROP_MAVEN_PROJECT = "MavenProject"; + private static final String PROP_MANIFEST = "BundleManifest"; + + private StateObjectFactory factory = StateObjectFactory.defaultFactory; + private State state; + private long id = 0; + private Logger logger; + + public static BundleDescription[] getDependentBundles(BundleDescription root) { + if (root == null) + return new BundleDescription[0]; + BundleDescription[] imported = getImportedBundles(root); + BundleDescription[] required = getRequiredBundles(root); + BundleDescription[] dependents = new BundleDescription[imported.length + required.length]; + System.arraycopy(imported, 0, dependents, 0, imported.length); + System.arraycopy(required, 0, dependents, imported.length, required.length); + return dependents; + } + + public static BundleDescription[] getImportedBundles(BundleDescription root) { + if (root == null) + return new BundleDescription[0]; + ExportPackageDescription[] packages = root.getResolvedImports(); + List<BundleDescription> resolvedImports = new ArrayList<BundleDescription>(packages.length); + for (int i = 0; i < packages.length; i++) + if (!root.getLocation().equals(packages[i].getExporter().getLocation()) && !resolvedImports + .contains(packages[i].getExporter())) + resolvedImports.add(packages[i].getExporter()); + return (BundleDescription[])resolvedImports.toArray(new BundleDescription[resolvedImports.size()]); + } + + public static BundleDescription[] getRequiredBundles(BundleDescription root) { + if (root == null) + return new BundleDescription[0]; + return root.getResolvedRequires(); + } + + public BundleResolver(Logger logger) { + this.logger = logger; + this.state = factory.createState(true); + Properties props = new Properties(); + props.putAll(System.getProperties()); + BundleUtil.loadVMProfile(props); + state.setPlatformProperties(props); + URL url = Bundle.class.getProtectionDomain().getCodeSource().getLocation(); + + File osgi = toFile(url); + try { + addBundle(osgi); + } catch (BundleException e) { + throw new IllegalArgumentException(e); + } + } + + private long getNextId() { + return ++id; + } + + public BundleDescription addBundle(File bundleLocation) throws BundleException { + return addBundle(bundleLocation, false); + } + + public BundleDescription addBundle(File bundleLocation, boolean override) throws BundleException { + if (bundleLocation == null || !bundleLocation.exists()) + throw new IllegalArgumentException("bundleLocation not found: " + bundleLocation); + Dictionary manifest = loadManifestAttributes(bundleLocation); + if (manifest == null) { + // throw new BundleException("manifest not found in " + bundleLocation); + return null; + } + return addBundle(manifest, bundleLocation, override); + } + + public BundleDescription addBundle(File manifestLocation, File bundleLocation, boolean override) + throws BundleException { + if (bundleLocation == null || !bundleLocation.exists()) + throw new IllegalArgumentException("bundleLocation not found: " + bundleLocation); + Dictionary manifest = loadManifestAttributes(manifestLocation); + if (manifest == null) + throw new IllegalArgumentException("manifest not found in " + manifestLocation); + return addBundle(manifest, bundleLocation, override); + } + + private Dictionary loadManifestAttributes(File bundleLocation) { + Manifest m = loadManifest(bundleLocation); + if (m == null) { + return null; + } + + return manifestToProperties(m.getMainAttributes()); + } + + public Manifest loadManifest(File bundleLocation) { + try { + return BundleUtil.getManifest(bundleLocation); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + private Properties manifestToProperties(Attributes d) { + Iterator iter = d.keySet().iterator(); + Properties result = new Properties(); + while (iter.hasNext()) { + Attributes.Name key = (Attributes.Name)iter.next(); + result.put(key.toString(), d.get(key)); + } + return result; + } + + private BundleDescription addBundle(Dictionary enhancedManifest, File bundleLocation, boolean override) + throws BundleException { + + BundleDescription descriptor = + factory.createBundleDescription(state, enhancedManifest, bundleLocation.getAbsolutePath(), getNextId()); + + setUserProperty(descriptor, PROP_MANIFEST, enhancedManifest); + if (override) { + BundleDescription[] conflicts = state.getBundles(descriptor.getSymbolicName()); + if (conflicts != null) { + for (BundleDescription conflict : conflicts) { + state.removeBundle(conflict); + logger + .warn(conflict.toString() + " has been replaced by another bundle with the same symbolic name " + + descriptor.toString()); + } + } + } + + state.addBundle(descriptor); + return descriptor; + } + + public BundleDescription getResolvedBundle(String bundleId) { + BundleDescription[] description = state.getBundles(bundleId); + if (description == null) + return null; + for (int i = 0; i < description.length; i++) { + if (description[i].isResolved()) + return description[i]; + } + return null; + } + + public void resolveState() { + state.resolve(false); + + if (logger.isDebugEnabled()) { + StringBuilder sb = new StringBuilder("Resolved OSGi state\n"); + for (BundleDescription bundle : state.getBundles()) { + if (!bundle.isResolved()) { + sb.append("[X] "); + } else { + sb.append("[V] "); + } + sb.append(bundle).append(": (").append(bundle.getLocation()); + sb.append(")\n"); + for (ResolverError error : state.getResolverErrors(bundle)) { + sb.append(" ").append(error.toString()).append('\n'); + } + } + logger.debug(sb.toString()); + } + } + + public State getState() { + return state; + } + + public BundleDescription[] getBundles() { + return state.getBundles(); + } + + public ResolverError[] getResolverErrors(BundleDescription bundle) { + Set<ResolverError> errors = new LinkedHashSet<ResolverError>(); + getRelevantErrors(errors, bundle); + return (ResolverError[])errors.toArray(new ResolverError[errors.size()]); + } + + private void getRelevantErrors(Set<ResolverError> errors, BundleDescription bundle) { + ResolverError[] bundleErrors = state.getResolverErrors(bundle); + for (int j = 0; j < bundleErrors.length; j++) { + ResolverError error = bundleErrors[j]; + errors.add(error); + + VersionConstraint constraint = error.getUnsatisfiedConstraint(); + if (constraint instanceof BundleSpecification || constraint instanceof HostSpecification) { + BundleDescription[] requiredBundles = state.getBundles(constraint.getName()); + for (int i = 0; i < requiredBundles.length; i++) { + getRelevantErrors(errors, requiredBundles[i]); + } + } + } + } + + private void logError(BundleDescription bundle, int level, Object object) { + StringBuffer msg = new StringBuffer(); + for (int i = 0; i < level; i++) { + msg.append("--"); + } + msg.append("> [").append(bundle.getSymbolicName()).append("] "); + msg.append(object); + logger.error(msg.toString()); + } + + public void analyzeErrors(BundleDescription bundle) { + analyzeErrors(bundle, new HashSet<BundleDescription>(), 1); + } + + private void analyzeErrors(BundleDescription bundle, Set<BundleDescription> bundles, int level) { + if (bundles.contains(bundle)) { + return; + } + bundles.add(bundle); + ResolverError[] errors = state.getResolverErrors(bundle); + for (ResolverError error : errors) { + logError(bundle, level, error); + VersionConstraint constraint = error.getUnsatisfiedConstraint(); + switch (error.getType()) { + case MISSING_IMPORT_PACKAGE: + ImportPackageSpecification pkgSpec = (ImportPackageSpecification)constraint; + for (BundleDescription b : getBundles()) { + for (ExportPackageDescription pkg : b.getExportPackages()) { + if (pkg.getName().equals(pkgSpec.getName())) { + if (pkgSpec.getVersionRange().isIncluded(pkg.getVersion())) { + if (!pkg.getExporter().isResolved()) { + logError(b, level, "Bundle unresolved: " + pkg); + analyzeErrors(pkg.getExporter(), bundles, level + 1); + } + } else { + logError(b, level, "Version mismatch: " + pkgSpec + " " + pkg); + } + } + } + } + break; + case MISSING_REQUIRE_BUNDLE: + case MISSING_FRAGMENT_HOST: + // BundleSpecification bundleSpec = (BundleSpecification)constraint; + for (BundleDescription b : getBundles()) { + if (b == bundle) { + continue; + } + if (b.getSymbolicName().equals(constraint.getName())) { + if (constraint.getVersionRange().isIncluded(b.getVersion())) { + // There must be something wrong in the bundle + analyzeErrors(b, bundles, level); + } else { + logError(bundle, level, "Version mismatch: " + constraint + " " + b); + } + } + } + break; + case IMPORT_PACKAGE_USES_CONFLICT: + case REQUIRE_BUNDLE_USES_CONFLICT: + default: + logger.error(reportErrors(bundle)); + break; + } + } + + } + + public Set<ResolverError> getAllErrors() { + BundleDescription[] bundles = state.getBundles(); + Set<ResolverError> errors = new LinkedHashSet<ResolverError>(); + for (int i = 0; i < bundles.length; i++) { + BundleDescription bundle = bundles[i]; + ResolverError[] bundleErrors = state.getResolverErrors(bundle); + if (bundleErrors != null) { + errors.addAll(Arrays.asList(bundleErrors)); + } + } + return errors; + } + + public List<BundleDescription> getDependencies(BundleDescription desc) { + Set<Long> bundleIds = new LinkedHashSet<Long>(); + addBundleAndDependencies(desc, bundleIds, true); + List<BundleDescription> dependencies = new ArrayList<BundleDescription>(); + for (long bundleId : bundleIds) { + if (desc.getBundleId() != bundleId) { + BundleDescription dependency = state.getBundle(bundleId); + BundleDescription supplier = dependency.getSupplier().getSupplier(); + HostSpecification host = supplier.getHost(); + if (host == null || !desc.equals(host.getSupplier())) { + dependencies.add(dependency); + } + } + } + return dependencies; + } + + /** + * Code below is copy&paste from org.eclipse.pde.internal.core.DependencyManager + * which seems to calculate runtime dependencies. In particular, it adds + * fragments' dependencies to the host bundle (see TychoTest#testFragment unit test). + * This may or may not cause problems... + * + * RequiredPluginsClasspathContainer#computePluginEntries has the logic to + * calculate compile-time dependencies in IDE. + * + * TODO find the code used by PDE/Build + */ + private static void addBundleAndDependencies(BundleDescription desc, Set<Long> bundleIds, boolean includeOptional) { + if (desc != null && bundleIds.add(new Long(desc.getBundleId()))) { + BundleSpecification[] required = desc.getRequiredBundles(); + for (int i = 0; i < required.length; i++) { + if (includeOptional || !required[i].isOptional()) + addBundleAndDependencies((BundleDescription)required[i].getSupplier(), bundleIds, includeOptional); + } + ImportPackageSpecification[] importedPkgs = desc.getImportPackages(); + for (int i = 0; i < importedPkgs.length; i++) { + ExportPackageDescription exporter = (ExportPackageDescription)importedPkgs[i].getSupplier(); + // Continue if the Imported Package is unresolved of the package is optional and don't want optional packages + if (exporter == null || (!includeOptional && Constants.RESOLUTION_OPTIONAL.equals(importedPkgs[i] + .getDirective(Constants.RESOLUTION_DIRECTIVE)))) + continue; + addBundleAndDependencies(exporter.getExporter(), bundleIds, includeOptional); + } + BundleDescription[] fragments = desc.getFragments(); + for (int i = 0; i < fragments.length; i++) { + if (!fragments[i].isResolved()) + continue; + String id = fragments[i].getSymbolicName(); + if (!"org.eclipse.ui.workbench.compatibility".equals(id)) //$NON-NLS-1$ + addBundleAndDependencies(fragments[i], bundleIds, includeOptional); + } + HostSpecification host = desc.getHost(); + if (host != null) + addBundleAndDependencies((BundleDescription)host.getSupplier(), bundleIds, includeOptional); + } + } + + public BundleDescription getBundleDescription(MavenProject project) { + String location = project.getFile().getParentFile().getAbsolutePath(); + return state.getBundleByLocation(location); + } + + public BundleDescription getBundleDescription(File location) { + String absolutePath = location.getAbsolutePath(); + return state.getBundleByLocation(absolutePath); + } + + private static void setUserProperty(BundleDescription desc, String name, Object value) { + Object userObject = desc.getUserObject(); + + if (userObject != null && !(userObject instanceof Map)) { + throw new IllegalStateException("Unexpected user object " + desc.toString()); + } + + Map props = (Map)userObject; + if (props == null) { + props = new HashMap(); + desc.setUserObject(props); + } + + props.put(name, value); + } + + private static Object getUserProperty(BundleDescription desc, String name) { + Object userObject = desc.getUserObject(); + if (userObject instanceof Map) { + return ((Map)userObject).get(name); + } + return null; + } + + public MavenProject getMavenProject(BundleDescription desc) { + return (MavenProject)getUserProperty(desc, PROP_MAVEN_PROJECT); + } + + public void assertResolved(BundleDescription desc) throws BundleException { + if (!desc.isResolved()) { + throw new BundleException("Bundle cannot be resolved: " + desc); + } + } + + public String reportErrors(BundleDescription desc) { + StringBuffer msg = new StringBuffer(); + msg.append("Bundle ").append(desc.getSymbolicName()).append(" cannot be resolved: \n"); + BundleDescription[] bundles = state.getBundles(); + int index = 0; + for (BundleDescription b : bundles) { + if (b.isResolved()) { + continue; + } + ResolverError[] errors = state.getResolverErrors(b); + if (errors.length > 0) { + msg.append(" ").append("[").append(index++).append("] ").append(b.getSymbolicName()).append("\n"); + } + for (int i = 0; i < errors.length; i++) { + ResolverError error = errors[i]; + msg.append(" -->").append(error).append("\n"); + } + } + return msg.toString(); + } + + public String getManifestAttribute(BundleDescription desc, String attr) { + Dictionary mf = (Dictionary)getUserProperty(desc, PROP_MANIFEST); + if (mf != null) { + return (String)mf.get(attr); + } + return null; + } + + private static File toFile(URL url) { + if (url.getProtocol().equals("file") == false) { + return null; + } else { + String filename = url.getFile().replace('/', File.separatorChar).replace("%20", " "); + return new File(filename); + } + } + + /* + public static void main(String[] args) throws Exception { + BundleResolver resolver = new BundleResolver(new ConsoleLogger(Logger.LEVEL_INFO, "tuscany")); + + String home = System.getProperty("user.home"); + File jar = + new File(new File(home), + ".m2/repository/org/apache/tuscany/sca/tuscany-sca-api/1.4-EQUINOX-SNAPSHOT/tuscany-sca-api-1.4-EQUINOX-SNAPSHOT.jar"); + BundleDescription bundle = resolver.addBundle(jar); + resolver.resolveState(); + resolver.assertResolved(bundle); + } + */ +} diff --git a/sca-java-2.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/java/org/apache/tuscany/sca/tools/maven/compiler/osgi/BundleUtil.java b/sca-java-2.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/java/org/apache/tuscany/sca/tools/maven/compiler/osgi/BundleUtil.java new file mode 100644 index 0000000000..be8ee92fb6 --- /dev/null +++ b/sca-java-2.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/java/org/apache/tuscany/sca/tools/maven/compiler/osgi/BundleUtil.java @@ -0,0 +1,618 @@ +/* + * 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.tuscany.sca.tools.maven.compiler.osgi; + +import static org.osgi.framework.Constants.BUNDLE_CLASSPATH; +import static org.osgi.framework.Constants.BUNDLE_MANIFESTVERSION; +import static org.osgi.framework.Constants.BUNDLE_NAME; +import static org.osgi.framework.Constants.BUNDLE_SYMBOLICNAME; +import static org.osgi.framework.Constants.BUNDLE_VERSION; +import static org.osgi.framework.Constants.DYNAMICIMPORT_PACKAGE; +import static org.osgi.framework.Constants.EXPORT_PACKAGE; +import static org.osgi.framework.Constants.IMPORT_PACKAGE; + +import java.io.BufferedInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashSet; +import java.util.Properties; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.eclipse.osgi.framework.internal.core.Constants; +import org.eclipse.osgi.framework.internal.core.FrameworkProperties; +import org.eclipse.osgi.util.ManifestElement; +import org.osgi.framework.Version; + +/** + * Common functions used by the plugin. + * + * @version $Rev$ $Date$ + */ +public final class BundleUtil { + + /** + * Returns the name of a bundle, or null if the given file is not a bundle. + * + * @param file + * @return + * @throws IOException + */ + public static String getBundleSymbolicName(File file) throws IOException { + if (!file.exists()) { + return null; + } + String bundleName = null; + if (file.isDirectory()) { + File mf = new File(file, JarFile.MANIFEST_NAME); + if (!mf.isFile()) { + mf = new File(file, "../../" + JarFile.MANIFEST_NAME); + } + if (mf.isFile()) { + Manifest manifest = new Manifest(new FileInputStream(mf)); + bundleName = manifest.getMainAttributes().getValue(BUNDLE_SYMBOLICNAME); + } + } else { + JarFile jar = new JarFile(file, false); + Manifest manifest = jar.getManifest(); + bundleName = manifest.getMainAttributes().getValue(BUNDLE_SYMBOLICNAME); + jar.close(); + } + if (bundleName == null) { + return bundleName; + } + int sc = bundleName.indexOf(';'); + if (sc != -1) { + bundleName = bundleName.substring(0, sc); + } + return bundleName; + } + + static Manifest getManifest(File file) throws IOException { + if (!file.exists()) { + return null; + } + Manifest manifest = null; + String bundleName = null; + if (file.isDirectory()) { + File mf = new File(file, JarFile.MANIFEST_NAME); + if (!mf.isFile()) { + mf = new File(file, "../../" + JarFile.MANIFEST_NAME); + } + if (mf.isFile()) { + manifest = new Manifest(new FileInputStream(mf)); + bundleName = manifest.getMainAttributes().getValue(BUNDLE_SYMBOLICNAME); + } + } else { + JarFile jar = new JarFile(file, false); + manifest = jar.getManifest(); + bundleName = manifest.getMainAttributes().getValue(BUNDLE_SYMBOLICNAME); + jar.close(); + } + if (bundleName != null) { + return manifest; + } + + if (file.isFile()) { + Set<File> jars = new HashSet<File>(); + jars.add(file); + String name = file.getName(); + manifest = libraryManifest(jars, name, name, jarVersion(name), null); + } + return manifest; + } + + /** + * Generate a Bundle manifest for a set of JAR files. + * + * @param jarFiles + * @param name + * @param symbolicName + * @param version + * @param dir + * @return + * @throws IllegalStateException + */ + static Manifest libraryManifest(Set<File> jarFiles, String name, String symbolicName, String version, String dir) + throws IllegalStateException { + try { + + // List exported packages and bundle classpath entries + StringBuffer classpath = new StringBuffer(); + Set<String> exportedPackages = new HashSet<String>(); + for (File jarFile : jarFiles) { + addPackages(jarFile, exportedPackages, version); + if (dir != null) { + classpath.append(dir).append("/"); + classpath.append(jarFile.getName()); + } else { + classpath.append("\"external:"); + classpath.append(jarFile.getPath().replace(File.separatorChar, '/')); + classpath.append("\""); + } + classpath.append(","); + } + + // Generate export-package and import-package declarations + StringBuffer exports = new StringBuffer(); + StringBuffer imports = new StringBuffer(); + Set<String> importedPackages = new HashSet<String>(); + for (String export : exportedPackages) { + + // Add export declaration + exports.append(export); + exports.append(','); + + // Add corresponding import declaration + String packageName = packageName(export); + if (!importedPackages.contains(packageName)) { + importedPackages.add(packageName); + imports.append(packageName); + imports.append(','); + } + } + + // Create a manifest + Manifest manifest = new Manifest(); + Attributes attributes = manifest.getMainAttributes(); + attributes.putValue("Manifest-Version", "1.0"); + attributes.putValue(BUNDLE_MANIFESTVERSION, "2"); + attributes.putValue(BUNDLE_SYMBOLICNAME, symbolicName); + attributes.putValue(BUNDLE_NAME, name); + attributes.putValue(BUNDLE_VERSION, version); + attributes.putValue(DYNAMICIMPORT_PACKAGE, "*"); + if (exports.length() > 1) { + attributes.putValue(EXPORT_PACKAGE, exports.substring(0, exports.length() - 1)); + } + if (imports.length() > 1) { + attributes.putValue(IMPORT_PACKAGE, imports.substring(0, imports.length() - 1)); + } + if (classpath.length() > 1) { + attributes.putValue(BUNDLE_CLASSPATH, classpath.substring(0, classpath.length() - 1)); + } + + return manifest; + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + /** + * Write a bundle manifest. + * + * @param manifest + * @param out + * @throws IOException + */ + static void write(Manifest manifest, OutputStream out) throws IOException { + DataOutputStream dos = new DataOutputStream(out); + Attributes attributes = manifest.getMainAttributes(); + write(attributes, "Manifest-Version", dos); + write(attributes, BUNDLE_MANIFESTVERSION, dos); + write(attributes, BUNDLE_SYMBOLICNAME, dos); + write(attributes, BUNDLE_NAME, dos); + write(attributes, BUNDLE_VERSION, dos); + write(attributes, DYNAMICIMPORT_PACKAGE, dos); + write(attributes, BUNDLE_CLASSPATH, dos); + write(attributes, IMPORT_PACKAGE, dos); + write(attributes, EXPORT_PACKAGE, dos); + dos.flush(); + } + + /** + * Add packages to be exported out of a JAR file. + * + * @param jarFile + * @param packages + * @throws IOException + */ + private static void addPackages(File jarFile, Set<String> packages, String version) throws IOException { + if (getBundleSymbolicName(jarFile) == null) { + String ver = ";version=" + version; + addAllPackages(jarFile, packages, ver); + } else { + addExportedPackages(jarFile, packages); + } + } + + /** + * Write manifest attributes. + * + * @param attributes + * @param key + * @param dos + * @throws IOException + */ + private static void write(Attributes attributes, String key, DataOutputStream dos) throws IOException { + String value = attributes.getValue(key); + if (value == null) { + return; + } + StringBuffer line = new StringBuffer(); + line.append(key); + line.append(": "); + line.append(new String(value.getBytes("UTF8"))); + line.append("\r\n"); + int l = line.length(); + if (l > 72) { + for (int i = 70; i < l - 2;) { + line.insert(i, "\r\n "); + i += 72; + l += 3; + } + } + dos.writeBytes(line.toString()); + } + + /** + * Strip an OSGi export, only retain the package name and version. + * + * @param export + * @return + */ + private static String stripExport(String export) { + int sc = export.indexOf(';'); + if (sc == -1) { + return export; + } + String base = export.substring(0, sc); + int v = export.indexOf("version="); + if (v != -1) { + sc = export.indexOf(';', v + 1); + if (sc != -1) { + return base + ";" + export.substring(v, sc); + } else { + return base + ";" + export.substring(v); + } + } else { + return base; + } + } + + /** + * Add all the packages out of a JAR. + * + * @param jarFile + * @param packages + * @param version + * @throws IOException + */ + private static void addAllPackages(File jarFile, Set<String> packages, String version) throws IOException { + ZipInputStream is = new ZipInputStream(new FileInputStream(jarFile)); + ZipEntry entry; + while ((entry = is.getNextEntry()) != null) { + String entryName = entry.getName(); + if (!entry.isDirectory() && entryName != null + && entryName.length() > 0 + && !entryName.startsWith(".") + && entryName.endsWith(".class") // Exclude resources from Export-Package + && entryName.lastIndexOf("/") > 0 + && Character.isJavaIdentifierStart(entryName.charAt(0))) { + String pkg = entryName.substring(0, entryName.lastIndexOf("/")).replace('/', '.'); + if (!pkg.endsWith(".enum")) { + packages.add(pkg + version); + } + } + } + is.close(); + } + + /** + * Returns the name of the exported package in the given export. + * @param export + * @return + */ + private static String packageName(String export) { + int sc = export.indexOf(';'); + if (sc != -1) { + export = export.substring(0, sc); + } + return export; + } + + /** + * Add the packages exported by a bundle. + * + * @param file + * @param packages + * @return + * @throws IOException + */ + private static void addExportedPackages(File file, Set<String> packages) throws IOException { + if (!file.exists()) { + return; + } + + // Read the export-package declaration and get a list of the packages available in a JAR + Set<String> existingPackages = null; + String exports = null; + if (file.isDirectory()) { + File mf = new File(file, "META-INF/MANIFEST.MF"); + if (mf.isFile()) { + Manifest manifest = new Manifest(new FileInputStream(mf)); + exports = manifest.getMainAttributes().getValue(EXPORT_PACKAGE); + } + } else { + JarFile jar = new JarFile(file, false); + Manifest manifest = jar.getManifest(); + exports = manifest.getMainAttributes().getValue(EXPORT_PACKAGE); + jar.close(); + existingPackages = new HashSet<String>(); + addAllPackages(file, existingPackages, ""); + } + if (exports == null) { + return; + } + + // Parse the export-package declaration, and extract the individual packages + StringBuffer buffer = new StringBuffer(); + boolean q = false; + for (int i = 0, n = exports.length(); i < n; i++) { + char c = exports.charAt(i); + if (c == '\"') { + q = !q; + } + if (!q) { + if (c == ',') { + + // Add the exported package to the set, after making sure it really exists in + // the JAR + String export = buffer.toString(); + if (existingPackages == null || existingPackages.contains(packageName(export))) { + packages.add(stripExport(export)); + } + buffer = new StringBuffer(); + continue; + } + } + buffer.append(c); + } + if (buffer.length() != 0) { + + // Add the exported package to the set, after making sure it really exists in + // the JAR + String export = buffer.toString(); + if (existingPackages == null || existingPackages.contains(packageName(export))) { + packages.add(stripExport(export)); + } + } + } + + /** + * starting with -, then some digits, then . or - or _, then some digits again + * + */ + private static Pattern pattern = Pattern.compile("-(\\d)+((\\.|-|_)(\\d)+)*"); + + /** + * Returns the version number to use for the given JAR file. + * + * @param fileName + * @return + */ + static String jarVersion(String fileName) { + String name = fileName; + int index = name.lastIndexOf('.'); + if (index != -1) { + // Trim the extension + name = name.substring(0, index); + } + + Matcher matcher = pattern.matcher(name); + String version = "0.0.0"; + if (matcher.find()) { + version = matcher.group(); + version = version.substring(1); + } + return version; + } + + /** + * Convert the maven version into OSGi version + * @param mavenVersion + * @return + */ + static String osgiVersion(String mavenVersion) { + ArtifactVersion ver = new DefaultArtifactVersion(mavenVersion); + String qualifer = ver.getQualifier(); + if (qualifer != null) { + StringBuffer buf = new StringBuffer(qualifer); + for (int i = 0; i < buf.length(); i++) { + char c = buf.charAt(i); + if (Character.isLetterOrDigit(c) || c == '-' || c == '_') { + // Keep as-is + } else { + buf.setCharAt(i, '_'); + } + } + qualifer = buf.toString(); + } + Version osgiVersion = + new Version(ver.getMajorVersion(), ver.getMinorVersion(), ver.getIncrementalVersion(), qualifer); + String version = osgiVersion.toString(); + return version; + } + + private static String J2SE = "J2SE-"; + private static String JAVASE = "JavaSE-"; + private static String PROFILE_EXT = ".profile"; + + public static void loadVMProfile(Properties properties) { + Properties profileProps = findVMProfile(properties); + String systemExports = properties.getProperty(Constants.OSGI_FRAMEWORK_SYSTEM_PACKAGES); + // set the system exports property using the vm profile; only if the property is not already set + if (systemExports == null) { + systemExports = profileProps.getProperty(Constants.OSGI_FRAMEWORK_SYSTEM_PACKAGES); + if (systemExports != null) + properties.put(Constants.OSGI_FRAMEWORK_SYSTEM_PACKAGES, systemExports); + } + // set the org.osgi.framework.bootdelegation property according to the java profile + String type = properties.getProperty(Constants.OSGI_JAVA_PROFILE_BOOTDELEGATION); // a null value means ignore + String profileBootDelegation = profileProps.getProperty(Constants.OSGI_BOOTDELEGATION); + if (Constants.OSGI_BOOTDELEGATION_OVERRIDE.equals(type)) { + if (profileBootDelegation == null) + properties.remove(Constants.OSGI_BOOTDELEGATION); // override with a null value + else + properties.put(Constants.OSGI_BOOTDELEGATION, profileBootDelegation); // override with the profile value + } else if (Constants.OSGI_BOOTDELEGATION_NONE.equals(type)) + properties.remove(Constants.OSGI_BOOTDELEGATION); // remove the bootdelegation property in case it was set + // set the org.osgi.framework.executionenvironment property according to the java profile + if (properties.getProperty(Constants.FRAMEWORK_EXECUTIONENVIRONMENT) == null) { + // get the ee from the java profile; if no ee is defined then try the java profile name + String ee = + profileProps.getProperty(Constants.FRAMEWORK_EXECUTIONENVIRONMENT, profileProps + .getProperty(Constants.OSGI_JAVA_PROFILE_NAME)); + if (ee != null) + properties.put(Constants.FRAMEWORK_EXECUTIONENVIRONMENT, ee); + } + } + + private static Properties findVMProfile(Properties properties) { + Properties result = new Properties(); + // Find the VM profile name using J2ME properties + String j2meConfig = properties.getProperty(Constants.J2ME_MICROEDITION_CONFIGURATION); + String j2meProfiles = properties.getProperty(Constants.J2ME_MICROEDITION_PROFILES); + String vmProfile = null; + String javaEdition = null; + Version javaVersion = null; + if (j2meConfig != null && j2meConfig.length() > 0 && j2meProfiles != null && j2meProfiles.length() > 0) { + // save the vmProfile based off of the config and profile + // use the last profile; assuming that is the highest one + String[] j2meProfileList = ManifestElement.getArrayFromList(j2meProfiles, " "); + if (j2meProfileList != null && j2meProfileList.length > 0) + vmProfile = j2meConfig + '_' + j2meProfileList[j2meProfileList.length - 1]; + } else { + // No J2ME properties; use J2SE properties + // Note that the CDC spec appears not to require VM implementations to set the + // javax.microedition properties!! So we will try to fall back to the + // java.specification.name property, but this is pretty ridiculous!! + String javaSpecVersion = properties.getProperty("java.specification.version"); + // set the profile and EE based off of the java.specification.version + // TODO We assume J2ME Foundation and J2SE here. need to support other profiles J2EE ... + if (javaSpecVersion != null) { + StringTokenizer st = new StringTokenizer(javaSpecVersion, " _-"); + javaSpecVersion = st.nextToken(); + String javaSpecName = properties.getProperty("java.specification.name"); + if ("J2ME Foundation Specification".equals(javaSpecName)) + vmProfile = "CDC-" + javaSpecVersion + "_Foundation-" + javaSpecVersion; //$NON-NLS-2$ + else { + // look for JavaSE if 1.6 or greater; otherwise look for J2SE + Version v16 = new Version("1.6"); + javaEdition = J2SE; + try { + javaVersion = new Version(javaSpecVersion); + if (v16.compareTo(javaVersion) <= 0) + javaEdition = JAVASE; + } catch (IllegalArgumentException e) { + // do nothing + } + vmProfile = javaEdition + javaSpecVersion; + } + } + } + URL url = null; + // check for the java profile property for a url + String propJavaProfile = FrameworkProperties.getProperty(Constants.OSGI_JAVA_PROFILE); + if (propJavaProfile != null) + try { + // we assume a URL + url = new URL(propJavaProfile); + } catch (MalformedURLException e1) { + // try using a relative path in the system bundle + url = findInSystemBundle(propJavaProfile); + } + if (url == null && vmProfile != null) { + // look for a profile in the system bundle based on the vm profile + String javaProfile = vmProfile + PROFILE_EXT; + url = findInSystemBundle(javaProfile); + if (url == null) + url = getNextBestProfile(javaEdition, javaVersion); + } + if (url == null) + // the profile url is still null then use the osgi min profile in OSGi by default + url = findInSystemBundle("OSGi_Minimum-1.1.profile"); + if (url != null) { + InputStream in = null; + try { + in = url.openStream(); + result.load(new BufferedInputStream(in)); + } catch (IOException e) { + // TODO consider logging ... + } finally { + if (in != null) + try { + in.close(); + } catch (IOException ee) { + // do nothing + } + } + } + // set the profile name if it does not provide one + if (result.getProperty(Constants.OSGI_JAVA_PROFILE_NAME) == null) + if (vmProfile != null) + result.put(Constants.OSGI_JAVA_PROFILE_NAME, vmProfile.replace('_', '/')); + else + // last resort; default to the absolute minimum profile name for the framework + result.put(Constants.OSGI_JAVA_PROFILE_NAME, "OSGi/Minimum-1.1"); + return result; + } + + private static URL getNextBestProfile(String javaEdition, Version javaVersion) { + if (javaVersion == null || (javaEdition != J2SE && javaEdition != JAVASE)) + return null; // we cannot automatically choose the next best profile unless this is a J2SE or JavaSE vm + URL bestProfile = findNextBestProfile(javaEdition, javaVersion); + if (bestProfile == null && javaEdition == JAVASE) + // if this is a JavaSE VM then search for a lower J2SE profile + bestProfile = findNextBestProfile(J2SE, javaVersion); + return bestProfile; + } + + private static URL findNextBestProfile(String javaEdition, Version javaVersion) { + URL result = null; + int minor = javaVersion.getMinor(); + do { + result = findInSystemBundle(javaEdition + javaVersion.getMajor() + "." + minor + PROFILE_EXT); + minor = minor - 1; + } while (result == null && minor > 0); + return result; + } + + private static URL findInSystemBundle(String entry) { + ClassLoader loader = BundleUtil.class.getClassLoader(); + return loader == null ? ClassLoader.getSystemResource(entry) : loader.getResource(entry); + } + + +} diff --git a/sca-java-2.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/resources/META-INF/plexus/components.xml b/sca-java-2.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/resources/META-INF/plexus/components.xml new file mode 100644 index 0000000000..4332d3f205 --- /dev/null +++ b/sca-java-2.x/tags/2.0-M1/tools/maven/maven-eclipse-compiler/src/main/resources/META-INF/plexus/components.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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. +--> +<component-set> + <components> + <component> + <role>org.codehaus.plexus.compiler.Compiler</role> + <role-hint>tuscany-eclipse</role-hint> + <implementation>org.apache.tuscany.sca.tools.maven.compiler.JavaCompiler</implementation> + </component> + </components> +</component-set> |