Using an autowired singleton bean in a non spring managed java class - java

Alright, this might seem pretty stupid to all the veterans out there, but bear with me here, as I'm only finding my way around Spring & Spring Boot.
I've got a Controller class here,
#RestController
public class Controller {
private static final Logger logger = LogManager.getLogger(Controller.class);
private static Controller controller = null;
#Autowired
private ApplicationParameters applicationParameters;
public static Controller getInstance() {
if (controller == null) {
synchronized (Controller.class) {
if (controller == null) {
controller = new Controller();
}
}
}
return controller;
}
public Controller() {}
public ApplicationParameters getApplicationParameters() {
return applicationParameters;
}
#RequestMapping("/")
public void init() {
try {
for (Entry<String, String> prop : applicationParameters.getProperties().entrySet())
logger.info("Loaded System Property: " + prop.getKey() + " -> " + prop.getValue());
Utils.concatenate("key1", "key2");
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
which autowires the ApplicationParameters bean with properties from a Property file.
Utils Class
public class Utils {
protected static final Logger logger = LogManager.getLogger(Utils.class);
//Need to get the value of the property keys propKey1 & propKey2 and concat them.
public static String concatenate(String propKey1, String propKey2) throws Exception {
if(StringUtils.isNoneEmpty(propKey2) && StringUtils.isNoneEmpty(propKey1)) {
return Controller.getInstance().getApplicationParameters().getProperties().get(propKey1) + Controller.getInstance().getApplicationParameters().getProperties().get(propKey2)
} else {
logger.error("System Property is undefined." );
return null;
}
}
So, I'd like use this autowired ApplicationParameters bean as a singleton instance throughout the lifecycle of my project.
For instance, I'd like to use it in the Utils class. Clearly Utils class is not spring managed, its just a regular old java class.
So I'd like to know how to use fully initialized applicationParameters in my Utils class.
This is what I've tried so far:
Autowiring the ApplicationParameters again in the Utils class, like this,
public class Utils {
#Autowired
private ApplicationParameters applicationParameters;
protected static final Logger logger = LogManager.getLogger(Utils.class);
But applicationParameters will be null here as, I'm presuming, this is because, Utils is not a spring managed bean.
Make Controller class singleton. (Not sure how to go about doing this as init() needs to get invoked when web server starts, then where to call getInstance()?)
Hence, would someone be so kind as to assist a novice here.
P.S. The Utils class is shown only as a sample to bring home the fact that, a spring managed autowired bean has to be used in a regular java class.

You could make the spring context accessible from outside with a helper class like this one:
public class SpringContextUtil implements ApplicationContextAware {
static ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext context) throws BeansException {
applicationContext = context;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
}
Then, you could do something like this: SpringContextUtil.getApplicationContext.getBean("applicationParameters")

As a first rule, don't. A second don't either. Only if you really must, as there is no garantuee that this will work reliable as there is no way that everything has been properly initialized when this method is called. Instead try re-working your util to be a spring managed class as well.
If you really want, ditch most of your code as you are trying to be too smart in your code. Use this hack (yes it is a hack imho and should be avoided if necessary!).
public class SpringUtil {
private static final ApplicationContext ctx;
SpringUtil(ApplicationContext ctx) {
SpringUtil.ctx=ctx;
}
public static Controller getController() {
return this.ctx.getBean(Controller.class);
}
public static ApplicationParameters getApplicationParameters() {
return ctx.getBean(ApplicationParameters.class);
}
}
Then cleanup your controller
#RestController
public class Controller {
private static final Logger logger = LogManager.getLogger(Controller.class);
#Autowired
private ApplicationParameters applicationParameters;
#GetMapping("/")
public void init() {
try {
for (Entry<String, String> prop : applicationParameters.getProperties().entrySet())
logger.info("Loaded System Property: " + prop.getKey() + " -> " + prop.getValue());
Utils.concatenate("key1", "key2");
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
THen use the SpringUtil to obtain the ApplicationParameters instead of the controller
public class Utils {
protected static final Logger logger = LogManager.getLogger(Utils.class);
//Need to get the value of the property keys propKey1 & propKey2 and concat them.
public static String concatenate(String propKey1, String propKey2) throws Exception {
if(StringUtils.isNoneEmpty(propKey2) && StringUtils.isNoneEmpty(propKey1)) {
return SpringUtils.getApplicationParameters().getProperties().get(propKey1) + SpringUtils.getApplicationParameters().getProperties().get(propKey2)
} else {
logger.error("System Property is undefined." );
return null;
}
}
However this is quite a hack and might work in 90% of the cases. Also there is quite a design flaw/smell as you are doing a lot of getter chaining in your class. So all in all you are probably better of refactoring the Utils to make use of regular method calls and proper design techniques.

Related

Injecting mock before Spring's post-construct phase

Basically, the question is in the title.
I faced a problem that in post-construct phase my bean (that is autowired in the bean that is going through post-construct phase right now) is already mocked, but all the behavior described by Mockito.when() doesn't work, all the calls return null.
While searching I found this solution.
But is it possible to make it work without using any 3rd party libraries?
Test class:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
#ContextConfiguration(classes = TestApplicationConfiguration.class)
public class ServiceTest {
#Autowired
#Qualifier("test")
private PCLPortType pclPortType;
#MockBean
private ClearingHelper сlearingHelper;
#MockBean
private OrganizationCacheRepository organizationCacheRepository;
#Before
public void setup() throws Exception{
OperationResultWithOrganizationSystemIdMappingList res = new OperationResultWithOrganizationSystemIdMappingList();
when(clearingHelper.getOrgIdSystemIdMapping(any(Keycloak.class))).thenReturn(res);
}
#Test
public void test() throws Exception{
pclPortType.call("123");
}
}
Test config:
#TestConfiguration
public class TestApplicationConfiguration {
#Bean(name = "test")
public PCLPortType pclPortTypeForTest() throws JAXBException {
...
}
#Bean
public Keycloak keycloak() {
return Mockito.mock(Keycloak.class);
}
}
Component where I want to get mocked beans:
#Component
public class OrganizationCacheJob {
private static final Logger logger =
LogManager.getLogger(OrganizationCacheJob.class);
private final ObjectFactory<Keycloak> factory;
private final ClearingHelper clearingHelper;
private final OrganizationCacheRepository organizationCacheRepository;
#Autowired
public OrganizationCacheJob(ObjectFactory<Keycloak> factory,
ClearingHelper clearingHelper,
OrganizationCacheRepository organizationCacheRepository) {
this.factory = factory;
this.clearingHelper = ClearingHelper;
this.organizationCacheRepository = organizationCacheRepository;
}
#PostConstruct
public void updateCacheRepository() {
doUpdateCacheRepository();
}
#Scheduled(cron = "${organization.cache.schedule}")
public void start() {
logger.info("Starting update organization cache.");
doUpdateCacheRepository();
logger.info("Job finished.");
}
private void doUpdateCacheRepository() {
try {
Keycloak keycloak = factory.getObject();
OperationResultWithOrganizationSystemIdMappingList orgIdSystemIdMapping = clearingHelper.getOrgIdSystemIdMapping(keycloak);
if (orgIdSystemIdMapping != null) {
orgIdSystemIdMapping.getContent().forEach(o -> organizationCacheRepository.saveOrgIdsSystemsIdsMappings(o.getOrgId(), o.getId()));
logger.debug("Was saved {} orgIds", orgIdSystemIdMapping.getContent().size());
}
} catch (Exception e) {
logger.error("Error fetching whole mapping for org and systems ids. Exception: {}", e);
}
}
}
So, in post-construct phase of OrganizationCacheJob I want to get res when calling clearingHelper, but instead I get null.
ClearingHelper is a regular Spring bean marked as a #Component with public methods.
Ahh ok I just realized - when you start your test case, whole env is up and running first, then you advance to testing phase. So, translating to your case - first you got injection and post-constructs called, then #Before method is done, thus the result.
So as you can see, code says more than all the words you could put in your original post.
If it is possible for you, use spies insteed of mocks. If it is not possible to construct that, you will have to redesign your tests to not rely on post construct.
In this case, since you want the same post-construct behavior for every test case, provide own factory method for given mock (like you did with keycloak) and move when-doReturn there. It will be guaranteed that it will happen before post construct.

Spring Boot: how to inject dependencies into a class called by a library?

I'm using Kinesis Client Library (KCL) and Spring boot. To use KCL, I have to implement a class (I named it RecordProcessor) for interface IRecordProcessor. And KCL will call this class and process records from kinesis. But when I tried to use dependency injection, I found it was not succeeded.
Here's the snippet for RecordProcessor:
#Component
public class RecordProcessor implements IRecordProcessor {
#Autowired
private SingleRecordProcessor singleRecordProcessor;
#Override
public void initialize(String shardId) {
...
}
#Override
public void processRecords(List<Record> records, IRecordProcessorCheckpointer checkpointer) {
...
}
}
I use Class SingleRecordProcessor to process single each record from kinesis. And this is my SingleRecordProcessor class snippet:
#Component
public class SingleRecordProcessor {
private Parser parser;
private Map<String, Table> tables;
public SingleRecordProcessor() {
}
#Autowired
private void setParser(Parser parser) {
this.parser = parser;
}
#Autowired
private void setTables(Map<String, Table> tables) {
this.tables = tables;
}
public void process(String record) {
...
}
}
I want to let spring framework automatically inject the SingleRecordProcessor instance into the class and use it. But I found that the field singleRecordProcessor is null.
Any idea why the dependency injection is failed? Or is it impossible to inject dependencies into a class which is called by other framework (in this case it's KCL)? Any suggestions will be appreciated! Really need some help please!!
[UPDATE]:
Sorry for not expressing the error clearly. The error was NullPointerException. I tried to inject singleRecordProcessor and call method process() on it. I think the injection was not successful so the instance singleRecordProcessor is null and there comes the NullPointerException.
More information is as follows:
I have a major class called Application
#SpringBootApplication
public class Application{
public static void main(String[] args) {
SpringApplication application = new SpringApplication(Application.class);
application.addListeners(new ApplicationPidFileWriter("./app.pid"));
ConfigurableApplicationContext ctx = application.run(args);
}
}
And I have the MainProcessor class which will call KCL.
#Service
public final class MainProcessor {
#EventListener(ApplicationReadyEvent.class)
public static void startConsumer() throws Exception {
init();
IRecordProcessorFactory recordProcessorFactory = new RecordProcessorFactory();
Worker worker = new Worker(recordProcessorFactory, kinesisClientLibConfiguration);
...
worker.run(); // this line will call KCL library and eventually call ProcessorRecord class.
}
}
[UPDATE2]
RecordProcessorFactory only has one method like this
#Component
public class RecordProcessorFactory implements IRecordProcessorFactory {
#Autowired
RecordProcessor recordProcessor;
#Override
public IRecordProcessor createProcessor() {
return recordProcessor;
}
}
It creates a new RecordProcessor instance for KCL to use it.
You should autowire an instance of this into your MainProcessor:
#Component
public class RecordProcessorFactory {
#Lookup IRecordProcessor createProcessor() { return null; }
}
Spring will instantiate a RecordProcessorFactory for you, and replace the implementation of createProcessor() in it with one that will return a new IRecordProcessor each time it's called. Both the factory and the processors will be Spring beans - which is what you want.

final Util Class Static vs Singleton Class

Currently I'm trying to Implement a utility Class that generates an invoice in a PDF Format and I predict that I'll need spring beans to be injected in my Utility Class Afterwards.
But I don't need the class to be instanciated, I only need the methods. So for me it's a dilemma
So I did some research and I still haven't made my mind If I want a spring singleton bean or .
Spring Singleton : Source
#Service
public class Singleton {
private static AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>();
public Singleton() {
final Singleton previous = INSTANCE.getAndSet(this);
if(previous != null)
throw new IllegalStateException("Second singleton " + this + " created after " + previous);
}
public static Singleton getInstance() {
return INSTANCE.get();
}
}
Or A final Class :
public final InvoiceUtil {
private InvoiceUtil() {}
public static String convertToPDF (Template template) {
//Do the work
}
}
but with the second approach, my class isn't managed by Spring so I can not inject beans to it.
Make me undrestand !! :p
If you use a Spring bean, do not implement Spring Service as a Singleton with getInstance(). Just add #Service and Spring will only instantiate it once.
You can use static methods, and later pass any dependency also to these methods (if those are few dependencies):
public static String convertToPDF (Template template, NeededHelper helper) {
...
}

Regarding spring container eagerly singleton design pattern

I have developed an utility class for spring which is a singleton which will provide the reference of container for the whole application , below is my class..
public class SpringUtility
{
private static BeanFactory factory ;
static
{try
{BeanFactory factory = new XmlBeanFactory(new FileSystemResource("Spring.xml"));
} catch(Exception e)
{
e.printStackTrace();
}
}
private SpringUtility()
{}
public static BeanFactory getBeanFactory()
{ return factory;
}}
Now please advise I want to convert it into style of eager singleton, Please advise how this could be achieved. please advise how this same class could be converted it in eager singleton design pattern such as the normal eager design pattern is ..
public class SingletonEager {
private final static SingletonEager INSTANCE = new SingletonEager();
private SingletonEager() {
}
public static SingletonEager getInstance() {
return SingletonEager.INSTANCE;
}
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
}
similar way I want for spring one too please advise
If you want BeanFactory to grab beans from Spring context, then I'd suggest you to implement BeanFactoryAware, It would stay singleton & eagerly loaded
public class BeanManager implements BeanFactoryAware {
private BeanFactory beanFactory;
public Person getPerson(){ beanFactory.getBean(Person.class) ;}
}
And mark this BeanManager class as spring bean

How to write tag in my spring project?

I want to write my tag (extends TagSupport) in my spring framework. In my tag class, will use some service which should auto inject by spring. But I always get null, seems spring can't inject service instance in my tag class.
The code is like the following:
public class FetchTagNameTag extends TagSupport {
#Autowired
private TaskService taskService;
...
taskService is always null.
How can I resolve this?
Thanks.
Have a try by utilizing RequestContextAwareTag. It will offer you methods to obtain RequestContext and then WebApplicaitonContext. Have a look at here.
JSP tag objects are not managed by Spring, they are managed by the servlet container. As a result, you cannot autowire stuff into your tags.
If you need to get hold of beans from the spring appcontext, then your Spring MVC controller needs to set the bean as a request attribute (using request.setAttribute()), so that the tag object can get hold of it.
Annotate your Tag-Implementation with #Configurable and add <context:component-scan base-package="your.webapp"> to your Spring-Configuration.
Check out these spring packages in the spring reference docs and in the spring source:
org.springframework.web.servlet.tags
org.springframework.web.servlet.tags.form
If nothing else, those will show you how the spring developers wrote the spring tags.
What you could do is create a static method like this:
public static void autowireAllFor(Object target) {
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(...yourBeanFactory...);
bpp.processInjection(target);
}
and then for your tag you could do
public class YourTag extends TagSupport {
#Autowired
private SomeBean someBean;
public YourTag() {
YourHelperClass.autowireAllFor(this);
}
}
The obvious disadvantage of this approach is that you have to do this for every constructor, but as TagSupport only has one, it should not be a problem. You can go even one step further and create a helper superclass which always guarantees autowiring:
public class SpringTagSupport extends TagSupport {
public SpringTagSupport() {
super();
YourHelperClass.autowireAllFor(this);
}
}
The rest is as easy as extending your classes from SpringTagSupport.
First I write this:
public abstract class SpringSuportedTag extends SimpleTagSupport{
protected WebApplicationContext _applicationContext;
protected WebApplicationContext getSpringContext(){
PageContext pageContext = (PageContext) getJspContext();
if(_applicationContext==null){
_applicationContext = RequestContextUtils.getWebApplicationContext(
pageContext.getRequest(),
pageContext.getServletContext()
);
initCustomBeans();
}
return _applicationContext;
}
protected abstract void initCustomBeans();
/**
* Deprecated for inserting extra logic. Use {#link #doTagWithSpring()} instead.
*/
#Override
#Deprecated
public void doTag() throws JspException, IOException {
getSpringContext();
doTagWithSpring();
}
abstract void doTagWithSpring() throws JspException, IOException;
}
And usage:
public class SlotTag extends SpringSuportedTag {
// #Resource(name="userDetailHolder")
// not work here
UserDetailHolder userDetail;
private String slotname;
public String getSlotname() {
return slotname;
}
public void setSlotname(String slotname) {
this.slotname = slotname;
}
#Override
void doTagWithSpring() throws JspException, IOException {
PageContext pageContext = (PageContext) getJspContext();
String userDetailCode = pageContext.getAttribute(InitWidgetUserTag.KAY_USERDETAIL, PageContext.PAGE_SCOPE).toString();
userDetail.init(userDetailCode);
String pageID = pageContext.getAttribute(InitWidgetUserTag.KAY_PAGEID, PageContext.PAGE_SCOPE).toString();
getJspContext().getOut().println("<b>slot for user:"+userDetail.getUserId()+"</b>");
}
#Override
protected void initCustomBeans() {
userDetail = (UserDetailHolder) getSpringContext().getBean("userDetailHolder");
}
}
It's work.
But than i found this:
Spring supported Tag Libraries. Truth in my progect I still use own solution.
Use :-
import org.springframework.web.servlet.tags.RequestContextAwareTag;
public class FetchTagNameTag extends RequestContextAwareTag {
// #Autowired
// private TaskService taskService;
#Override
protected int doStartTagInternal() throws Exception {
TaskService taskService= getRequestContext().getWebApplicationContext().getBean(TaskService.class);
return 0;
}

Categories