I defined an annotation as follows
import java.lang.annotation.ElementType
import java.lang.annotation.Inherited
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Target
/**
* Annotation for any object that exposed a remote interface
*/
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface Remote {
String label()
}
and i'm trying to use it this way
import com.yascie.annotation.Remote
#Remote("Bar")
class Foo {
String name
String value
static String code
}
I keep getting though an error saying that the annotation is missing element label
java.lang.annotation.IncompleteAnnotationException: Remote missing element label
Now when i tried to inspect the annotation object i can see that a label method is available trough a proxy but i can't access it. Any ideas ?
Remote annotation = objectClass.clazz.getAnnotation(Remote.class);
annotation.metaClass.methods.each {println it}
public final java.lang.String $Proxy14.label()
ken
You have two options. If you want to use the #Remote("Bar") syntax then you need to change the label() method to value() since that's the method name for the default property for annotations when the name isn't specified.
If you want it to be called label() though, specify it as #Remote(label="Bar")
Related
The docs say that depending on version, accessing Domain.constraints or Domain.constrainedProperties should give a Map of key values.
https://grails.github.io/grails2-doc/2.5.4/ref/Domain%20Classes/constraints.html
At runtime the static constraints property is a Map such that the keys in the Map are property names and the values associated with the keys are instances of ConstrainedProperty:
However, using 2.5+, accessing the constraints property at runtime doesn't give a map, but a closure, and I can't access the ConstrainedProperty instances.
I tried using grails class utils to access the static property also
GrailsClassUtils.getStaticFieldValue(Domain,"constraints")//this is still a closure
GrailsClassUtils.getStaticFieldValue(Domain,"constrainedProperties")//null, this property doesn't exist below version 3.0
Property access doesn't work for me like the example in the docs
Domain.constraints //returns closure
but using the method getter does
Domain.getConstraints() //returns the map
See the project at https://github.com/jeffbrown/constraintsmapdemo.
https://github.com/jeffbrown/constraintsmapdemo/blob/master/grails-app/domain/demo/Widget.groovy:
package demo
class Widget {
int width
int height
static constraints = {
width range: 1..100
height range: 1..50
}
}
The test at https://github.com/jeffbrown/constraintsmapdemo/blob/master/test/unit/demo/WidgetSpec.groovy passes:
package demo
import grails.test.mixin.TestFor
import spock.lang.Specification
#TestFor(Widget)
class WidgetSpec extends Specification {
void "test accessing the constraints property"() {
when:
def propValue = Widget.constraints
then:
propValue instanceof Map
propValue.containsKey 'width'
propValue.containsKey 'height'
}
}
If you are not using static compilation, Widget.constraints will evaluate to the Map. If you are using static compilation, Widget.getConstraints() will return the Map but Widget.constraints will evaluate to the closure.
Not sure if I'm thinking right about this, I'm looking[in CDI] for something similar to what we have in Spring - #ConditionalOnMissingBean - that allows you tell spring - create only if the bean specified is missing.
I've tried using extensions, looks like one can tap several events, and use those to VETO beans. One way might be to have BeanManager at this stage, and look for already present beans, and if it contains the one you're about to inject, VETO this one. BUT, this would only work when we HAVE LOOKED AT ALL the beans.
AfterBeanDiscovery looks suitable, however, before it is invoked, validation fails, complaining of multiple beans of the same type.
Would be great if I could get some help here.
Your question is interesting and can be solved using a CDI extension (almost as you describe, actually), see below for a naive, working, proof-of-concept implementation. It is naive because it does not handle e.g. producer methods/fields and may be missing more.
CDI extensions are really great and powerful, but can be rather technical, so let's discuss other alternatives first.
Specialization: Maybe it is enough for your use case to document explicitly that you provide the default implementation of SomeService through, say, public class SomeServiceDefaultImpl and in order to override it the developer should do:
#Specializes
public class SomeServiceSpecialImpl extends SomeServiceDefaultImpl {...}
Also consider the alternatives, as mentioned in the comment from John Ament.
Qualifiers: If this service is used only in one place/a few places and only inside your code, you could qualify your SomeServiceDefaultImpl with a custom qualifier, say #MyDefaultImpl. Then inject an Instance<SomeService>, look for an unqualified instance first and, if that is not satisfied, look for the qualified - something along the lines of:
private SomeService someService;
#Inject
void setSomeServiceInstance(Instance<SomeService> s) {
// not tried, please adapt as needed
if( s.isUnsatisfied() ) {
someService = s.select(new MyDefaultImplAnnotation()).get();
}
else {
someService = s.get();
}
}
Provide a default implementation that is #Vetoed so as to force the client of your code to provide an implementation. If the client wants to use the default, they can simply use a producer.
Having said the above, the implementation below is a proof of concept that:
Requires the following annotation to be present on the default implementation:
#Target({ TYPE, METHOD, FIELD })
#Retention(RUNTIME)
#Documented
public #interface ConditionalOnMissingBean {
Class<?> value();
}
The value() is required and denotes the bean type that is "defaulted". Your implementation can be smarter, i.e. detect the bean type from the actual default implementation, but, hey, that's only a proof of concept!
Blatantly ignores producers!
Is lightly tested, so there are probably evil corner cases, so BEWARE!
In addition to the code you need all the choreography of an extension (META-INF/services/javax.enterprise.inject.spi.Extension, beans.xml).
import java.util.HashMap;
import java.util.Map;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.AfterBeanDiscovery;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanAttributes;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.InjectionTargetFactory;
import javax.enterprise.inject.spi.ProcessAnnotatedType;
public class ConditionalOnMissingBeanExtension implements Extension {
private Map<Class<?>, AnnotatedType<?>> map = new HashMap<>();
<T> void processAnnotatedType(#Observes ProcessAnnotatedType<T> pat) {
AnnotatedType<?> annotatedType = pat.getAnnotatedType();
ConditionalOnMissingBean annotation = annotatedType.getAnnotation(ConditionalOnMissingBean.class);
if( annotation != null ) {
map.put(annotation.value(), annotatedType);
pat.veto();
}
}
void afterBeanDiscovery(#Observes AfterBeanDiscovery abd, BeanManager beanManager) {
map.entrySet().stream()
.filter(e -> doesNotHaveBeanOfType(beanManager, e.getKey()))
.map(e -> defineBean(beanManager, e.getValue()))
.forEach(abd::addBean);
map = null;
}
private boolean doesNotHaveBeanOfType(BeanManager beanManager, Class<?> type) {
return beanManager.getBeans(type).isEmpty();
}
private <T> Bean<T> defineBean(BeanManager beanManager, AnnotatedType<T> annotatedType) {
BeanAttributes<T> beanAttributes = beanManager.createBeanAttributes(annotatedType);
InjectionTargetFactory<T> injectionTargetFactory = beanManager.getInjectionTargetFactory(annotatedType);
return beanManager.createBean(beanAttributes, annotatedType.getJavaClass(), injectionTargetFactory);
}
}
An example of a default implementation of a service interface would be:
#ApplicationScoped
#ConditionalOnMissingBean(SomeService.class)
public class SomeServiceDefaultImpl implements SomeService {
#Override
public String doSomeCalculation() {
return "from default implementation";
}
}
I am using grails 2.5.5 and the problem is when I'm using a command Object, I want to change the format of the data I get with the #BindUsing annotation.
#Validatable
class FooCommand extends BarCommand{
#BindUsing({obj, source -> return source['foo'].collect{it['id']}})
List<Long> foo
static constraints = {foo(nullable:true)}
}
the BindUsing closure never gets executed. What is the problem and how to solve?
JSONData = {"foo":[
{'id':5},
{'id':4}
]
}
FooCommand fooCommand = FooCommand(JSONData)
fooCommand.validate()
EDIT:
To be clear: the problem is that the data change from format,from list of maps, to just a list of longs. Is there a way to do this with the BindUsing or will i just have to do it in the controller each time?
Say I have the following Annotation and 2 classes:
class AppModel extends Reflectable {
final String name;
const AppModel([this.name])
: super(newInstanceCapability, metadataCapability);
}
const appModel = const AppModel();
#appModel
class ImGonnaBePickedUp {
}
#AppModel(' :( ')
class AndImNotPickedUpOnServer_IDoOnWebClient {
}
main() {
appModel.annotatedClasses // that's what I mean by "Picked Up".
}
On CmdApp side (Server): only AndImNotPickedUpOnServer_IDoOnWebClient is given in appModel.annotatedClasses.
On the web side, both classes are given.
Long story short, how do I retrieve classes annotated with direct const constructor calls like in the example above #AppModel(' :( ') (for both CmdApp and Web)?
since version 0.5.4 reflectable classes doesn't support constructors with arguments
This appears in reflectable documentation:
Footnotes: 1. Currently, the only setup which is supported is when the metadata object is an instance of a direct subclass of the class [Reflectable], say MyReflectable, and that subclass defines a const constructor taking zero arguments. This ensures that every subclass of Reflectable used as metadata is a singleton class, which means that the behavior of the instance can be expressed by generating code in the class. Generalizations of this setup may be supported in the future if compelling use cases come up.
one possible solution could be to use a second annotation to handle the name, for example:
import 'package:reflectable/reflectable.dart';
import 'package:drails_commons/drails_commons.dart';
class AppModel extends Reflectable {
const AppModel()
: super(newInstanceCapability, metadataCapability);
}
const appModel = const AppModel();
class TableName {
final String name;
const TableName(this.name);
}
#appModel
class ImGonnaBePickedUp {
}
#appModel
#TableName(' :( ')
class AndImNotPickedUpOnServer_WorksOnWebClient {
}
main() {
print(appModel.annotatedClasses); // that's what I mean by "Picked Up".
print(new GetValueOfAnnotation<TableName>()
.fromDeclaration(appModel.reflectType(AndImNotPickedUpOnServer_WorksOnWebClient)).name);
}
Note: I'm also using drails_common package
I'm trying to create a table with a BeanContainer after populating it. However, when I try to run it, I get the following error message:
java.lang.IllegalStateException: Property RoomID not found
The relevant code is below (in which rooms can create a list of random Room objects). The Room class has a RoomID integer, and while it is a private variable the code produces the same error even if RoomID isn't private. I've also made sure that r_list actually contains Room instances.
BeanContainer<Integer, Room> r_cont = new BeanContainer<Integer, Room>(Room.class);
r_cont.setBeanIdProperty("RoomID");
//fetches all rooms and adds them to the bean container
List<Room> r_list = rooms.getRooms();
for (Room room: r_list)
r_cont.addBean(room);
EDIT: Here's the notable part of the Room object. I left out the initialization method and the methods for setting/getting the other variables.
package Entities;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="Room")
public class Room {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="RoomID")
private int RoomID;
...
public int getRoomID(){
return RoomID;
}
...
}
The property name is not deducted from the the actual variable holding it, but from the getter/setter. This means it should be roomId (see e.g. Where is the JavaBean property naming convention defined?). If you are not sure about the properties you hold, you can debug it on the container with: .getContainerPropertyIds().