Index: build.xml
===================================================================
--- build.xml (revision 1358274)
+++ build.xml (working copy)
@@ -58,6 +58,15 @@
+
+
+
+
+
+
+
+
+
Index: lucene/build.xml
===================================================================
--- lucene/build.xml (revision 1358274)
+++ lucene/build.xml (working copy)
@@ -174,6 +174,14 @@
+
+
+
+
+
+
+
+
Index: lucene/tools/build.xml
===================================================================
--- lucene/tools/build.xml (revision 1358274)
+++ lucene/tools/build.xml (working copy)
@@ -24,7 +24,11 @@
-
+
+
+
+
+
-
+
+
+
Index: lucene/tools/custom-tasks.xml
===================================================================
--- lucene/tools/custom-tasks.xml (revision 1358274)
+++ lucene/tools/custom-tasks.xml (working copy)
@@ -55,4 +55,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
Index: lucene/tools/forbiddenMethods/commons-io.txt
===================================================================
--- lucene/tools/forbiddenMethods/commons-io.txt (revision 0)
+++ lucene/tools/forbiddenMethods/commons-io.txt (working copy)
@@ -0,0 +1,20 @@
+# These methods from commons-io should not be used by Solr classes (unsafe, no charset,...):
+
+org.apache.commons.io.IOUtils#copy(java.io.InputStream,java.io.Writer)
+org.apache.commons.io.IOUtils#copy(java.io.Reader,java.io.OutputStream)
+org.apache.commons.io.IOUtils#readLines(java.io.InputStream)
+org.apache.commons.io.IOUtils#toByteArray(java.io.Reader)
+org.apache.commons.io.IOUtils#toByteArray(java.lang.String)
+org.apache.commons.io.IOUtils#toCharArray(java.io.InputStream)
+org.apache.commons.io.IOUtils#toInputStream(java.lang.CharSequence)
+org.apache.commons.io.IOUtils#toInputStream(java.lang.String)
+org.apache.commons.io.IOUtils#toString(byte[])
+org.apache.commons.io.IOUtils#toString(java.io.InputStream)
+org.apache.commons.io.IOUtils#toString(java.net.URI)
+org.apache.commons.io.IOUtils#toString(java.net.URL)
+org.apache.commons.io.IOUtils#write(byte[],java.io.Writer)
+org.apache.commons.io.IOUtils#write(char[],java.io.OutputStream)
+org.apache.commons.io.IOUtils#write(java.lang.CharSequence,java.io.OutputStream)
+org.apache.commons.io.IOUtils#write(java.lang.StringBuffer,java.io.OutputStream)
+org.apache.commons.io.IOUtils#write(java.lang.String,java.io.OutputStream)
+org.apache.commons.io.IOUtils#writeLines(java.util.Collection,java.lang.String,java.io.OutputStream)
Index: lucene/tools/forbiddenMethods/commons-io.txt
===================================================================
--- lucene/tools/forbiddenMethods/commons-io.txt (revision 0)
+++ lucene/tools/forbiddenMethods/commons-io.txt (working copy)
Property changes on: lucene/tools/forbiddenMethods/commons-io.txt
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+Date Author Id Revision HeadURL
\ No newline at end of property
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: lucene/tools/forbiddenMethods/jdk.txt
===================================================================
--- lucene/tools/forbiddenMethods/jdk.txt (revision 0)
+++ lucene/tools/forbiddenMethods/jdk.txt (working copy)
@@ -0,0 +1,20 @@
+# These methods should not be used by Lucene classes (unsafe, no charset,...):
+
+java.lang.String#(byte[])
+java.lang.String#(byte[],int)
+java.lang.String#(byte[],int,int)
+java.lang.String#(byte[],int,int)
+java.lang.String#getBytes()
+java.lang.String#toLowerCase()
+java.lang.String#toUpperCase()
+
+java.io.InputStreamReader#(java.io.InputStream)
+java.io.OutputStreamWriter#(java.io.OutputStream)
+java.io.FileReader#(java.io.File)
+java.io.FileReader#(java.io.FileDescriptor)
+java.io.FileReader#(java.lang.String)
+java.io.FileWriter#(java.io.File)
+java.io.FileWriter#(java.io.File,boolean)
+java.io.FileWriter#(java.io.FileDescriptor)
+java.io.FileWriter#(java.lang.String)
+java.io.FileWriter#(java.lang.String,boolean)
Index: lucene/tools/forbiddenMethods/jdk.txt
===================================================================
--- lucene/tools/forbiddenMethods/jdk.txt (revision 0)
+++ lucene/tools/forbiddenMethods/jdk.txt (working copy)
Property changes on: lucene/tools/forbiddenMethods/jdk.txt
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+Date Author Id Revision HeadURL
\ No newline at end of property
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: lucene/tools/ivy.xml
===================================================================
--- lucene/tools/ivy.xml (revision 1358274)
+++ lucene/tools/ivy.xml (working copy)
@@ -18,4 +18,8 @@
-->
+
+
+
+
Index: lucene/tools/lib
===================================================================
--- lucene/tools/lib (revision 0)
+++ lucene/tools/lib (working copy)
Property changes on: lucene/tools/lib
___________________________________________________________________
Added: svn:ignore
## -0,0 +1 ##
+*.jar
Index: lucene/tools/src/java/lucene-solr.antlib.xml
===================================================================
--- lucene/tools/src/java/lucene-solr.antlib.xml (revision 1358274)
+++ lucene/tools/src/java/lucene-solr.antlib.xml (working copy)
@@ -18,4 +18,7 @@
+
Index: lucene/tools/src/java/org/apache/lucene/validation/ForbiddenMethodCallCheckTask.java
===================================================================
--- lucene/tools/src/java/org/apache/lucene/validation/ForbiddenMethodCallCheckTask.java (revision 0)
+++ lucene/tools/src/java/org/apache/lucene/validation/ForbiddenMethodCallCheckTask.java (working copy)
@@ -0,0 +1,270 @@
+package org.apache.lucene.validation;
+
+/*
+ * 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.
+ */
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.EmptyVisitor;
+import org.objectweb.asm.commons.Method;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.MethodNode;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.types.resources.Resources;
+import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.types.resources.StringResource;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.BufferedReader;
+import java.io.Reader;
+import java.io.File;
+import java.io.StringReader;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Task to check if a set of class files contains calls to forbidden methods
+ * from a given classpath and list of methods (either inline or as pointer to files).
+ */
+public class ForbiddenMethodCallCheckTask extends Task {
+
+ private final Resources classFiles = new Resources();
+ private final Resources methodDescriptors = new Resources();
+ private Path classpath = null;
+
+ private final Map classCache = new HashMap();
+ private final Map forbiddenMethods = new HashMap();
+
+ /** Adds the method signature to the list of disallowed methods. The Signature is checked against the given ClassLoader. */
+ private void addMethod(ClassLoader loader, String signature) throws BuildException {
+ final int p = signature.indexOf('#');
+ if (p < 0)
+ throw new BuildException("Invalid method/constructor signature: " + signature);
+ final String clazz = signature.substring(0, p);
+ // we ignore the return type, its just to match easier (so return type is void):
+ final Method dummy = Method.getMethod("void " + signature.substring(p+1), true);
+ // check method signature, if it is really existent (in classpath), but we don't really load the class into JVM:
+ try {
+ ClassNode c = classCache.get(clazz);
+ if (c == null) {
+ final ClassReader reader;
+ if (loader != null) {
+ final InputStream in = loader.getResourceAsStream(clazz.replace('.', '/') + ".class");
+ if (in == null) {
+ throw new BuildException("Loading of class " + clazz + " failed: Not found");
+ }
+ try {
+ reader = new ClassReader(in);
+ } finally {
+ in.close();
+ }
+ } else {
+ // load from build classpath
+ reader = new ClassReader(clazz);
+ }
+ reader.accept(c = new ClassNode(), ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
+ classCache.put(clazz, c);
+ }
+ // list all methods with this signature:
+ @SuppressWarnings("unchecked") List methods = (List) c.methods;
+ boolean found = false;
+ for (final MethodNode mn : methods) {
+ if (mn.name.equals(dummy.getName()) && Arrays.equals(Type.getArgumentTypes(mn.desc), dummy.getArgumentTypes())) {
+ found = true;
+ forbiddenMethods.put(c.name + '\000' + new Method(mn.name, mn.desc), signature);
+ // don't break when found, as there may be more covariant overrides!
+ }
+ }
+ if (!found)
+ throw new BuildException("No method with following signature: " + signature);
+ } catch (IOException e) {
+ throw new BuildException("Loading of class " + clazz + " failed.", e);
+ }
+ }
+
+ /** Parses a class given as Resource and checks for valid method invocations */
+ private int checkClass(final Resource res) throws IOException {
+ final InputStream stream = res.getInputStream();
+ try {
+ final int[] violations = new int[1];
+ new ClassReader(stream).accept(new EmptyVisitor() {
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
+ return new EmptyVisitor() {
+ private int lineNo;
+
+ @Override
+ public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+ final String key = owner + '\000' + new Method(name, desc);
+ final String printout = forbiddenMethods.get(key);
+ if (printout != null) {
+ violations[0]++;
+ log(String.format(Locale.ENGLISH, "Forbidden method invocation: %s\n in %s, source line %d",
+ printout, res.getName(), lineNo), Project.MSG_ERR);
+ }
+ }
+
+ @Override
+ public void visitLineNumber(int lineNo, Label start) {
+ this.lineNo = lineNo;
+ }
+ };
+ }
+ }, ClassReader.SKIP_FRAMES);
+ return violations[0];
+ } finally {
+ stream.close();
+ }
+ }
+
+ /** Reads a list of method signatures. Closes the Reader when done (on Exception, too)! */
+ private void parseMethodFile(ClassLoader loader, Reader reader) throws IOException {
+ final BufferedReader r = new BufferedReader(reader);
+ try {
+ String line;
+ while ((line = r.readLine()) != null) {
+ line = line.trim();
+ if (line.length() == 0 || line.startsWith("#"))
+ continue;
+ addMethod(loader, line);
+ }
+ } finally {
+ r.close();
+ }
+ }
+
+ @Override
+ public void execute() throws BuildException {
+ AntClassLoader loader = null;
+ try {
+ if (classpath != null) {
+ classpath.setProject(getProject());
+ loader = getProject().createClassLoader(classpath);
+ }
+ classFiles.setProject(getProject());
+
+ try {
+ @SuppressWarnings("unchecked")
+ Iterator iter = (Iterator) methodDescriptors.iterator();
+ while (iter.hasNext()) {
+ final Resource r = iter.next();
+ if (!r.isExists()) {
+ throw new BuildException("Resource does not exist: " + r.getName());
+ }
+ if (r instanceof StringResource) {
+ parseMethodFile(loader, new StringReader(((StringResource) r).getValue()));
+ } else {
+ parseMethodFile(loader, new InputStreamReader(r.getInputStream(), "UTF-8"));
+ }
+ }
+ } catch (IOException ioe) {
+ throw new BuildException("IO problem while reading files with method signatures.", ioe);
+ }
+
+ long start = System.currentTimeMillis();
+
+ int checked = 0;
+ int errors = 0;
+ @SuppressWarnings("unchecked")
+ Iterator iter = (Iterator) classFiles.iterator();
+ while (iter.hasNext()) {
+ final Resource r = iter.next();
+ if (!r.isExists()) {
+ throw new BuildException("Class file does not exist: " + r.getName());
+ }
+
+ try {
+ errors += checkClass(r);
+ } catch (IOException ioe) {
+ throw new BuildException("IO problem while reading class file " + r.getName(), ioe);
+ }
+ checked++;
+ }
+
+ log(String.format(Locale.ENGLISH,
+ "Scanned %d class file(s) for forbidden method invocations (in %.2fs), %d error(s).",
+ checked, (System.currentTimeMillis() - start) / 1000.0, errors),
+ errors > 0 ? Project.MSG_ERR : Project.MSG_INFO);
+
+ if (errors > 0) {
+ throw new BuildException("Check for forbidden method calls failed, see log.");
+ }
+ } finally {
+ if (loader != null) loader.cleanup();
+ }
+ }
+
+ /** Set of class files to check */
+ public void add(ResourceCollection rc) {
+ classFiles.add(rc);
+ }
+
+ /** A file with method signatures methodFile= attribute */
+ public void setMethodFile(File file) {
+ methodDescriptors.add(new FileResource(getProject(), file));
+ }
+
+ /** Set of files with method signatures as nested element */
+ public FileSet createMethodFileSet() {
+ final FileSet fs = new FileSet();
+ fs.setProject(getProject());
+ methodDescriptors.add(fs);
+ return fs;
+ }
+
+ /** Support for method signature list as nested text */
+ public void addText(String text) {
+ methodDescriptors.add(new StringResource(getProject(), text));
+ }
+
+ /** Classpath as classpath= attribute */
+ public void setClasspath(Path classpath) {
+ createClasspath().append(classpath);
+ }
+
+ /** Classpath as classpathRef= attribute */
+ public void setClasspathRef(Reference r) {
+ createClasspath().setRefid(r);
+ }
+
+ /** Classpath as nested element */
+ public Path createClasspath() {
+ if (this.classpath == null) {
+ this.classpath = new Path(getProject());
+ }
+ return this.classpath.createPath();
+ }
+
+}
Index: lucene/tools/src/java/org/apache/lucene/validation/ForbiddenMethodCallCheckTask.java
===================================================================
--- lucene/tools/src/java/org/apache/lucene/validation/ForbiddenMethodCallCheckTask.java (revision 0)
+++ lucene/tools/src/java/org/apache/lucene/validation/ForbiddenMethodCallCheckTask.java (working copy)
Property changes on: lucene/tools/src/java/org/apache/lucene/validation/ForbiddenMethodCallCheckTask.java
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+Date Author Id Revision HeadURL
\ No newline at end of property
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: solr/build.xml
===================================================================
--- solr/build.xml (revision 1358274)
+++ solr/build.xml (working copy)
@@ -187,6 +187,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+