I'm looking to distribute a custom API and know the classes will be available at run time. I would like my public and protected methods / classes to be included in a jar I can distribute but I don't want any other source code and I would like to throw an exception if that jars code is actually executed.
This is the exact behaviour the Android framework jar has when you attempt to execute the jar directly.
My question is how to I create the same jar from my source without manually going through and creating each stubbed method. I would like this to scale as my API grows.
I believe you can use for that purpose the mkstubs tool: https://github.com/android/platform_development/tree/master/tools/mkstubs
As #CommonsWare mentioned stubs in AOSP are generated by javadoc DroidDoc script, read here: How are .java files in android_stubs_current_intermediates directory generated?
One possibility is to write a Java compiler which outputs a copy of your source code with empty method bodies or a body work a single statement which throws an exception. Then you can use normal dev tools to compile the generated classes.
Related
I've got a project and want to share an API that can be used for building a plugin for my application.
Now I don't want to share the full source code but only class definitions and member declarations without their body.
I've seen dependencies before that without downloading the sources the IDE I'm using already knows the structure. That is what I'm going for.
A jar file already does most of what you want, as it does, if not obfuscated, contain all the class and method names in a format that will be understood by any Java IDE.
The rest can be done by preparing and delivering a javadoc jar.
I have some code in an Android project that parses HTML using Jsoup. It doesn't use anything Android specific, they're just static methods that take an InputStream, and return my model classes. The app uses Gradle to build itself in Android Studio.
Is there any way I can create a standard Java main method to do something like load HTML from a local file, run it through my parser, and output a JSON file (using Gson on my model class)? I'm thinking maybe I can add a new sourceSet to Gradle like a jvmCompatible set of classes? I would greatly prefer not to copy my code to a separate project.
EDIT:
I guess I didn't make this clear, but I would like the be able to run this locally on my dev machine from the command line, rather than on an Android device or emulator.
You don't necessarily need to do anything in the build file to set this up; the build file generates Java .class files, and you can feed them to Java directly from the command line. You can add a main method to any class:
package com.example.foo;
class MyClass {
...
public static void main(String [] args) {
...
}
}
The main method will be happily ignored util you invoke it via the Java command line. You can do this by setting your classpath to the intermediate build directory and telling the Java command line which class to start:
java -classpath app/build/intermediates/classes/debug/ com.example.foo.MyClass
where you pass in the path to the build/intermediates/classes/debug directory in your app module's build output, and the fully-qualified name of the class.
Note that if you're running a release build that uses ProGuard, this main method could get stripped out if it's not otherwise referenced in the code.
Make sure you don't access any Android classes or you'll get a runtime error.
As an aside, you might find it worthwhile to separate out your Java-only code into a Java-only module in the build. Among other things, it would let you use JUnit to write nice test cases for the classes within; if you're asking this question because you want to do some testing of your parser, you might find it convenient to do so within the auspices of a unit test.
When I used to write libraries in C/C++ I got into the habit of having a method to return the compile date/time. This was always a compiled into the library so would differentiate builds of the library. I got this by returning a #define in the code:
C++:
#ifdef _BuildDateTime_
char* SomeClass::getBuildDateTime() {
return _BuildDateTime_;
}
#else
char* SomeClass::getBuildDateTime() {
return "Undefined";
}
#endif
Then on the compile I had a '-D_BuildDateTime_=Date' in the build script.
Is there any way to achieve this or similar in Java without needing to remember to edit any files manually or distributing any seperate files.
One suggestion I got from a co-worker was to get the ant file to create a file on the classpath and to package that into the JAR and have it read by the method.
Something like (assuming the file created was called 'DateTime.dat'):
// I know Exceptions and proper open/closing
// of the file are not done. This is just
// to explain the point!
String getBuildDateTime() {
return new BufferedReader(getClass()
.getResourceAsStream("DateTime.dat")).readLine();
}
To my mind that's a hack and could be circumvented/broken by someone having a similarly named file outside the JAR, but on the classpath.
Anyway, my question is whether there is any way to inject a constant into a class at compile time
EDIT
The reason I consider using an externally generated file in the JAR a hack is because this is) a library and will be embedded in client apps. These client apps may define their own classloaders meaning I can't rely on the standard JVM class loading rules.
My personal preference would be to go with using the date from the JAR file as suggested by serg10.
I would favour the standards based approach. Put your version information (along with other useful publisher stuff such as build number, subversion revision number, author, company details, etc) in the jar's Manifest File.
This is a well documented and understood Java specification. Strong tool support exists for creating manifest files (a core Ant task for example, or the maven jar plugin). These can help with setting some of the attributes automatically - I have maven configured to put the jar's maven version number, Subversion revision and timestamp into the manifest for me at build time.
You can read the contents of the manifest at runtime with standard java api calls - something like:
import java.util.jar.*;
...
JarFile myJar = new JarFile("nameOfJar.jar"); // various constructors available
Manifest manifest = myJar.getManifest();
Map<String,Attributes> manifestContents = manifest.getAttributes();
To me, that feels like a more Java standard approach, so will probably prove more easy for subsequent code maintainers to follow.
I remember seeing something similar in an open source project:
class Version... {
public static String tstamp() {
return "#BUILDTIME#";
}
}
in a template file. With Ant's filtering copy you can give this macro a value:
<copy src="templatefile" dst="Version.java" filtering="true">
<filter token="BUILDTIME" value="${build.tstamp}" />
</copy>
use this to create a Version.java source file in your build process, before the compilation step.
AFAIK there is not a way to do this with javac. This can easily be done with Ant -- I would create a first class object called BuildTimestamp.java and generate that file at compile time via an Ant target.
Here's an Ant type that will be helpful.
Unless you want to run your Java source through a C/C++ Preprocessor (which is a BIG NO-NO), use the jar method. There are other ways to get the correct resources out of a jar to make sure someone didn't put a duplicate resource on the classpath. You could also consider using the Jar manifest for this. My project does exactly what you're trying to do (with build dates, revisions, author, etc) using the manifest.
You'll want to use this:
Enumeration<URL> resources = Thread.currentThread().getContextClassLoader().getResources("META-INF/MANIFEST.MF");
This will get you ALL of the manifests on the classpath. You can figure out which jar they can from by parsing the URL.
Personally I'd go for a separate properties file in your jar that you'd load at runtime... The classloader has a defined order for searching for files - I can't remember how it works exactly off hand, but I don't think another file with the same name somewhere on the classpath would be likely to cause issues.
But another way you could do it would be to use Ant to copy your .java files into a different directory before compiling them, filtering in String constants as appropriate. You could use something like:
public String getBuildDateTime() {
return "#BUILD_DATE_TIME#";
}
and write a filter in your Ant file to replace that with a build property.
Perhaps a more Java-style way of indicating your library's version would be to add a version number to the JAR's manifest, as described in the manifest documentation.
One suggestion I got from a co-worker
was to get the ant file to create a
file on the classpath and to package
that into the JAR and have it read by
the method. ... To my mind that's a
hack and could be circumvented/broken
by someone having a similarly named
file outside the JAR, but on the
classpath.
I'm not sure that getting Ant to generate a file is a terribly egregious hack, if it's a hack at all. Why not generate a properties file and use java.util.Properties to handle it?
I have a text (.txt) file that contains Java code! I want to create a method that includes this Java code and then call that method through the program.
Can anybody suggest a way to do this?
let consider this example what it does actually load the source code, compile and execute the java code by simpler program by using JavaCompiler API.
Use the JavaCompiler. It can compile code from a String, so I'm sure it could handle code from a text file.
Do you think instead of putting it in the main method I can put it in for example test method and call method like this?
Put it wherever you like. E.G. see the STBC & especially the source code. It provides a GUI and can compile the code in the text area on button click.
this program need tools.jar but jre 7 doesnt have this!!
Did you try reading the documentation that is provided for the STBC? Notably:
System Requirements
STBC will run on any computer with a version 1.6+ Java Plug-In* JDK (AKA SDK).
(*) The API that STBC uses is merely a public interface to the compiler in the tools.jar that is distributed only with JDKs (though the 'public JRE' of the JDK also seems to acquire a tools.jar). This leads to some unusual requirements in running either the native jar, or the web start app.
Or shorter, no JRE will have a JavaCompiler, only JDKs have them.
Change the .txt file to a .java file,
add it to your java project
Compile the code
Execute the methods
Load the file in through standard java IO and then have Groovy evaluate it for you:
http://groovy.codehaus.org/Embedding+Groovy
it's something like quine:
http://www.nyx.org/%7Egthompso/quine.htm
We have a jar that we lost the source code to. I decompiled the jar and created new source from it. I want to then verify that the source code and the old jar have the same behavior. I am writing unit tests to do the verification the problem is that they both have same namespace / class name so I do not know how to disambiguate the old jar and the new source code. What can I do or is it impossible?
You need to only have one version on the class path at once to guarantee that you are running that version of the code. Develop your unit test separate from the code so you can drop in either version.
Give the new source a temporary namespace for testing purposes. Then instead of import, you can refer your new classes as:
com.yourfirm.test.packagename.TheClassName
the old ones can be simply imported and refered to as TheClassName. This way you can tell by looking at your test cases which is which.
Or simply run the tests with -cp oldpackage.jar and then -cp newpackage.jar.
It's possible, but you have to mess around with class loading. Instead of putting either of the jars on the classpath, you'll need to load them at runtime. Check out JCL for a library to allow you to do this. (Disclaimer: I have never used JCL.)
Basically, each test would have to load the class from the old JAR, grab the results of the method you're testing, then unload that JAR, load up the new one, run the same method against the new version, and compare the results.
I'd change which classes are being tested at runtime with the classpath. This approach would be less error-prone in terms of ensuring that you're running the same test code against both binaries. Otherwise you introduce more complexity around whether the tests are correct.
It sounds like you are trying to execute the tests against both jars at the same time. I don't know of a way to disambiguate the old/new jars if they are both in the classpath.
If your unit tests output results to stdout/stderr, you could run the tests against the original jar and save the results. Then run the tests against the new jar and save the results in a separate file. Then diff the files.
Another approach would be to refactor the new source code so that it has a unique namespace. You could then test against both jars at the same time, but it could be a lot of work to make existing programs use the new jar.
If you run your tests via ant (Junit-task), you can control the ant classpath seperately for both runs (once via jar, once via fileset of classes).