How to inject a Logger in an enum - java

When I try to inject my Logger producer in an enum, I get a NPE. How can I inject a Logger in an enum?
Example:
public enum MyEnum {
HI("Hi there!"),
HELLO("Hello mister!");
#Inject
private Logger log;
private final String greeting;
private MyEnum(String greeting) {
this.greeting = greeting;
// this.log = LoggerFactory.getLogger(this.getClass());
}
public String getGreeting() {
log.debug("Method getGreeting called");
return this.greeting;
}
}
This class gives me a NPE on the log.debug() line. When I remove the #Inject and uncomment the this.log line it works.
Testcase looks like this:
#RunWith(Arquillian.class)
public class CoverKindTest {
#Deployment
public static WebArchive createDeployment() {
return ShrinkWrap.create(WebArchive.class, "test.war")
.addClass(MyEnum.class)
.addClass(LoggerProducer.class)
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
}
#Test
public void testEnum() {
MyEnum myEnum = MyEnum.HI;
String greeting = myEnum.getGreeting();
assertThat("Should give the greeting.", greeting, is("Hi there!"));
}
}
Complete testable project for this question can be found here, MyEnum.class is the original question, MyEnum1.class is the solution without injection (working, but not what I am looking for) and MyEnum2.class is a suggested answer.
Edit: Updated the GitHub repo with a working solution.
https://github.com/martijnburger/how-to-inject-a-logger-in-an-enum

This direct injection won't work since enum is static.
You can either create a new logger in your enum class
private static final Logger log = Logger.getLogger(Myenum.class.getName());

Found a solution that works!
I created a helper class as follows:
public class LoggerHelper {
private static Logger logger;
private void injectLogger(#Observes #Initialized(ApplicationScoped.class) Object context,
Logger logger) {
LoggerHelper.logger = logger;
}
public static Logger getLogger() {
return logger;
}
}
Now I can inject the logger in the Enum using:
private final Logger log = LoggerHelper.getLogger();

Enums cannot be injected, as they're static.
If you still want injection (instead of just creating the Logger), then you need to create a static class inside your enum that has either setter or constructor injection. When the setter or constructor gets called by the DI framework, take the value it gives you and assign it yourself to a static value in the enum.
The enum can access it now as needed. Beware though, the value will be null if your class was not injected yet.
Something like this:
public enum MyEnum {
HI("Hi there!"),
HELLO("Hello mister!");
private static Logger log;
private final String greeting;
private MyEnum(String greeting) {
this.greeting = greeting;
}
public String getGreeting() {
log.debug("Method getGreeting called");
return this.greeting;
}
#Component
public static class InjectionHelper {
#Inject
public InjectionHelper(Logger log) {
MyEnum.log = log;
}
}
}

You can create a Singleton Service class (created by your respective DI Framework - say Spring), Inject this log inside that class and use it in your enum.
Here is a sample code which works. (Replace the Service annotation with a bean tag for this class in XML, if you're using XML way of doing it. Also you can neglect the lombok #Getter annotation and replace it with a static getter)
// Service which is actually a Utility class but have DI Managed Beans.
#Service("staticService")
public class StaticService {
#Getter
private static Logger log;
#Inject
StaticService(Logger log) {
StaticService.log = log;
}
}
Now in your corresponding Enum:
public String getGreeting() {
StaticService.getLog().debug("Method getGreeting called");
return this.greeting;
}
I used a similar pattern for one of my classes (In Spring) and the injection worked.
Logic:
We first create an instance of the class (singleton instance) and inject the needed variable in the constructor.
Since this gets called during spring initialization (During Application startup), the log is initialized and we manually assign the static variable of log with the injected object in the constructor.
Note:
Don't forget the Inject annotation in the constructor Args.
Better to not provide a setter method to this log object. As its static and shared, we don't want people to replace this value post-construction. (Making it final is not an option as its static).

Related

How does Autowire work when referring only to a base class

I am trying to extend existing code which auto-wires a number of components which share the same base class and are only referred to by their base class, ei:
#Configuration
public class NavigationHotKeyBindConfigs {
private static final Logger logger = LogManager.getLogger(NavigationHotKeyBindConfigs.class);
#Autowired
private HotKeyConfig hotKeyConfig;
#Autowired
private ANavigation upNavigation;
#Autowired
private ANavigation downNavigation;
#Autowired
private ANavigation centerOnSelection;
#Autowired
private ANavigation handVerify;
public abstract class ANavigation {
#Autowired
protected ScrollSchedulerConfigurer scrollSchedulerConfigurer;
public abstract void execute(DetectionView DetectionView);
}
#Component
public class DownNavigation extends ANavigation{
private static final Logger logger = LogManager.getLogger(DownNavigation.class);
public void execute(DetectionView detectionView){
logger.info(String.format("Received: Navigation key %s", ENavigationKey.DOWN.name()));
scrollSchedulerConfigurer.stop();
detectionView.selectNextDetection();
}
}
#Component
public class UpNavigation extends ANavigation{
private static final Logger logger = LogManager.getLogger(UpNavigation.class);
public void execute(DetectionView detectionView){
logger.info(String.format("Received: Navigation key %s", ENavigationKey.Up.name()));
scrollSchedulerConfigurer.stop();
detectionView.selectPrevDetection();
}
}
I don't understand how spring is ever supposed to match up upNavigation/downNavigation with the UpNavigation/DownNavigation components respectively. Was this just bad design from the original author or is there a subtlety I'm missing?
This lack of understanding is also causing some issues as the centerOnSelection and handVerify objects end up NULL after autowiring though they follow the same pattern in every way I can see. Interestingly, this last behavior is different when run in the intellij IDE vs when run out of a .jar
You can do it like this ...
Let's say we have two beans which have the same base class or interface
#Component("fooFormatter")
public class FooFormatter implements Formatter {
public String format() {
return "foo";
}
}
#Component("barFormatter")
public class BarFormatter implements Formatter {
public String format() {
return "bar";
}
}
Then we inject them like this ...
public class FooService {
#Autowired
#Qualifier("fooFormatter")
private Formatter formatter;
}
From the point of view of clarity and maintainability this seems to be the best solution
#Autowired first looks at the type of the variable and then the name of the variable to match the correct bean. By default, the name of a class marked with #Component is its short name, e.g. upNavigation for the UpNavigation class.
#Autowired
private ANavigation upNavigation;
is the same as
#Autowired
#Qualifier("upNavigation")
private ANavigation upNavigation;
And
#Component
public class UpNavigation
is the same as
#Component("upNavigation")
public class UpNavigation
Since the names match, Spring is able to find the corresponding bean.

BeanCreationException error when referencing configuration class inside a service class

I am trying to #Autowire a #Configuration class inside a #Service class. basically my #Configuration class contains mapping to my custom .properties file. When i try to autowire my configuration class inside my service class, BeanCreationException occurs. I am not sure what happen. Just followed the guide on creating Property classes from spring. There must be something i missed out.
Also, when i try to autowire #Configuration class to another #Configuration class, it runs smoothly
Currently, i know that, prop is always null because when i remove prop.getUploadFileLocation() call, everything will be fine. There must be something wrong during autowiring.
Here is my Service class
#Service
public class ImageService {
public static Logger logger = Logger.getLogger(ImageService.class.getName());
#Autowired
MyProperties prop;
private final String FILE_UPLOAD_LOCATION = prop.getUploadFileLocation() +"uploads/images/";
public void upload(String base64ImageFIle) throws IOException {
logger.info(FILE_UPLOAD_LOCATION);
}
}
Here is my Configuration class
#Data
#Configuration
#ConfigurationProperties (prefix = "my")
public class MyProperties {
private String resourceLocation;
private String resourceUrl;
public String getUploadFileLocation() {
return getResourceLocation().replace("file:///", "");
}
public String getBaseResourceUrl() {
return getResourceUrl().replace("**", "");
}
}
And here is where i can successfully use MyProperties
#Configuration
public class StaticResourceConfiguration implements WebMvcConfigurer {
#Autowired
MyProperties prop;
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(prop.getResourceUrl())
.addResourceLocations(prop.getResourceLocation());
}
}
The issue is that you are trying to use an autowired field to set the value in an inline field assignment.
That means
private final String FILE_UPLOAD_LOCATION = prop.getUploadFileLocation() +"uploads/images/";
is executed before the prop is autowired, meaning it will always be null
The way to mitigate this would be to use constructor injection instead.
#Service
public class ImageService {
//Fine since you are using static method
public static Logger logger = Logger.getLogger(ImageService.class.getName());
//Not needed if you are only using it to set FILE_UPLOAD_LOCATION
//Allows field to be final
private final MyProperties prop;
//Still final
private final String FILE_UPLOAD_LOCATION;
//No need for #Autowired since implicit on component constructors
ImageService(MyProperties prop){
//Again not needed if you aren't going to use anywhere else in the class
this.prop = prop;
FILE_UPLOAD_LOCATION = prop.getUploadFileLocation() +"uploads/images/";
}
public void upload(String base64ImageFIle) throws IOException {
logger.info(FILE_UPLOAD_LOCATION);
}
}
See this question for why constructor is preferred over #autowired in general
If you need MyProperties bean to be created before StaticResourceConfiguration bean, you can put #ConditionalOnBean(MyProperties.class) as following. Spring will make sure MyProperties is there before processing StaticResourceConfiguration.
#Configuration
#ConditionalOnBean(MyProperties.class)
public class StaticResourceConfiguration implements WebMvcConfigurer {

Is this an example of a 'Static Bean'?

public class SomeClass {
private static final int num = 432;
#Bean
public int getNum(){
return num;
}
}
or would the method signature need to actually have the static keyword ?
I am not entirely sure about what you mean about a static Bean, Beans are instances in runtime.
If you mean Singleton, meaning that the bean would be created on application start and destroyed in application end.
Then by default, every Bean is #Bean(scope=DefaultScopes.SINGLETON), if you want a bean to be created every new usage of it, you can define it as #Bean(scope=DefaultScopes.PROTOTYPE)
Take a look at the doc: https://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch02s02.html
My understanding of a static bean would be different. As an example, take a look at the EventPublisherHolder class from Eclipse Hawkbit:
public final class EventPublisherHolder {
private static final EventPublisherHolder SINGLETON = new EventPublisherHolder();
#Autowired
private ApplicationEventPublisher eventPublisher;
public static EventPublisherHolder getInstance() {
return SINGLETON;
}
public ApplicationEventPublisher getEventPublisher() {
return eventPublisher;
}
...
}
The way the ApplicationEventPublisher is injected into the EventPublisherHolder is through Spring magic
#Bean
EventPublisherHolder eventBusHolder() {
return EventPublisherHolder.getInstance();
}
The EventPublisherHolder class makes it easier to get the ApplicationEventPublisher statically.
As an example, take a look at the way this class is intended to be used:
From JpaAction class:
#Override
public void fireCreateEvent(final DescriptorEvent descriptorEvent) {
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new ActionCreatedEvent(...));
}
In this sense, you can consider the EventPublisherHolder bean to be a static bean.

How to access application.properties values in constructor

I am trying to use #ConfigurationProperties to load key-value pairs from application.properties file.
application.properties
soap.action.segalRead=Segal/SegalRead
soap.action.mantilUpdate=Mantil/MantilUpdate
SoapUri.java
#ConfigurationProperties(prefix = "soap.action")
public class SoapUri {
#NotNull
private String segalRead;
#NotNull
private String mantilUpdate;
//getters and setters
}
SoapUriTests.java
#RunWith(SpringRunner.class)
#SpringBootTest
public class SoapUriTests {
#Autowired
private SoapUri soapUri;
#Test
public void testSoapUri_returnsSoapAction() {
assertThat(soapUri.getSegalRead()).isEqualTo("Segal/SegalRead");
assertThat(soapUri.getMantilUpdate()).isEqualTo("Mantil/MantilUpdate");
}
}
Above unit test works great.
However, I need to use SoapUri in real code.
Consider following code:
public class MantilUpdateReadVO extends RequestClientVO {
#Autowired
private SoapUri soapUri;
public MantilUpdateReadVO(final MantilUpdate mantilUpdate) {
super(mantilUpdate, soapUri.getMantilUpdate(), MantilUpdateResponse.class);
}
}
public class RequestClientVO {
private Object readRequest;
private String serviceName;
private Class<?> unmarshalTargetclass;
public MwsRequestClientVO(Object readRequest, String serviceName, Class<?> unmarshalTargetclass) {
super();
this.readRequest = readRequest;
this.serviceName = serviceName;
this.unmarshalTargetclass = unmarshalTargetclass;
}
//getters and setters
}
Above complains about: "Cannot refer to an instance field soapUri while explicitly invoking a constructor"
Does anyone know a workaround for injecting segalRead and mantilUpdate in constructor of super()
You are using field-injection, which is not a good idea. See Oliver Gierke's Why Field Injection is Evil for details.
The field cannot be injected until after the instance is constructed; so, you cannot use an injected field during construction.
Change the code like this:
#Autowired
public MantilUpdateReadVO(final SoapUri soapUri, final MantilUpdate mantilUpdate) {
super(mantilUpdate, soapUri.getMantilUpdate(), MantilUpdateResponse.class);
}
You also need to ensure MantilUpdateReadVO is a Spring Bean; might need to add #Component.
Good luck!

How to force CDI/Weld to work with the new keyword?

I have a command line Java SE application that I would like to modernize a bit. I want to use interceptors and dependency injection among other CDI features. However the application was not designed with CDI or dependency injection in mind, it extensively uses the new keyword and constructor parameters instead of delegating object creation to the DI container. CDI/Weld does not inject dependencies or run interceptors on objects created with new, and it can not handle constructor parameters at all. A simplified example:
class Main {
#Inject
private SomeModule someModule;
public static void main (String[] args) {
SeContainer container = ... set up CDI container ...
Main main = container.select(Main.class).get();
main.main(args);
}
#TraceLog
public Main () {
...
}
#TraceLog
public main (String[] args) {
Encryptor = new Encryptor(args[1], args[2], args[3]);
encryptor.run();
}
}
class Encryptor {
#Inject
private SomeModule someModule;
private String inputFile;
private String outputFile;
private String key;
#TraceLog
public Encryptor (String inputFile, String outputFile, String key) {
...
}
#TraceLog
public run () {
...
}
}
Main is instantiated by the CDI container, someModule is injected, and #TraceLog interceptor is called for both constructor and method. However Encryptor is created explicitly with the new keyword, someModule is not injected, and #TraceLog is not called.
CDI supports programmatic creation of beans, but only for classes with a parameterless non-private constructor. Examples:
CDI.current().select(DefinitelyNotEncryptor.class).get();
#Inject
private Instance<DefinitelyNotEncryptor> instance;
instance.select(DefinitelyNotEncryptor.class).get();
Spring supports injection into objects created with the new keyword, with the use of AspectJ. No idea about support for interceptors on constructors and methods though.
#Configurable(preConstruction = true)
#Component
class Encryptor {
#Autowired
private SomeModule someModule;
private String inputFile;
private String outputFile;
private String key;
#TraceLog
public Encryptor (String inputFile, String outputFile, String key) {
...
}
#TraceLog
public run () {
...
}
}
Is there any similar solution to CDI/Weld? Or should I resort to using Spring? Does it support constructor and method interceptors?
Let's start with few remarks...
and it can not handle constructor parameters at all
Wrong. It's called constructor injection.The only limitation being that all parameters have to be resolvable CDI beans.
#Inject
public Foo(Bar bar) { // -> CDI will attempt to inject Bar
// constructor logic
}
CDI/Weld does not inject dependencies or run interceptors on objects created with new
Yes, not by default. But it is achievable via BeanManager.createInjectionTarget(...).inject(...)
Not a go-to way to convert an existing application though!
NOTE: the above code will only enable injection. Not interception. For that would perhaps need to use InterceptionFactory.
You shouldn't need either for your problem though.
CDI supports programmatic creation of beans...
What you described with your code (Instance<T>) is not creation but rather a dynamic/programmatic lookup. It adheres to the same resolution rules as #Inject only allowing you to make it dynamic and not set in stone.
If you speak of creation, you may mean producer methods?
Now, to your problem...
If I get it correctly, the only problem is that the constructor of Encryptor has parameters. Well, then you need to make sure those parameters can be injected in some way. Since they are all three of type String, you will need to either wrap them in some bean or use qualifiers so that typesafe resolution does not blow up with ambiguous resolution when you have multiple beans of type String.
Here is how the constructor would look like with qualifier-based solution. #Output, #Input and #Key are all qualifiers:
#Inject
public Encryptor (#Input String inputFile, #Output String outputFile, #Key String key){...}
Here is an example of one of these qualifiers:
#Qualifier
#Retention(RUNTIME)
#Target({METHOD, FIELD, PARAMETER, TYPE})
public #interface Key {}
And finally, you need to produce those String beans, that you can do with producer methods, mentioned above. Here is one (exception the logic to grab that value as I cannot know how you do that):
#Produces
#Key
public String produceKeyString() {
// CDI will invoke this method in order to create bean of type String with qual. #Key
String key = new String("safeKey") // replace with your logic to get the value
return key;
}
If you want CDI Injection, you must avoid using the new operator.
This this how I would have find a solution to your design problem
#ApplicationScoped
class Main
{
#Inject
private SomeModule someModule;
#Inject
private Encryptor encryptor;
public static void main (String[] args)
{
SeContainer container = ... set up CDI container ...
Main main = container.select(Main.class).get();
main.run(args);
}
#TraceLog
public Main ()
{
...
}
#TraceLog
public void run(String[] args)
{
encryptor.init(args[0], args[1], args[2]).run();
}
}
#Dependent
class Encryptor
{
#Inject
private SomeModule someModule;
private String inputFile;
private String outputFile;
private String key;
#TraceLog
public Encryptor init(String inputFile, String outputFile, String key)
{
this.inputFile = inputFile;
this.outputFile = outputFile;
this.key = key;
return this;
}
protected void run()
{
// do the real job of the encryptor here
}
}
The main things about this code are :
using scope annotations ApplicationScoped and Dependent
providing init() method on your Encryptor class which will take runtime arguments
returning the instance of Encryptor at the end of init() to be able to call the run() method which is protected to avoid direct call (It could be also private, I think) without calling init() first
It should work with this design.

Categories