I have the following Groovy classes:
enum Protocol {
File,
Ftp,
Sftp,
Http,
Https
}
#Canonical
abstract class Endpoint {
String name
Protocol protocol
}
#Canonical
#TupleConstructor(includeFields=true, includeSuperFields=true)
class LocalEndpoint extends Endpoint {
}
class MyAppModule extends AbstractModule {
#Override
protected void configure() {
// Lots of stuff...
}
// Lots of other custom providers
#Provides
Endpoint providesEndpoint() {
new LocalEndpoint('fileystem', Protocol.File)
}
}
Don't worry about why I'm using a custom provider for the Endpoint instead of just:
bind(Endpoint).toInstance(new LocalEndpoint('fileystem', Protocol.File))
I'm 99.999% sure that's outside of this problem and is coded that way because of how the full (very large) code is wired.
My problem is that Guice and/or Groovy cannot find a constructor for LocalEndpoint that takes a String and Protocol argument:
1) Error in custom provider, groovy.lang.GroovyRuntimeException: Could not find matching constructor for: com.example.myapp.model.LocalEndpoint(java.lang.String, com.example.myapp.model.Protocol)
at com.example.myapp.inject.MyAppModule.providesEndpoint(MyAppModule.groovy:130)
while locating com.example.myapp.model.Endpoint
for parameter 2 at com.example.myapp.inject.MyAppModule.providesConfig(MyAppModule.groovy:98)
at com.example.myapp.inject.MyAppModule.providesConfig(MyAppModule.groovy:98)
while locating com.example.myapp.config.MyAppConfig
It then spits out a large stack trace with the following listed as the cause:
Caused by: groovy.lang.GroovyRuntimeException: Could not find matching constructor for: com.example.myapp.model.LocalEndpoint(java.lang.String, com.example.myapp.model.Protocol)
at groovy.lang.MetaClassImpl.invokeConstructor(MetaClassImpl.java:1731)
at groovy.lang.MetaClassImpl.invokeConstructor(MetaClassImpl.java:1534)
Hopefully this is something that I can tweak by modifying Endpoint and/or LocalEndpoint, perhaps I need to pass some special parameters into the #Canonical/#TupleConstructor annotations or something. Any ideas?
I think you need to add includeSuperProperties in the TupleConstructor annotation, and that seems to resolve it, even by itself:
#TupleConstructor(includeSuperProperties=true)
So the whole thing would be:
#Canonical
abstract class Endpoint {
String name
Protocol protocol
}
#Canonical // You may not need this anymore
#TupleConstructor(includeSuperProperties=true)
class LocalEndpoint extends Endpoint {
}
Related
I have an existing program with some plugin infrastructure that currently relies on plugin classes having parameterless constructors. I'd like to offer plugin authors the opportunity to just require some infrastructure from my program by specifying parameters for the constructor.
Internally, I use some generic wrapper class to encapsulate the plugin's classes and to make them behave to the rest of my program like older pre-plugin era internal classes.
I have some placeholder here representing my infrastructure:
public interface IInfrastructure
{
}
public class Infrastructure : IInfrastructure
{
}
Some plugin interface specification:
public interface IPlugin
{
}
the plugin implementation requiring my infrastructure:
public class Plugin : IPlugin
{
public Plugin(IInfrastructure _)
{
}
}
and my generic wrapper class expecting some plugin class
public class PluginWrapper<TImpl> where TImpl: class, IPlugin
{
public PluginWrapper(TImpl _)
{
}
}
After registering the involved types:
ServiceLocator.Default.RegisterType<IInfrastructure, Infrastructure>(RegistrationType.Transient);
ServiceLocator.Default.RegisterType(typeof(Plugin),typeof(Plugin), RegistrationType.Transient);
var wrapperType = typeof(PluginWrapper<>).MakeGenericType(typeof(Plugin));
ServiceLocator.Default.RegisterType(wrapperType, wrapperType,RegistrationType.Transient);
I find out that I can resolve the "inner" plugin type:
Assert.NotNull(ServiceLocator.Default.ResolveType<Plugin>());
but I can't resolve the "Wrapper" type.
Assert.NotNull(ServiceLocator.Default.ResolveType<PluginWrapper<Plugin>>());
Am I hitting a limitation of Catel's IoC container, or am I doing something wrong?
When not using the generic registration method, I passed the registration type in the position of the "tag" parameter by accident.
So, changing the registration part to this version:
ServiceLocator.Default.RegisterType<IInfrastructure, Infrastructure>(RegistrationType.Transient);
ServiceLocator.Default.RegisterType(typeof(Plugin),typeof(Plugin),registrationType:RegistrationType.Transient);
var wrapperType = typeof(PluginWrapper<>).MakeGenericType(typeof(Plugin));
ServiceLocator.Default.RegisterType(wrapperType, wrapperType, registrationType:RegistrationType.Transient);
fixes the problem and everything goes as expected.
I have a Repository interface that has two implementations. One reads data from a locally stored CSV file while the other reads from an Amazon Dynamo DB. I would like to be able to switch between which implementation I'm using based on an application property or custom build profile. I would normally use a Factory to retrieve the correct class at runtime, but I would like to do this with injection if possible.
I found a similar question using Spring boot but couldn't find an equivalent that would work in Quarkus Spring choose bean implementation at runtime
I also tried implementing a Configuration class similar to what is found in the docs here but again didn't have much luck. https://quarkus.io/guides/cdi-reference#default_beans
It feels like I'm missing something obvious so any pointers would be much appreciated.
Here is a simple example of my classes:
#ApplicationScoped
public class ExampleService {
#Inject
ExampleRepository repository;
public List<Data> retrieveData() {
return repository.retrieveData();
}
}
public interface ExampleRepository {
List<Data> retrieveData();
}
#ApplicationScoped
public class DynamoRepository implements ExampleRepository {
#Override
public List<Data> retrieveData() {
//Get Data from DynamoDb
}
}
#ApplicationScoped
public class CsvRepository implements ExampleRepository {
#Inject
CsvBeanHandler csvBeanHandler;
#Inject
LocalFileReader fileReader;
#Override
public List<Data> retrieveData() {
// Get data from CSV
}
}
I currently also have the following in my application.yml:
com:
example:
application:
storage-type: 'CSV' # OR AMAZON_DYNAMO_DB
It looks like they've added this directly to the documentation:
https://quarkus.io/guides/cdi-reference#declaratively-choose-beans-that-can-be-obtained-by-programmatic-lookup
I feel a bit guilty pasting this much, but it's the SO way.
I can add that it is NOT like a Guice 'binding'; BOTH classes will be instantiated, but only one will be injected. Also unlike Guice, you cannot inject the interface (or I did it wrong) - you have to do what's shown below, with Instance.
Personally I just use constructor injection and then drop the value of the Instance wrapper into a final field, so I'm not crying about the extra step. I do miss the power and explicit bindings possible with Modules ala Guice, but the simplicity here has its own value.
5.16. Declaratively Choose Beans That Can Be Obtained by Programmatic Lookup
It is sometimes useful to narrow down the set of beans that can be
obtained by programmatic lookup via javax.enterprise.inject.Instance.
Typically, a user needs to choose the appropriate implementation of an
interface based on a runtime configuration property.
Imagine that we have two beans implementing the interface
org.acme.Service. You can’t inject the org.acme.Service directly
unless your implementations declare a CDI qualifier. However, you can
inject the Instance instead, then iterate over all
implementations and choose the correct one manually. Alternatively,
you can use the #LookupIfProperty and #LookupUnlessProperty
annotations. #LookupIfProperty indicates that a bean should only be
obtained if a runtime configuration property matches the provided
value. #LookupUnlessProperty, on the other hand, indicates that a bean
should only be obtained if a runtime configuration property does not
match the provided value.
#LookupIfProperty Example
interface Service {
String name();
}
#LookupIfProperty(name = "service.foo.enabled", stringValue = "true")
#ApplicationScoped
class ServiceFoo implements Service {
public String name() {
return "foo";
}
}
#ApplicationScoped
class ServiceBar implements Service {
public String name() {
return "bar";
}
}
#ApplicationScoped
class Client {
#Inject
Instance<Service> service;
void printServiceName() {
// This will print "bar" if the property "service.foo.enabled" is NOT set to "true"
// If "service.foo.enabled" is set to "true" then service.get() would result in an AmbiguousResolutionException
System.out.println(service.get().name());
}
}
If your request is to bind at startup time the right implementation based on a configuration property, I suppose your problem may be resolved used #Produces annotation:
public class ExampleRepositoryFactory {
#Config("storage-type")
String storageType;
#Produces
public ExampleRepository dynamoInstance() {
return storageType == "CSV" ? new CsvRepository() : new DynamoRepository();
}
}
If the following is entered in Eclipse/STS (with groovy):
interface iFaceWithAnIssue {
def thisIsFine(a,b,c)
def thisHasProblems(alpha='va')
}
The only line that complains is the one trying to use a default value. I can not tell from the codehaus site if this is supported or not.
The IDE error is:
Groovy:Cannot specify default value for method parameter
So this makes me think it is not supported. As there will be multiple implementations, I wanted to use an interface here. I don't really need the default value in the interface, but there is an error trying to fulfill the interface contract if the implementation class then tries to default this argument. Is there any way?
No, you cannot.
When you define a default value, Groovy actually creates multiple methods in your class, so for example:
class Test {
void something( a=false ) {
println a
}
}
Actually creates
public void something(java.lang.Object a) {
this.println(a)
}
and
public void something() {
this.something(((false) as java.lang.Object))
}
This can't be done as it stands in Interfaces.
You could do:
interface iFaceWithAnIssue {
def thisHasProblems()
def thisHasProblems(alpha)
}
Then
class Test implements iFaceWithAnIssue {
// This covers both Inteface methods
def thisHasProblems(alpha='va') {
// do something
}
}
I'm trying to dynamicly crate an annotation that will dynamicaly add an #XmlElement annotation to every field in a class using metaprogramming and AST. I'm having problems creating the annotations and applying them to the fields properly.
The code i have is formatted here: http://pastebin.com/60DTX5Ya
import javax.xml.bind.annotation.XmlElement
#GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
class WebserviceAnnotationModifier implements ASTTransformation {
#Override
void visit(ASTNode[] astNodes, SourceUnit sourceUnit) {
if (!astNodes) return
if (!astNodes[0] || !astNodes[1]) return
if (!(astNodes[0] instanceof AnnotationNode)) return
if (!(astNodes[1] instanceof ClassNode)) return
ClassNode node = (ClassNode)astNodes[1]
List fields = node.getFields()
fields.each {FieldNode field ->
field.addAnnotation(ClassHelper.make(new XmlElement.DEFAULT()));
}
}
}
#Retention(RetentionPolicy.SOURCE)
#Target([ElementType.TYPE])
#GroovyASTTransformationClass(classes =[WebserviceAnnotationModifier])
public #interface WebresourceAnnotation{}
#WebresourceAnnotation
class TestPerson{
String name;
String lastName;
int Age
}
Am i approaching this all wrong? The reason i do this is i have a domain that is still in the making and i'd like to just go in and apply the annotation to all fields. Couldn't find any examples of annotations added during compilation. Is this not possible?
Writing codes using Groovy AST Transformation alone does not work with the Grails reloading mechanism. Here's a proper way to implement AST transformation for a Grails app.
Your transformer class must extends AbstractGrailsArtefactTransformer.
Your transformer class must be annotated by #AstTransformer.
You class must be put under org.codehaus.groovy.grails.compiler or its sub-package. In my case I use org.codehaus.groovy.grails.compiler.zk and it's working fine.
Implement shouldInject() to match only classes you want, in this case domain classes.
Override performInjection() and write your codes there.
Pack your transformer and releated classes into a .jar file, or Grails compiler does not load it.
I'm trying to use Groovy #groovy.transform.Immutable to implement classes with properties of unsupported "immutable" types. In my case it is java.io.File
For example, having class like
#groovy.transform.Immutable class TwoFiles {
File file1,file2
}
gives me following compile error
Groovyc: #Immutable processor doesn't know how to handle field 'file1' of type 'java.io.File' while compiling class TwoFiles.
#Immutable classes only support properties with effectively immutable types including:
- Strings, primitive types, wrapper types, BigInteger and BigDecimal, enums
- other #Immutable classes and known immutables (java.awt.Color, java.net.URI)
- Cloneable classes, collections, maps and arrays, and other classes with special handling (java.util.Date)
Other restrictions apply, please see the groovydoc for #Immutable for further details
One option I found it to extend java.io.File to make it Cloneable but I'm not happy with this solution. Following code compiles and works, but having own subclass of java.io.File is not what I'd like.
#groovy.transform.Immutable class TwoCloneableFiles {
FileCloneable file1,file2
class FileCloneable extends File implements Cloneable{
FileCloneable(String s) {
super(s)
}
// ... and other constructors ...
}
}
So my question is: Is there any other option how to use java.io.File directly in such class?
Possibly to mark java.io.File as "known immutable" for the purpose of #groovy.transform.Immutable (same as it seems to be done for java.awt.Color, java.net.URI)?
Have you tried using knownImmutableClasses to specify File? Something like this should work:
#groovy.transform.Immutable(knownImmutableClasses = [File])
class TwoFiles {
File file1,file2
}
(With File, you could probably also get rougly the effect you want with the following:
#groovy.transform.Immutable
class TwoFiles {
String file1,file2
public File getFile1() {return new File(file1)}
public File getFile2() {return new File(file2)}
}
def f = new TwoFiles("/", "/Users")
assert f.file1.class == File
)