I'm trying to add some annotations to classes while they are loaded.
For that I've wrote a java agent transformer which gets the class bytecode upon loading and can change it.
When I run the following code the new annotation apears on the class but all previous annotation and fields / methods are removed.
CtClass ctClass = classPool.makeClass(new java.io.ByteArrayInputStream(classFileBuffer));
ClassFile classFile = clazz.getClassFile();
ConstPool constPool = classFile.getConstPool();
AnnotationsAttribute attr= new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
javassist.bytecode.annotation.Annotation annotation = new javassist.bytecode.annotation.Annotation(type, constPool);
attr.setAnnotation(annotation);
classFile.addAttribute(attr);
classFileBuffer = ctClass.toBytecode();
Where classFileBuffer is the byte array which is returned to the class loader.
If anyone has an idea why the previous class annotations and code are removed it will be very helpful.
Thanks,
Avner
setAnnotation takes only one parameter which is of type Annotation, and it erases all the others annotations. If you want to add an annotation to the existing ones, use setAnnotations instead. It takes an array of Annotation so you have first to build the array by gathering all the existing annotations (using getAnnotations) then add the Annotation at the end, then call the method.
a setAnnotation(annotation) call is equivalent to setAnnotations(new Annotation[] { annotation })
Related
I have the following data class:
data class Foo(#field:Password val password: String)
Why does Foo::password.annotations return an empty list?
Also, Foo::password.findAnnotation<Password>() returns null.
The same happens when I use an instance of Foo:
Foo("")::password.annotations
Foo("")::password.findAnnotation<Password>()
However, this java variant works: Foo::password.javaField.getAnnotation(Password::class.java).
This is on kotlin version 1.3.10.
The docs don't give much information on the inner workings of findAnnotation.
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/-k-property/index.html
fun <T : Annotation> KAnnotatedElement.findAnnotation(): T?
Returns an annotation of the given type on this element.
What am I missing here?
Thanks in advance!
Foo::password.annotations is the list of annotations on the property. In your code, you've used the #field: use site target, which means that the annotation is applied to the backing field, not the property. Therefore, the list of annotations on the property is empty.
The Java variant works because it loads the list of annotations on the field.
I am using FOSrestBundle in my Symfony2 project. I have a view created like this:
$view = $this
->view(array(
'form' => $this->formHandler->getForm()->createView(),
'translation' => $translation,
), Response::HTTP_OK)
->setTemplate('MyBundle:Translation.html.twig');
Where $translation is an object of my own bundle. The thing is when I call the $this->handleView($view), FosRestBundle use JMS serializer to serialize the data of my view (the form and the translation object) but my translation object have a lot of attributes useless in my case and the response is far too big for what I am trying to do.
I decide to use the group annotation to only retrieve useful attributes.
Here is the context with the view group:
$context = SerializationContext::create()->setGroups(array('view'));
$view->setSerializationContext($context);
And in my Translation model I can set the ExclusionPolicy to all and add usefull attributes to the view group. It is working but with this configuration (the group view in the serialization context) my form object (which is a Symfony\Component\Form\FormView) is serialized to {}
How can I use a group for my Translation model but still serialize my FormView object ?
If you're using annotations the JMS serializer has exclusion policies for each class, which you can see here.
I would suggest instead to default to exclude all and add the serializer groups annotation on only properties you want to expose. You can add multiple groups, so in this case your serializer context could have the groups "form" and "translationBasic", then add the "form" group to all properties on formView and "translationBasic" to just those you want on the Translation class.
I have a Kotlin class whose primary (and only) constructor is empty.
I have a reference to this class:
val kClass: KClass<MyClass> = MyClass::class
How do I create an instance of this class using reflection?
In Java I would do myClass.newInstance() but it seems in Kotlin I need to find the constructor first:
kClass.constructors.first().call()
I have seen mention of primaryConstructor in some bug reports but it's not showing up in my IDE.
In your case, Java reflection might be enough: you can use MyClass::class.java and create a new instance in the same way as you would with Java reflection (see #IngoKegel's answer).
But in case there's more than one constructor and you really need to get the primary one (not the default no-arg one), use the primaryConstructor extension function of a KClass<T>. It is a part of Kotlin reflection, which is not shipped within kotlin-stdlib.
To use it, you have to add kotlin-reflect as a dependency, e.g. a in Gradle project:
dependencies {
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
}
Assuming that there is ext.kotlin_version, otherwise replace $kotlin_version with the version you use.
Then you will be able to use primaryConstructor, for example:
fun <T : Any> construct(kClass: KClass<T>): T? {
val ctor = kClass.primaryConstructor
return if (ctor != null && ctor.parameters.isEmpty())
ctor.call() else
null
}
You can use the Java class to create new instance:
MyClass::class.java.newInstance()
For those checking this question now, since Kotlin 1.1 there's also createInstance() extension method on KClass
Much like the accepted answer, this function works only in case class has an empty constructor or constructor with all default arguments.
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect.full/create-instance.html
Expanding on Alexeys Answer, to include a primary constructor call with parameters:
/* Example class with no-args constructor */
class MyClass
/* Example class requiring parameters */
class MyClassWithParams(parameter1: String, parameter2: MyClass)
val myKClass: KClass<MyClass> = MyClass::class
val myKClassWithParameters: KClass<MyClassWithParams> = MyClassWithParams::class
/* We can create an object by calling createInstance when no constructor parameters are required as explained in other answers. */
val myObject: MyClass = myKClass.createInstance()
/* To create an object with parameters, we need to get the constructor first, and call it with the parameters instead, similarly to how we would do in Java. */
val myObjectWithParameters: MyClassWithParams? =
myKClassWithParameters.primaryConstructor?.call(
"StringParameter", myObject
)
I have an MVC controller class containing a method named "Func". I have noticed that if I insert inside it simple code such as the following:
String s1="foo";
String s2="bee";
return s1+s2;
the function is called and executed correctly. But if I use classes not belonging to the Base Class Library, such as in the following code block
MongoClient mongo_client = new MongoClient();
IMongoDatabase mongo_database = mongo_client.GetDatabase("dbname");
IMongoCollection<BsonDocument> mongo_collection = mongo_database.GetCollection<BsonDocument>("colname");
the method is not even called. Why? Is it something related to MongoDB?
I've got a two classes (pupil, class) in a Roo-project and their scaffolded views.
pupil and class have a 1:1 relationship
In the list.jspx of pupil I'd like to display a column for a property of class.
I don't know the correct attributes to give to the table:column-tag.
This following example gives the error:
SpelEvaluationException: EL1027Epos 4): Indexing into type 'com.pupil' is not supported
<table:table data="${pupil}" duplicate="true" id="l_com_pupil" path="/admin/pupil" z="user-managed">
<table:column id="c_com_pupil_pupilName" property="pupilName" z="user-managed"/>
<!-- I'd like to display the attribute teacher_name of the class 'class' here but it doesn't work -->
<table:column id="c_com_pupil_class_teacherName" property="teacherName" z="user-managed"/>
</table:table>
Instead of messing around with the jspx files, you can simply do this by implementing a converter for the Teacher entity within the ApplicationServiceFactoryBean.java.
See the below conversion method for an example.
static class com.mycompany.test.controllers.ApplicationConversionServiceFactoryBean.TeacherConverter implements org.springframework.core.convert.converter.Converter<com.mycompany.test.domain.master.Teacher, java.lang.String> {
public String convert(Teacher teacher) {
return new StringBuilder().append(teacher.getName()).toString();
}
}
By default, Roo generates these converters and they are stored within the ApplicationConversionServiceFactoryBean_Roo_ConversionService.aj file.
You can push in refactor the related method for the Teacher entity from this aspectJ file into the
ApplicationServiceFactoryBean.java file and then implement your own conversion which will be used to show the Teacher name across the application as in the above example.
Cheers and all the best with Roo!
This is how I did it, not for listing, but rather for showing the name of the teacher when you view the pupil entity:
Edit the controller and specifically the method show (in the java file, not in the aj file, of course).
Add an attribute to your UI Model, for instance "teacherName" (use Model.addAttribute), where you populate the teacherName with the desired name.
Add in the show.jspx file something like:
<div><label for="_pupilTeacher">Teacher Name:</label><div class="box">${teacherName}</div></div><br/>
(alternatively, you could create a new tagx file with your own parameters)
Hope it helped.
Radu