I created a reasonably big web project on Eclipse (8 months of work). I've been using Eclipse build system until now. Now I'd like to go to Ant for a number of reasons (among them, be able to add certain pre WAR tasks, like js compression and other...). I discovered that Eclipse creates a build.xml file automatically, with all dependencies set up. The problem is that if I try to run it, it fails and gives this error:
type parameters of <TypeName>TypeName cannot be determined; no unique maximal instance exists for type variable TypeName with upper bounds TypeName,java.lang.Object
[javac] return dao.getItemByProperty(propertyName, val, objectClass);
it besically dies beacause of an error with generics.... usually it compiles fine on Eclipse (I know it is a different compiler...). How can I have javac work with this??
The method is:
#Override
#Transactional
public <TypeName> TypeName getItemByProperty(String propertyName,
Object val, Class objectClass) {
return dao.getItemByProperty(propertyName, val, objectClass);
}
and dao.getItem... is
#Override
public <TypeName> TypeName getItemByProperty(String propertyName,
Object val, Class objectClass) {
Session sess = sessionFactory.getCurrentSession();
Criteria criteria = sess.createCriteria(objectClass);
criteria.add(Expression.eq(propertyName, val));
#SuppressWarnings("unchecked")
List<TypeName> results = criteria.list();
if (results != null && results.size() != 0) {
TypeName res = results.get(0);
return res;
}
return null;
}
they are in two classes that respectively implement two interface, the first for a service, the second for a dao and they are used in Spring.
Why is this happening? Is Eclipse compiler so different from javac?
What version of Ant is actually being used, and which version of the compiler is being used? For a sanity check, try this at the command line where you are trying to run ant:
ant -v.
javac -v.
I ran into a similar situation once, where everything should work but didn't. Here Weblogic had an older version of both ant and javac than what I was trying to use, and these older version were being used instead of the ones I wanted. I ended up writing a script that explicitly set these variables in my PATH, and running the script before running the ant task.
Related
I'm trying to redefine a method at runtime using javassist, but I'm running into some issues on the last step, because of the weird requirements I have for this:
I can't require the user to add startup flags
My code will necessarily run after the class has already been defined/loaded
My code looks like this:
val cp = ClassPool.getDefault()
val clazz = cp.get("net.minecraft.world.item.ItemStack")
val method = clazz.getDeclaredMethod(
"a",
arrayOf(cp.get("net.minecraft.world.level.block.state.IBlockData"))
)
method.setBody(
"""
{
double destroySpeed = this.c().a(this, $1);
if (this.s()) {
return destroySpeed * this.t().k("DestroySpeedMultiplier");
} else {
return destroySpeed;
}
}
""".trimIndent()
)
clazz.toClass(Items::class.java)
(I'm dealing with obfuscated method references, hence the weird names)
However, calling .toClass() causes an error as there are then two duplicate classes on the class loader - and to my knowledge there's no way to unload a single class.
My next port of call to update the class was to use the attach API and an agent, but that requires a startup flag to be added (on Java 9+, I'm running J17), which I can't do given my requirements. I have the same problem trying to load an agent on startup.
I have tried patching the server's jar file itself by using .toBytecode(), but I didn't manage to write the new class file to the jar - this method sounds promising, so it's absolutely on the table to restart the server after patching the jar.
Is there any way I can get this to work with my requirements? Or is there any alternative I can use to change a method's behavior?
This code works on Netbeans but gives an error on IntelliJ:
public static List<String> readFile(String file) throws IOException {
Path path = Paths.get("src", file);
Stream<String> lines = Files.lines(path);
return lines.collect(Collectors.toList());
}
The error is on the return statement and says that a List<String> is expected but the collect() method gives a List<Object>.
It looks like that Netbeans is able to understand that a Stream<String> must be collected to a List<String> while IntelliJ isn't.
What's the problem here?
EDIT: As suggested, I upgraded IntelliJ to the latest version (14.1.1) and I tried to compile my code with javac by terminal: it works. The problem must be in my IntelliJ configuration but it looks correct to me
You can help the compiler with an explicit type parameter:
return lines.collect(Collectors.<String>toList());
This was true some times ago but nowadays the work done to improve type inference on the compiler should make this type parameter unnecessary.
As far as I'm aware, IntelliJ uses javac behind the scenes, so your compile error is a bit weird.
Try also to compile your file with javac directly in command line and see if you have the same error.
If yes, then upgrade javac as soon as possible. If not, then there's certainly a problem in your IntelliJ configuration. Also you can upgrade IntelliJ to its latest 14.1.1 version.
Why you are manually creating a List<String>? "There's an API for it." ;-)
Amend the snippet for your needs.
Path path = Paths.get("src", file);
Charset charset = Charset.defaultCharset();
List<String> lines = Files.readAllLines(path, charset);
edit
Have a look at the signature of your toList() method. I believe it's linked to that one.
if it's like
private static List<Object> toList() {
return null;
}
you get in IntelliJ the error message you have posted: Collector<? super String,Object,List<String>> in Stream cannot be applied to ListObject>. Similar in Netbeans.
if it's like
static <T> Collector<T,?,List<T>> toList() {
return null;
}
you get in IntelliJ the error required: List<String> found: List<Object. Whereas in Netbeans it compiles.
if it's like
static Collector<? super String, Object, List<String>> toList() {
return null;
}
you don't get an error.
So as already mentioned it's linked to your toList() somehow. It would be interesting to know the type parameters for new Collector(T, A, R).
Could it be, that your IntelliJ is not set up with Java 1.8 properly?
Set the JDK for IntelliJ. It probably points to an older Java Version
I solved the mistery: in File Menu->Project Structure, change Project Language Level to 8.0 - Lambdas, type annotations etc.
I have a Ruby script that I'd like to run at the startup of my Java program.
When you tell the ScriptEngine to evaluate the code for the first time, it takes a while. I'm under the impression that the reason it takes this long is because it first needs to compile the code, right?
I found that you can compile Ruby code, and then evaluate it later. The evaluation itself is fast - the compilation part is the slow one. Here I am compiling:
jruby = new ScriptEngineManager().getEngineByName("jruby");
Compilable compilingEngine = (Compilable)jruby;
String code = "print 'HELLO!'";
CompiledScript script;
script = compilingEngine.compile(code);
This snippet is what takes a while. Later when you evaluate it, it is fine.
So of course, I was wondering if it would be possible to "save" this compiled code into a file, so in the future I can "load" it and just execute it without compiling again.
As others have said, this is not possible with CompiledScript. However, with JRuby you have another option. You can use the command line tool jrubyc to compile a Ruby script to Java bytecode like so:
jrubyc <scriptname.rb>
This will produce a class file named scriptname.class. You can run this class from the command line as if it were a normal class with a main(String[] argv) method (note: the jruby runtime needs to be in the classpath) and you can of course load it into your application at runtime.
You can find more details on the output of jrubyc here: https://github.com/jruby/jruby/wiki/JRubyCompiler#methods-in-output-class-file
According to this, no.
"Unfortunately, compiled scripts are not, by default, serializable, so they can't be pre-compiled as part of a deployment process, so compilation should be applied at runtime when you know it makes sense."
I think some really easy cache will solve your problem:
class CompiledScriptCache {
static {
CompiledScriptCache INSTANCE = new CompiledScritCache();
}
publich static CompiledScriptCache get(){
retrun INSTANCE;
};
List<CompiledScript> scripts = new ArrayList<>();
public CompiledScript get(int id){
return scripts.get(id);
}
public int add(String code){
ScriptEngine jruby = new ScriptEngineManager().getEngineByName("jruby");
Compilable compilingEngine = (Compilable)jruby;
CompiledScript script;
script = compilingEngine.compile(code);
scripts.add(script);
return scripts.size()-1;
}
}
update
I thought this question was about avoiding to comile the source more than once.
Only other approach I could imagine is to create Java-Classes and make a cross-compile:
https://github.com/jruby/jruby/wiki/GeneratingJavaClasses
I am facing one problem. I renamed javac.exe on my machine and noticed that ant javac task still works fine.
Does anybody know from where its getting javac.exe?
Actually, I believe, that by default Ant tries to execute the java compiler class directly with this code:
try {
Class c = Class.forName ("com.sun.tools.javac.Main");
Object compiler = c.newInstance ();
Method compile = c.getMethod ("compile",
new Class [] {(new String [] {}).getClass ()});
int result = ((Integer) compile.invoke
(compiler, new Object[] {cmd.getArguments()}))
.intValue ();
return (result == MODERN_COMPILER_SUCCESS);
} catch (Exception ex) {
if (ex instanceof BuildException) {
throw (BuildException) ex;
} else {
throw new BuildException("Error starting modern compiler",
ex, location);
}
}
The code came from here.
Which means that if the library tools.jar is on the current classpath of Ant, it will pickup the class and launch it. This results in the fact that javac.exe can be renamed to whatever you want, it will still work. So to answer your question, it actually executes none of any "javac.exe".
There are other implementations of the Javac task, but I think this is the default one for all compilers 1.3+
You could try starting here and check what is configured in global build.compiler property, it may be pointing somewhere else
I have a test that compares a large blob of expected XML with the actual XML received. If the XML is significantly different, the actual XML is written to disk for analysis and the test fails.
I would prefer to use assertEquals so that I can compare the XML more easily in Eclipse - but this could lead to very large JUnit and CruiseControl logs.
Is there a way I can change a JUnit test behaviour depending on whether it's running through Eclipse or through Ant.
Here are 2 solutions.
Use system properties
boolean isEclipse() {
return System.getProperty("java.class.path").contains("eclipse");
}
Use stacktrace
boolean isEclipse() {
Throwable t = new Throwable();
StackTraceElement[] trace = t.getStackTrace();
return trace[trace.length - 1].getClassName().startsWith("org.eclipse");
}
Yes - you can test if certain osgi properties are set (System.getProperty("osgi.instance.area") for instance). They will be empty if junit is started through ant outside of eclipse.
Maybe the "java.class.path" approach can be weak if you include some eclipse jar in the path.
An alternative approch could be to test "sun.java.command" instead:
On my machine (openjdk-8):
sun.java.command org.eclipse.jdt.internal.junit.runner.RemoteTestRunner ...
A possible test:
boolean isEclipse() {
return System.getProperty("sun.java.command")
.startsWith("org.eclipse.jdt.internal.junit.runner.RemoteTestRunner");
}
Usually, the system proeprties are different in different environments. Try to look for a system property which is only set by eclipse or ant.
BTW: The output in eclipse is the same, its just that the console for eclipse renders the output in a more readable form.
Personally, I wouldn't worry about the size of the logs. Generally you don't need to keep them very long and disk space is cheap.
With Java 1.6+, it looks like the result of System.console() makes a difference between running for Eclipse or from a terminal:
boolean isRealTerminal()
{
// Java 1.6+
return System.console() != null;
}