With GWT you have stuff like this:
public interface LoginConstants extends Constants {
#DefaultStringValue("Wellcome to my super app")
#Key("appDescription")
String appDescription();
#DefaultStringValue("Ok")
#Key("okButtonLabel")
String okButtonLabel();
}
Then you can use from your classes doing GWT.create(LoginConstant.class), in this way the interface is backed by dynamic implementation that, when I call loginConstants.appDescription() returns the value contained from a property file using the #Key annotation to reference the key in the property file. If the property file misses the property, then de #DefaultStringValue is returned. This is used for internationalization, but can possibly work also for configuration.
But with GWT, this is meant to be used on the client side (ie. translated to JavaScript), and for i18n, not for configuration.
But, I find this idea very convenient also for configuration handling.
I wonder if somebody knows a framework to do a similar thing on the server side, without necessarily bind your code to GWT. ie. if there is any library that implements this kind of logic specifically designed for the configuration handling. I am not aware of anything like this.
Reference to the feature in GWT: https://developers.google.com/web-toolkit/doc/latest/DevGuideI18nConstants
I implemented my own solution to the question:
BASIC USAGE
The approach used by OWNER APIs, is to define a Java interface
associated to a properties file.
Suppose your properties file is defined as ServerConfig.properties:
port=80
hostname=foobar.com
maxThreads=100
To access this property you need to define a convenient Java interface
in ServerConfig.java:
public interface ServerConfig extends Config {
int port();
String hostname();
int maxThreads();
}
We'll call this interface the Properties Mapping Interface or just
Mapping Interface since its goal is to map Properties into an easy to
use a piece of code.
Then, you can use it from inside your code:
public class MyApp {
public static void main(String[] args) {
ServerConfig cfg = ConfigFactory.create(ServerConfig.class);
System.out.println("Server " + cfg.hostname() + ":" + cfg.port() +
" will run " + cfg.maxThreads());
}
}
But this is just the tip of the iceberg.
Continue reading here: Basic usage || Website || Github
I still have a couple of features in mind, but the current implementation goes a little forward than the basic functionalities described in the questions.
I need to add samples and documentation.
I loved the idea so much that I quickly assembled some code using Java Dynamic proxies.
So basically you create an interface with relevant methods and annotate them with #Key, #DefaultStringValue annotations.
Below is the sample Java code:
Main.java
package net.viralpatel;
import net.viralpatel.annotations.DefaultStringValue;
import net.viralpatel.annotations.Key;
interface LoginConstants extends Constants {
#DefaultStringValue("Wellcome to my super app")
#Key("appDescription")
String appDescription();
#DefaultStringValue("Ok")
#Key("okButtonLabel")
String okButtonLabel();
}
public class Main {
public static void main(String[] args) {
LoginConstants constants = DynamicProperty.create(LoginConstants.class);
System.out.println(constants.appDescription());
System.out.println(constants.okButtonLabel());
}
}
Also the property file in background that we load is
config.property
okButtonLabel=This is OK
Just execute the Main java class, following output will be displayed:
Output:
Wellcome to my super app
This is OK
Here is the rest of code: http://viralpatel.net/blogs/dynamic-property-loader-using-java-dynamic-proxy-pattern/
You could mimic that with spring (but I'm not sure it's worth it):
#Component
public class SomeBean {
#Value("${appDescription:Wellcome to my super app}")
private String appDescription;
#Value("${okButtonLabel:Ok}")
private String okButtonLabel;
// accessors
}
with a PropertyPlaceHolderConfigurer.
I would like to consider the CDI as the following :-
The Qualifier
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({
ElementType.METHOD,
ElementType.FIELD,
ElementType.PARAMETER,
ElementType.TYPE
})
#Documented
public #interface MessageTemplate {
#Nonbinding
String baseName();
#Nonbinding
Locale locale() default Locale.ENGLISH;
#Nonbinding
String key();
}
The Producer
public class CustomizedProducer {
#Produces
#MessageTemplate(baseName = "",
key = "")
public String createMessageTemplate(final InjectionPoint ip) {
MessageTemplate configure = null;
ResourceBundle bundle = null;
try{
configure = ip.getAnnotated().getAnnotation(MessageTemplate.class);
bundle = ResourceBundle.getBundle(configure.baseName(),
configure.locale());
return bundle.getString(configure.key());
} finally{
configure = null;
bundle = null;
}
}
}
The Service Configure
public class MyServiceConfigure {
#Inject
#MessageTemplate(baseName = "com.my.domain.MyProp",
key = "appDescription")
private String appDescription;
#Inject
#MessageTemplate(baseName = "com.my.domain.MyProp",
key = "okButtonLabel")
private String okButtonLabel;
//Getter
}
The working class
public class MyService {
#Inject
private MyServiceConfigure configure;
public void doSomething() {
System.out.println(configure.getAppDescription());
System.out.println(configure.getOkButtonLabel());
}
}
Regarding to the coding above you may use the java.util.Properties instead of the java.util.ResourceBundle and provide the default member to the Qualifier as well.
If you are running these under the JavaEE 6, the CDI is already enable for you. Just put the empty beans.xml to the META-INF or WEB-INF. If you are running under the Java SE you may need a bit further work as mentioned at the Weld web site and its documentation.
I'm using the CDI as a main part of my current production project and it works quite well.
EDITED:-
The good point to use the CDI is the Scope, we may produce the #MessageTemplate as the #ApplicationScope,#SessionScoped, #RequestScoped, #ConversationScoped or the pseudo-scope as #Singleton or #Depenendent
If you annotate the MyServiceConfigure as #Named, it is ready to use at the JSF as well.
Related
I'm trying to achieve something like this:
#Controller
public SomeController {
#CustomConfig("var.a")
private String varA;
#CustomConfig("var.b")
private String varB;
#RequestMapping(value = "/", method = RequestMethod.GET)
public String get() {
return varA;
}
}
CustomConfig would be an #Interface class that accepts one value parameter. The reason why we are not using #Value is because this will not come from config file but from API (such as https://getconfig.com/get?key=var.a). So we are going to make HTTP request to inject it.
So far I've only manage to make something work if the varA and varB is inside get() method as parameter, by using below in a class that extends WebMvcConfigurerAdapter:
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
CustomConfigResolver resolver = new CustomConfigResolver();
argumentResolvers.add(resolver);
}
And inside CustomComfigResolver.resolveArgument() we would do the HTTP query, but that's not really what we wanted, we need it to be injected as class variable.
Does anyone have experience in resolving it at class variable level?
Thank you
This could work if you use #Value instead of your own custom annotation. This uses the built in environment:
#Order(Ordered.HIGHEST_PRECEDENCE)
#Configuration
public class TcpIpPropertySourceConfig implements InitializingBean {
#Autowired
private ConfigurableEnvironment env;
#Autowired
private RestTemplate rest;
public void afterPropertiesSet() {
// Call your api using Resttemplate
RemoteProperties props = //Rest Call here;
// Add your source to the environment.
MutablePropertySources sources = env.getPropertySources();
sources.addFirst(new PropertiesPropertySource("customSourceName", props)
}
}
What you are trying to achieve is difficult when you start to consider "unhappy" scenarios. Server down / not reachable. You need to account for all of that in the method above.
I would highly recommend to instead use Spring Cloud Config. Great guide on that is here: https://www.baeldung.com/spring-cloud-configuration
This provides:
- Reloading of your #Value() properties, so no custom annotation needed.
- A more stable server and great Spring integration out of the box.
Best of all, it is easy to apply Retries and Backoffs if the configuration server goes down (see https://stackoverflow.com/a/44203216/2082699). This will make sure your app doesn't just crash when the server is not available.
I am working on a REST API where I have an interface that defines a list of methods which are implemented by 4 different classes, with the possibility of adding many more in the future.
When I receive an HTTP request from the client there is some information included in the URL which will determine which implementation needs to be used.
Within my controller, I would like to have the end-point method contain a switch statement that checks the URL path variable and then uses the appropriate implementation.
I know that I can define and inject the concrete implementations into the controller and then insert which one I would like to use in each particular case in the switch statement, but this doesn't seem very elegant or scalable for 2 reasons:
I now have to instantiate all of the services, even though I only need to use one.
The code seems like it could be much leaner since I am literally calling the same method that is defined in the interface with the same parameters and while in the example it is not really an issue, but in the case that the list of implementations grows ... so does the number of cases and redundant code.
Is there a better solution to solve this type of situation? I am using SpringBoot 2 and JDK 10, ideally, I'd like to implement the most modern solution.
My Current Approach
#RequestMapping(Requests.MY_BASE_API_URL)
public class MyController {
//== FIELDS ==
private final ConcreteServiceImpl1 concreteService1;
private final ConcreteServiceImpl2 concreteService2;
private final ConcreteServiceImpl3 concreteService3;
//== CONSTRUCTORS ==
#Autowired
public MyController(ConcreteServiceImpl1 concreteService1, ConcreteServiceImpl2 concreteService2,
ConcreteServiceImpl3 concreteService3){
this.concreteService1 = concreteService1;
this.concreteService2 = concreteService2;
this.concreteService3 = concreteService3;
}
//== REQUEST MAPPINGS ==
#GetMapping(Requests.SPECIFIC_REQUEST)
public ResponseEntity<?> handleSpecificRequest(#PathVariable String source,
#RequestParam String start,
#RequestParam String end){
source = source.toLowerCase();
if(MyConstants.SOURCES.contains(source)){
switch(source){
case("value1"):
concreteService1.doSomething(start, end);
break;
case("value2"):
concreteService2.doSomething(start, end);
break;
case("value3"):
concreteService3.doSomething(start, end);
break;
}
}else{
//An invalid source path variable was recieved
}
//Return something after additional processing
return null;
}
}
In Spring you can get all implementations of an interface (say T) by injecting a List<T> or a Map<String, T> field. In the second case the names of the beans will become the keys of the map. You could consider this if there are a lot of possible implementations or if they change often. Thanks to it you could add or remove an implementation without changing the controller.
Both injecting a List or a Map have some benefits and drawbacks in this case. If you inject a List you would probably need to add some method to map the name and the implementation. Something like :
interface MyInterface() {
(...)
String name()
}
This way you could transform it to a Map<String, MyInterface>, for example using Streams API. While this would be more explicit, it would polute your interface a bit (why should it be aware that there are multiple implementations?).
When using the Map you should probably name the beans explicitly or even introduce an annotation to follow the principle of least astonishment. If you are naming the beans by using the class name or the method name of the configuration class you could break the app by renaming those (and in effect changing the url), which is usually a safe operation to do.
A simplistic implementation in Spring Boot could look like this:
#SpringBootApplication
public class DynamicDependencyInjectionForMultipleImplementationsApplication {
public static void main(String[] args) {
SpringApplication.run(DynamicDependencyInjectionForMultipleImplementationsApplication.class, args);
}
interface MyInterface {
Object getStuff();
}
class Implementation1 implements MyInterface {
#Override public Object getStuff() {
return "foo";
}
}
class Implementation2 implements MyInterface {
#Override public Object getStuff() {
return "bar";
}
}
#Configuration
class Config {
#Bean("getFoo")
Implementation1 implementation1() {
return new Implementation1();
}
#Bean("getBar")
Implementation2 implementation2() {
return new Implementation2();
}
}
#RestController
class Controller {
private final Map<String, MyInterface> implementations;
Controller(Map<String, MyInterface> implementations) {
this.implementations = implementations;
}
#GetMapping("/run/{beanName}")
Object runSelectedImplementation(#PathVariable String beanName) {
return Optional.ofNullable(implementations.get(beanName))
.orElseThrow(UnknownImplementation::new)
.getStuff();
}
#ResponseStatus(BAD_REQUEST)
class UnknownImplementation extends RuntimeException {
}
}
}
It passes the following tests:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class DynamicDependencyInjectionForMultipleImplementationsApplicationTests {
#Autowired
private MockMvc mockMvc;
#Test
public void shouldCallImplementation1() throws Exception {
mockMvc.perform(get("/run/getFoo"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("foo")));
}
#Test
public void shouldCallImplementation2() throws Exception {
mockMvc.perform(get("/run/getBar"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("bar")));
}
#Test
public void shouldRejectUnknownImplementations() throws Exception {
mockMvc.perform(get("/run/getSomethingElse"))
.andExpect(status().isBadRequest());
}
}
Regarding two of your doubts :
1. Instantiating the service object should not be an issue as this is one time job and controller gonna need them to serve all type of request.
2. You can use the exact Path mapping to get rid of switch case. For e.g. :
#GetMapping("/specificRequest/value1")
#GetMapping("/specificRequest/value2")
#GetMapping("/specificRequest/value3")
All of the above mapping will be on separate method which would deal with specific source value and invoke respective service method.
Hope this will help to make code more cleaner and elegant.
There is one more option of separating this on service layer and having only one endpoint to serve all types of source but as you said there is different implementation for each source value then it says that source is nothing but a resource for your application and having separate URI/separate method makes the perfect sense here. Few advantages that I see here with this are :
Makes it easy to write the test cases.
Scaling the same without impacting any other source/service.
Your code dealing the each source as separate entity from other sources.
The above approach should be fine when you have limited source values. If you have no control over source value then we need further redesign here by making source value differentiate by one more value like sourceType etc. and then having separate controller for each group type of source.
#Transactional
#Component
#EntranceLog
public class TransferServiceImpl implements TransferService {
xxxx
}
I hava a class with Transactional annotation and Component annotation. EntranceLog is my customize annotation to print log by aop.
public class LogProxyCreator extends AbstractAutoProxyCreator implements ApplicationContextAware {
private static final LogInterceptor LOG = new LogInterceptor();
private static Logger log = LoggerFactory.getLogger(LogProxyCreator.class);
#Override
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String s, TargetSource targetSource) throws BeansException {
Annotation anno = null;
for (Annotation annotationTemp : beanClass.getAnnotations()) {
Log temp = annotationTemp.annotationType().getAnnotation(EntranceLog.class);
if (temp != null) {
anno = temp;
break;
}
}
if (anno == null) {
return null;
}
Object[] additional = new Object[]{LOG};
log.error(beanClass.getName() + " has register the fc log.");
return additional;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
LOG.setContext(applicationContext);
}
}
When my app is starting, the bean transferServiceImpl start, but beanClass.getAnnotations() can not get any annotation. Why?
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE})
#Log(logName = "entrance")
public #interface EntranceLog {
#AliasFor(
annotation = Log.class,
attribute = "subLogName"
)
String logName() default "";
#AliasFor(
annotation = Log.class,
attribute = "openInfoLog"
)
boolean openInfoLog() default false;
}
This is my annotation.
In Spring #Transactionalis already an AOP processed annotation, so adding your own will require some additional work. Let me explain how Spring AOP and #Transactional works.
Spring has two ways of doing AOP, if the class implements an interface it can use a standard JDK Proxy, if the class does not implement an interface it will create a new subclass by using CGLib to emit bytecode at runtime. Unless you are very careful you will almost always get a CGLib proxy with Spring AOP.
When Spring encounters a #Transactional (class or method level) it creates a new subclass using CGLib, you can think of this class as a decorator, which forwards all calls to your implementation class. Before and after (around Advice) it check the #Transactional annotation properties, and check Thread Local storage to see if a transaction already exist, if there is no transaction it creates one, and remembers it so it can commit it afterwards. If you set a breakoint inside a Transactional method and look at the callstack you will see the call to your implementation came from the decorater class, and that there is no source code for it.
In your case the bean that is added to the Application Context, is not your TransferServiceImplbean, but the CGLib proxy created by Spring when it found the #Transactional annotation on your class, it will be named something like TransferServiceImpl$$FastClassBySpringCGLIB$$<hexstring> - This class does not have the #EntranceLog annotation, which is why your own aspect is not working.
I have never encountered this problem myself, as I try to avoid AOP in general, or at always on classes that are already being CGLib proxied by Spring. Unless you want to dig deep into the Spring source, or find someone on the Spring dev team to help you with this, I suggest that you create another layer of indirection, so you don't need to handle two aspects in the same class.
For anyone who may be unwilling or unable to alter their code structure in order to avoid this issue, the following can probably help:
As Klaus mentioned, Spring creates a decorator class when it encounters a class tagged with #Transactional. However, because this new class is just that--a decorator--you should be able to call getSuperclass() on beanClass to give you the actual class Spring is decorating, like so:
beanClass.getSuperclass().getAnnotations()
If you're using your own Annotation, ensure it also persists through runtime by annotating the Annotation class with:
#Retention(RetentionPolicy.RUNTIME)
I am required to read all classes(interfaces) decorated with a particular annotation (say #HttpSecurity) at runtime. After scanning through, I wish to read and parse the fields(enum fields) of class decorated with annotations. For eg.
#HttpSecurity
public interface MyHttpSecurityConfig {
public enum secure {
#Path(pathGroup = "/*", pathName = "")
#Authc
#Form(errorPage = "/error.html", loginPage = "/login.html", restoreOriginalRequest = "")
#Authz
#AllowedRoles(roles = { "roleA", "roleB" })
#AllowedGroups(groups = { "groupA" })
#AllowedRealms(realms = { "realmA" })
#Expressions(expressions = { "#{identity.isLoggedIn()}" })
Admin
}
}
There might be one or more classes/interfaces decorated with #HttpSecurity. My first requirement is to fetch all such classes and second requirement is to build up HttpSecurityBuilder by reading the annotations and their values decorated on enum field(s).
The second requirement is fine and can be done away using reflections. But, my problem is the first requirement. I want to achieve first requirement with JavaSE core i.e., without using any external dependency like google reflections. It might be assumed,if necessary, that we have the package name in which classes are to be scanned. Here is what I did usiNG cdi
You can create a CDI Extension that observes the scan from CDI Annotations and create you customization, as the example below:
1) You need to create a Qualifier, by using you #HttpSecurity
#Qualifier
#Retention(RUNTIME)
#Target({TYPE, METHOD, FIELD, PARAMETER})
public #interface HttpSecurity {}
2) You need to create a extension by implementing the interface javax.enterprise.inject.spi.Extension:
package net.mperon.cdi.extension;
public class MyExtension implements Extension {
private static final Logger log = LoggerFactory.getLogger(MyExtension.class);
public <T> void processAnnotatedType(#Observes ProcessAnnotatedType<T> pat) {
AnnotatedType<T> at = pat.getAnnotatedType();
//if dont have you anotation, just continue
if(!at.isAnnotationPresent(HttpSecurity.class)) {
return;
}
//here you can read all annotation from object and do whatever you want:
log.info("class: {}", at.getJavaClass());
log.info("constructors: {}", at.getConstructors());
log.info("fields: {}", at.getFields());
log.info("methods: {}", at.getMethods());
//and so more...
}
}
3) You can see all methods and properties here
4) At last you need to create a service file, under META-INF/services named javax.enterprise.inject.spi.Extension
5) Inside this text file, you need to put you extension full class name, by example:
net.mperon.cdi.extension.MyExtension
Unfortunately, Java doesn't provide an easy way to list classes in the "native" JRE. my favourite solution is the Google Reflections library, but if you don't want to use it there are other ways. One way would be to find the jar or jars in question and scan them for annotations on class files. This is achieved as follows:
// Jars are really just zip files with a different name
ZipInputStream zip = new ZipInputStream(new FileInputStream("/path/to/jar/file.jar"));
for(ZipEntry entry=zip.getNextEntry(); entry!=null; entry=zip.getNextEntry()) {
String name = entry.getName();
// We only want class files
if(name.endsWith(".class") && !entry.isDirectory()) {
// Remove the .class extension
name = name.substring(0, name.length() - 6);
// Replace the slashes in the path with '.'
name.replaceAll("/",".");
// Get the class object so we can use reflection on it
Class<?> cls = Class.forName(name);
}
}
Maybe you can give a hint, where to find a solution for this problem.
Currently I discover CDI portable extensions, like the sample shown here Wrapping an InjectionTarget.
This CDI portable extension reads values from properties files and configures fields of a Java object.
Here is a snippet of the extension:
public <T> void checkForPropertyFileAnnotation(
final #Observes ProcessInjectionTarget<T> pit) {
AnnotatedType<T> at = pit.getAnnotatedType();
if (!at.isAnnotationPresent(PropertyFile.class)) {
return;
}
// found annotation
[...load properties...]
[...assign properties to fields...]
[...create new wrapped InjectionTarget...]
pit.setInjectionTarget([created InjectionTarget]);
}
Running this extension on an example class does what it should do. But when I apply a, for example LoggingInterceptor like shown here simple cdi interceptor, the extension seems not working.
The logging interceptor:
#Log #Interceptor
public class LoggingInterceptor {
#AroundInvoke
public Object log(InvocationContext ctx) throws Exception {
Logger logger = Logger.getLogger(ctx.getTarget().getClass().getName());
logger.info("before");
Object result = ctx.proceed();
logger.info("after");
return result;
}
}
The sample-class looks like:
#Named
#Model // to use EL in jsp/jsf
#Log // the interceptor annotation
#PropertyFile("myprops.txt") // the annotation used within the extension
public class MyProperties {
#Property("version")
Integer version;
#Property("appname")
String appname;
public Integer getVersion() {
return version;
}
public String getAppname() {
return appname;
}
}
The content of the result-page:
<h:body bgcolor="white">
#{myProperties.appname} v#{myProperties.version}
</h:body>
It's not really true, the extension works, means it injects the appropriate values into the desired fields as I can see in the log-file, but after the interceptor gets applied, the injected values are gone.
Do you have any idea why this can happen? Maybe the extension handles a different instance of the class than the interceptor.
Thanx in advance for your reply!
You're twiddling fields on a proxy class, and then the getters are getting the values from the underlying instance.
I had the same problem. Both the interceptor and extension work separately, but when used together, the interceptor stops working. This is due to a bug in OpenWebBeans CDI implementation where interceptors are only processed when the injection target is an instance of an OpenWebBeans specific InjectionTargetImpl.
See: https://issues.apache.org/jira/browse/OWB-897