I found the bytecode compiled from ECJ compiler has the annotation information missing.
The source code decompiled from bytecode compiled by javac:
public class HelloWorldApp {
#GetAction("/hello")
public String sayHello() {
return "Hello World!";
}
}
The source code decompiled from bytecode compiled by ECJ:
public class HelloWorldApp {
public String sayHello() {
return "Hello World!";
}
}
So clearly the annotation #GetAction("/hello") is missing from the bytecode compiled by ECJ (in memory compilation).
Anyone has encountered this issue before and get any clue?
Update with more information
Take a look at the following screenshot:
So it is in the ASTNode.resolveAnnotations() methods line #797:
A) The state if ((method.tagBits & TagBits.AnnotationResolved) != 0) return annotations; will return null as annotations even
B) the source annotation #GetAction("/hello") is presented because
C) the this.annotations field is null and
D) the if condition (method.tagBits & TagBits.AnnotationResolved) != 0 evaluates to true
Updates 2
It looks like I captured the screen too early, so once process finished, I found the annotations information get stored:
However I still can't get the annotation information from the result file. Click here to download the bytecode file generated:
Note I am using ECJ 4.4.1:
<dependency>
<groupId>org.eclipse.jdt.core.compiler</groupId>
<artifactId>ecj</artifactId>
<version>4.4.1</version>
</dependency>
Problem solved! Just add the following lines:
opt(map, OPTION_TargetPlatform, "1.6");
The issue is caused by default JDK version is 1.2 in ECJ, which does not support annotation
Related
I'm creating a JSF Applikation and i would like to get some kind of warning (preferably i the console) if i make typos in my EL-Expression.
Example:
On my page i wanted to show some Text that is localized. The locale-config in faces-config.xml is properly set up and works with the var 'msgs'.
I used this code on my page:
<h:outputText value="#{msg.title_edit_customer}"/>
When i checked my Page in the browser, nothing got showed.
I took me a while to realize that i made a typo - with #{msgs.... it worked as expected.
Can i activate some kind of Debug-Output, so i can see directly that there is an invalid EL somewhere?
My Setup: Eclipse 4.4.2, Tomcat 8, MyFaces 2.2.8
Thanks to #SJuan76 i could figure it out:
Create your own javax.el.ELResolver, all the Methods can return null/false.
Open the Source of the Class org.apache.myfaces.el.unified.resolver.ScopedAttributeResolver and copy the methods facesContext(ELContext) and findScopedMap(FacesContext, Object) (since ScopedAttributeResolver is final, we can't extend it).
Edit the getValue-Method:
#Override
public Object getValue(ELContext context, Object base, Object property) {
if(!context.isPropertyResolved()){
//Douple Check false-positives
boolean foundInScope = false;
final Map<String, Object> scopedMap = findScopedMap(
facesContext(context), property);
if (scopedMap != null) {
Object object = scopedMap.get(property);
if (object != null) {
foundInScope = true;
}
}
if (!foundInScope) {
log.warn(String.format("EL-Property %s couldn't be resolved",
property));
}
}
return null;
}
Edit faces-config.xml to register your resolver:
<application>
<el-resolver>com.myPackage.DebugELResolver</el-resolver>
</application>
Since there are many Resolvers and each one does a little bit of the Resolving, our new Resolver should come last. Add following to web.xml:
<context-param>
<param-name>org.apache.myfaces.EL_RESOLVER_COMPARATOR</param-name>
<param-value>org.apache.myfaces.el.unified.CustomLastELResolverComparator</param-value>
</context-param>
Now you'll get some log-Output, every time an expression couldn't be resolved:
03.07.2015 07:34:21 com.myPackage.DebugELResolver [http-nio-8080-exec-2] WARN EL-Property msgx couldn't be resolved
I couldn't figure out how to get the actual EL-Expression (e.g. #{msgx.title_edit_customer}.
Working on a GWT project (2.7.0), i have experienced a very odd client code behaviour.
The following code throws the error "SEVERE: (ReferenceError) : Ljava_io_Serializable_2_classLit_0_g$ is not definedcom.google.gwt.core.client.JavaScriptException: (ReferenceError) : Ljava_io_Serializable_2_classLit_0_g$ is not defined".
The error occurs, when calling Arrays.asList() with a parameter, that has an interface type.
Is this expected behaviour or a GWT bug?
// Working
Integer n1 = 1;
Arrays.asList(n1);
// Not working
Serializable n2 = 1;
Arrays.asList(n2);
GWT 2.7's Super Dev Mode (and from the _g$ in your class literal field, I think that is what you are using) has been observed having other issues like this, but when compiled the issues go away.
If this is indeed what you are seeing, the issue seems to be fixed in 2.8, not yet released: https://groups.google.com/d/topic/google-web-toolkit/RzsjqX2gGd4/discussion
This behavior is definitely not expected, but if you can confirm that this works correctly when compiled for production and in GWT 2.8, then we at least know the bug is fixed.
Well, the typical use of Arrays.asList would be
Object myObj = new Object();
List theList = Arrays.asList(new Object[] {myObj});
This works in GWT with any kind of interface/class/enum you throw at it.
EDIT : I've tested this with GWT 2.5.1 :
public class Foo implements EntryPoint {
public static interface MyInterface {
}
public static class MyObject implements MyInterface {
}
public void onModuleLoad() {
MyInterface myObject = new MyObject();
List<MyInterface> myList = Arrays.asList(myObject);
}
}
Isn't it possible that the problem lies somewhere else?
I am trying to write a plugin for Papyrus that converts Alf code.
I tried to use the Alf-parser that is already included in Papyrus (org.eclipse.papyrus.uml.alf.*). So I tried to instantiate the parser as written here:
public class Activator extends Plugin {
// default Activator code here ...
public String ConvertAlfToSpecSharp(String alf)
{
new org.eclipse.emf.mwe.utils.StandaloneSetup().setPlatformUri("../");
Injector injector = new AlfStandaloneSetup().createInjectorAndDoEMFRegistration();
XtextResourceSet resourceSet = injector.getInstance(XtextResourceSet.class);
resourceSet.addLoadOption(XtextResource.OPTION_RESOLVE_ALL, Boolean.TRUE);
// ...
}
}
But the first line (new org.eclipse.emf.mwe.utils.StandaloneSetup().setPlatformUri("../");) throws the following exception:
com.google.inject.CreationException: Guice creation errors:
1) Error injecting method, java.lang.IllegalStateException: No EPackages were registered for the validator org.eclipse.papyrus.uml.alf.validation.CommonJavaValidator please override and implement getEPackages().
at org.eclipse.xtext.validation.AbstractInjectableValidator.register(Unknown Source)
at org.eclipse.xtext.service.MethodBasedModule.configure(MethodBasedModule.java:55)
while locating org.eclipse.papyrus.uml.alf.validation.CommonJavaValidator
1 error
at com.google.inject.internal.Errors.throwCreationExceptionIfErrorsExist(Errors.java:435)
at com.google.inject.internal.InternalInjectorCreator.injectDynamically(InternalInjectorCreator.java:183)
at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:109)
at com.google.inject.Guice.createInjector(Guice.java:95)
at com.google.inject.Guice.createInjector(Guice.java:72)
at com.google.inject.Guice.createInjector(Guice.java:62)
at org.eclipse.papyrus.uml.alf.CommonStandaloneSetupGenerated.createInjector(CommonStandaloneSetupGenerated.java:28)
at org.eclipse.papyrus.uml.alf.CommonStandaloneSetupGenerated.createInjectorAndDoEMFRegistration(CommonStandaloneSetupGenerated.java:22)
at org.eclipse.papyrus.uml.alf.CommonStandaloneSetup.doSetup(CommonStandaloneSetup.java:23)
at org.eclipse.papyrus.uml.alf.AlfStandaloneSetupGenerated.createInjectorAndDoEMFRegistration(AlfStandaloneSetupGenerated.java:20)
at <packagenamehere>.Activator.ConvertAlfToSpecSharp(Activator.java:113)
I have no idea how to solve this, especially since I find it very hard to debug eclipse applications...
Update:
Here are links to some relevant classes (all from the org.eclipse.papyrus.uml.alf.common plugin of Papyrus plugins (link)):
org.eclipse.papyrus.uml.alf.CommonStandaloneSetup
org.eclipse.papyrus.uml.alf.CommonStandaloneSetupGenerated
org.eclipse.papyrus.uml.alf.validation.CommonJavaValidator
org.eclipse.papyrus.uml.alf.validation.AbstractCommonJavaValidator
As you can see, the method getEPackages() of AbstractCommonJavaValidator.java returns an empty list.
If you look at this AbstractAlfJavaValidator implementation, there is an EPackage which is added to the list.
As a solution, i think you should edit CommonJavaValidator.java and override getEPackages() in order to add an EPackage:
#Override
protected List<EPackage> getEPackages() {
List<EPackage> result = super.getEPackages();
// result.add(org.eclipse.papyrus.uml.alf.alf.AlfPackage.eINSTANCE);
// Edit
result.add(org.eclipse.emf.ecore.EcorePackage.eINSTANCE);
return result;
}
Edit
If you can't edit papyrus plugins, it think you should add the following before your code:
if (!EPackage.Registry.INSTANCE.containsKey("http://www.eclipse.org/papyrus/alf/Alf")) {
EPackage.Registry.INSTANCE.put("http://www.eclipse.org/papyrus/alf/Alf", org.eclipse.papyrus.uml.alf.alf.AlfPackage.eINSTANCE);
}
It will add an EPackage before the guice creation and the exception will avoided.
Is it possible to do Inter Type Declarations with AspectJ on Compiled Class Files at Load Time Weaving?
As an example: I compile some Groovy code and want to add fields or methods with IDT.
Update:
Oh my goodness, you do not need reflection to access members or execute methods. Eclipse shows errors in the editor, but you may just ignore them, the code compiles and runs fine anyway. So the aspect is really much more strightforward and simple:
public aspect LTWAspect {
public static String Application.staticField = "value of static field";
public String Application.normalField = "value of normal field";
public void Application.myMethod() {
System.out.println(normalField);
}
void around() : execution(void Application.main(..)) {
System.out.println("around before");
proceed();
System.out.println("around after");
System.out.println(Application.staticField);
new Application().myMethod();
}
}
Original answer:
Yes, but you have a hen-and-egg problem there, i.e. you cannot just reference the newly introduced fields from your LTW aspect code without reflection. (The last sentence is not true, see update above.) Plus, in order to make your LTW aspect compile, you need the classes to be woven on the project's build path so as to be able to reference them. Example:
Java project
public class Application {
public static void main(String[] args) {
System.out.println("main");
}
}
AspectJ project
import org.aspectj.lang.SoftException;
public aspect LTWAspect {
public static String Application.staticField = "value of static field";
public String Application.normalField = "value of normal field";
public void Application.myMethod() {
try {
System.out.println(Application.class.getDeclaredField("normalField").get(this));
} catch (Exception e) {
throw new SoftException(e);
}
}
void around() : execution(void Application.main(..)) {
System.out.println("around before");
proceed();
System.out.println("around after");
try {
System.out.println(Application.class.getDeclaredField("staticField").get(null));
Application.class.getDeclaredMethod("myMethod", null).invoke(new Application());
} catch (Exception e) {
throw new SoftException(e);
}
}
}
So, e.g. in Eclipse you need to put the Java project on the AspectJ project's build path under "Projects" because only then it can see Java class Application on which you want to declare members. After compilation you just start the Java project and do LTW on the aspect project (don't forget an aop-ajc.xml referencing LTWAspect).
In my example above I declare a static member, a non-static ("normal") member and a non-static method. My advice prints the static member and calls the non-static method, both via reflection. The non-static method then prints the non-static member, again via reflection. This is not nice, but it works and proves the ITD in combination with LTW is possible. There might be a more elegant way, but if so I am unaware of it. (Update: There is a more elegant way: Just ignore the errors marked by Eclipse IDE, see above.)
Program output
around before
main
around after
value of static field
value of normal field
I have 3 java files: HW.java, myAnn.java, and Constants.java in package myApp.
Constants.java:
public final class Constants {
public static final String WORLD ="World";
}
myAnn.java:
public #interface myAnn {
java.lang.String name() default "";
}
HW.java:
class HW {
#myAnn(name = Constants.WORLD)
public static void main(String[] args){
System.out.println("Hi "+ Constants.WORLD);
}
}
My app compiles and runs fine as shown above, but I want to migrate HW.java to scala as
HelloWorld.scala:
object HelloWorld {
#myAnn(name = Constants.WORLD)
def main(args: Array[String]) {
println("Hello " + Constants.WORLD)
}
}
When I try to compile this, I get
error: annotation argument needs to be a constant; found:
Constants.WORLD #myAnn(name = Constants.WORLD)
If I remove the annotation then HelloWorld compiles and executes as expected.
Why can I use Constants.WORLD as a parameter to an annotation from a java program, but not from a scala program? Is there something I can modify in Constants.java to allow it to be used from either java or scala? I can't modify MyAnn.java, and I can't migrate Constants.java yet.
It is a bug that only shows up when feeding the java source files into the scala compiler, see issue SI-2764. The example works when compiling the java files first using javac and then pointing scalac's classpath to the generated classfiles.