Depending on a Spring project from a non-Spring project - java

I've got two Java applications that I have to combine. One is Spring, and methods within it need to be called by the second, an Elasticsearch plugin (that I don't think can be turned into a Spring app as it already uses some form of Guice for dependency injection).
The Spring class I need to call looks like:
#Component
public class DataServiceController {
//This is defined within a #Config
#Autowired
DataTypesMap dataTypesMap;
/**
* Create an item in the data platform
*/
public ItemCreatedResponse createItem(String data, String dataType)
throws IOException {
ProcessStrategy dataStrategy = dataTypesMap.get(dataType);
return dataStrategy.add(data);
}
If I just add this project as a Maven dependency within the ES plugin, the Autowired dataTypesMap is always null (which is to be expected as nothing in the Elasticsearch plugin will be telling it how to autowire).
What can I do here?

You can use a setter method for your autowired fields, then sets the value.
#autowired
public void setDataTypesMap (DataTypesMap dataTypesMap ){
this.dataTypesMap = dataTypesMap ;
}
In your application you can not autowired the bean, but you can set it.
myBean.setDataTypeMap();
The second option is initiate a the context of the spring application inside the non-spring application.
You can see how to do it here.
http://www.springbyexample.org/examples/intro-to-ioc-creating-a-spring-application.html

Related

How to inject extra property sources in SPRING before other beans are created?

I am writing a library which will be used by spring-boot projects. I'd like to inject into the boot projects' SpringEnvironment a property source that I take from the Internet.
I tried the following
#Configuration
public class MyCustomConfiguration {
#Bean
BeanDefinedAbove above() { /* do some work */ }
#Bean
#ConditionalOnBean(BeanDefinedAbove.class)
SmartInitializingSingleton propertySourceSetting(ConfigurableEnvironment env, BeanDefinedAbove bean) {
return () -> {
PropertySource source = bean.getPropertySourceDownloadedFromTheInternet();
env.getPropertySources().addFirst(source);
}
}
}
In my clients' projects when I debug this code what happens is either one of the two:
above() is called
user's #Service or #Controller are called
propertySourceSetting(...) is called
OR
user's #Service or #Controller are called
above() is called
propertySourceSetting(...) is called
Depending whether or not my client's depend on my BeanDefinedAbove bean, which is normal as the #Service is depdent on the bean created in above().
I have also added the FQDN of my class to the EnableAutoConfiguration in the META-INF/spring.factories.
So how to ensure that the logic in propertySourceSetting(..) is called before users' #Service and #Controller
I'll provide you with three options.
Option 1: (THIS IS A BAD APPROACH, BUT A QUICK WORKAROUND)
Add #Lazy(false) annotation to both Beans. Spring will eagerly create those two beans, which they will probably be created before the other ones.
Why this is bad?
This does not ensure order. Spring decides the creation order based on dependencies and some other conditions. This is why it will "probably" work :)
Option 2: Create a main class to bootstrap Spring Boot Initialization (the old way of starting spring boot).
public static void main(String[] args) throws Exception {
SpringApplication application = new SpringApplication(MyApplication.class);
// ... add property source here before start
application.run(args)
}
You also need to specify main class in the manifest for Spring Boot like this: https://www.baeldung.com/spring-boot-main-class
In that main-class you would add your propertysource, kinda like this:
SomeClassThatRetrievesProperties propRetriever = new SomeClassThatRetrievesProperties ();
Map<String,String> properties = propRetriever.getAllPropertiesAsMap();
application.setDefaultProperties(properties);
Option 3: Create a CustomApplicationContext by extending WebApplicationContext and overriding getSpecificConfigurations() method.
This way you will have full control but we aware that you could break some important stuff.

Should i never use 'new' keyword on a spring boot project?

I'm working on Spring Boot Rest API, and I did end up using the new keyword here and there.
I'm wondering, did I do something wrong when I used the new keyword for my program. And if it is absolutely forbidden to use new keyword on a real project.
If the answer is yes should i annotate each class i wrote with #component annotation so i can instantiate an object using #autowired.
If the answer is no when can we break that rule ?
You can create objects using the new keyword in a spring application.
But these objects would be outside the scope of the Spring Application Context and hence are not spring managed.
Since these are not spring managed, any nested levels of dependency (such as your Service class having a reference to your Repository class etc)
will not be resolved.
So if you try to invoke a method in your service class, you might end up getting a NullPointer for the repository.
#Service
public class GreetingService {
#Autowired
private GreetingRepository greetingRepository;
public String greet(String userid) {
return greetingRepository.greet(userid);
}
}
#RestController
public class GreetingController {
#Autowired
private GreetingService greetingService;
#RequestMapping("/greeting")
public String greeting(#RequestParam(value = "name", defaultValue = "World") String name) {
return String.format("Hello %s", greetingService.greet(name));
}
#RequestMapping("/greeting2")
public String greeting2(#RequestParam(value = "name", defaultValue = "World") String name) {
GreetingService newGreetingService = new GreetingService();
return String.format("Hello %s", newGreetingService.greet(name));
}
}
In the above example /greeting will work but /greeting2 will fail because the nested dependencies are not resolved.
So if you want your object to be spring managed, then you have to Autowire them.
Generally speaking, for view layer pojos and custom bean configurations, you will use the new keyword.
There is no rule for using or not using new.
It's up to you if you want Spring to manage your objects or want to take care of them on your own.
Spring eases object creation, dependency management, and auto wiring; however, you can instantiate it using new if you don't want that.
I think its fine to use new keyword, but you should learn the difference between different stereotype (Controller, Service, Repository)
You can follow this question to get some clarity:
What's the difference between #Component, #Repository & #Service annotations in Spring?
Using appropriate annotation will allow you to correctly use DI (dependency injection), that will help in writing sliced tests for your spring boot application. Also the Service,Controller and Repository components are created as Singleton, so lesser GC overhead. Moreover components that you create using new keyword are not managed by Spring, and by default Spring will never inject dependencies in a object created using new.
Spring official documentation:
https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-spring-beans-and-dependency-injection.html
You will need new on Spring mock tests when you will have to create an object as service and inject mock object as dao.
Look at the following code; here as you see, based on a condition it's necessary to dynamically load advertisements on demand. so here you can not #autowire this group of items because all the information are loaded from DB or an external system, so you just need to fill you model accordingly.
if (customer.getType() == CustomerType.INTERNET) {
List < Advertisement > adList = new ArrayList < Advertisement > ();
for (Product product: internetProductList) {
Advertisement advertisement = new Advertisement();
advertisement.setProduct(product);
adList.add(advertisement);
}
}
Note it's appropriate to use Spring for managing external dependencies
like plugging a JDBC connection into a DAO or configurations like
specifying which database type to use.

Spring Boot Camel - Autowiring issues in Camel components

I am using Spring Boot 1.5.7 and Apache Camel 2.19.3, using Spring Boot AutoConfiguration provided by spring-boot-starter-camel
It is pretty basic Spring Boot and Camel initialized as in their tutorial, so we have a RouteBuilder component that does exactly that.
#Component
public class CIFRoutes extends RouteBuilder {
#Override
public void configure() throws Exception {
// build routes
}
}
We have a Configuration that defines some beans we need in our application
#Configuration
public class MyConfiguration {
#Bean
public void Map<String, Object> map() {
return new HashMap<>()
}
}
Finally, we have a custom InflightRepository implementation that should be scanned by auto-configuration and added to the CamelContext which basically works, but for some reason, the component doesn't get initialized properly. Means, its dependencies are not initialized but the bean is instantiated and injected in my Application.
#Component
public class MyCustomInflightRepository extends DefaultInflightRepository {
#Autowired
private Map<String, Object> map;
#Override
public void add(Exchange exchange) {
super.addExchange(exchange);
// ....
}
}
The problem now is that map remains (null), I also tried adding a #PostConstruct initializer method but it doesn't get called.
As far as I was able to reconstruct, it seems to be connected to premature in CamelAutoConfiguration where the CamelContext bean gets instantiated (done in private method afterPropertiesSet.
InflightRepository inflightRepository = getSingleBeanOfType(applicationContext, InflightRepository.class);
if (inflightRepository != null) {
LOG.info("Using custom InflightRepository: {}", inflightRepository);
camelContext.setInflightRepository(inflightRepository);
}
If MyCustomInflightRepository doesn't implement InflightRepository, the bean is initialized correctly, but indeed not recognized by Camel. When disabling auto-configuration, the bean's dependencies are injected.
So, either I'm doing the impossible by Spring standards or there's something fishy with the Camel component for Spring.
I'm a bit quick on resolving this (I wanted to post this two days ago already^^), but a colleague figured out what could be the problem.
When using CamelAutoConfiguration the InflightRepository bean (or practicially everything for which Camel tries to get a matching bean here), the context is accessed before property resolvers are fully initialized which leads to the bean being initialized (and cached in context) before any auto-wired properties can be resolved.
I'm not a Spring expert but this behavior is a bit problematic in my opinion because uninitialized beans are pulled into the CamelContext when you rely on Spring DI for your custom components.
To be sure, I'll raise this with the maintainers...
By the way, my simple solution was to manually setting the in-flight repository in context configuration (as suggested)
#Bean
public CamelContextConfiguration camelConfig() {
return new CamelContextConfiguration() {
#Override
public void beforeApplicationStart(CamelContext context) {
context.setInflightRepository(new MyCustomInflightRepository(/* Dependencies go here */ ));
}
#Override
public void afterApplicationStart(CamelContext camelContext) {
}
};
}
Also it seems to be an issue when use camel-http-starter in your project which isn't recommended, they claim it is deprecated.
So, either don't do DI (regardless if via property or constructor injection) for your camel-managed beans or skip that starter.
The problem is that a Map<String,Object> is too vague for Spring to be able to understand what you want; I think the default behavior is that it'll give you all beans keyed by name.
Instead, be more specific, or possibly provide the necessary parameters as constructor arguments and configure them explicitly in an #Bean method (it's a good idea to always use constructor injection anyway).

Using IoC to inject a Node2BeanProcessor in my Magnolia module instead of using Components

I have a standard Magnolia module that I've implemented as a Spring MVC REST client. In this module, I am trying to retrieve a JCR node and use Node2BeanProcessor to transform the Node object into my custom bean. Code below:
#Repository
public class JcrRepo() {
#Inject
public Node2BeanProcessor node2Bean;
public MagicWord getMagicWord(String key) {
Session session = LifeTimeJCRSessionUtil.getSession("magic");
Node theNode = session.getNode("/magicWords/" + key);
return node2Bean.toBean(theNode, MagicWord.class);
}
}
When I run this, I encounter a NullPointerException for the variable node2Bean. Which means it wasn't injected properly. However, I am able to do this:
node2Bean = Components.getComponent(Node2BeanProcessor.class);
The Components.getComponent() javadoc states: "Returns a component from the currently set ComponentProvider. Consider using IoC to inject the component instead." Which is what I'm trying to figure out.
Note that I have not done any Guice configuration as I'm looking for a way to leverage on Magnolia's already initialized Guice context to grab my objects.
Is there a better way to do injection than this, or have I done anything wrong or skipped a step?
Appreciate the help.
P.S. For now I've implemented a hacky way to use this in Spring IoC:
#Bean
public Node2BeanProcessor node2Bean() {
return Components.getComponent(Node2BeanProcessor.class);
}
(Working with Magnolia 4.5) I use #Inject for Node2BeanProcessor in a class implementing info.magnolia.module.ModuleLifecycle:
public class MyModule implements ModuleLifecycle {
#Inject
private Node2BeanProcessor node2BeanProcessor;
#Override
public void start(ModuleLifecycleContext moduleLifecycleContext) {
...
getNode2BeanProcessor().toBean(someNode);
...
}
}
Maybe your NullPointerException comes from theNode? Have you verified that theNode is not null?
Another guess is that it could be a lifecycle issue. From what I remember, Components.getComponent() works in situations where #Inject does not (in Magnolia).
Finally: Your instance variable should definitely be private.
If JcrRepo is not instantiated by Guice then Guice also won't be available to inject the Node2BeanProcessor field. Mixing Spring and Guice IoC containers can get confusing, so I tend to stick with Guice as that's what comes with Magnolia.

Spring autowire a stubbed service - duplicate bean

Ok. We have the need to #Autowire a different webservice on-the-fly (preferably by toggling a JNDI setting on the webserver) and I'm at a loss on how to do this. This is the way I was approaching the problems..
Two packages:
com.mycomp.service.stub
com.mycomp.service.impl
One package contains MyServiceStub.java while implement MyService
The other package contains MyServiceImpl.java, which implements same
My controller, which requires MyService, has the bean defined as such
#Autowire
private MyService communicator;
My spring-context.xml has the following:
<context:component-scan base-package="com.mycomp" />
At this point I get a DuplicateBean exception during autowiring. Now, I can statically define which bean to autowire in spring-context.xml:
<bean id="communicator" class="com.mycomp.service.impl.MyServiceImpl" />
and everything works fine... But then, how to 'flip' the switch and change over to the Stub method on our QA server? It has no connection to that service, so we need to run with stubs enabled. A JNDI property would be best for this.. but I just can't get my head around how to toggle what bean spring autowires at runtime.
Any help??
Cheers,
Chris
#Profile solution
You definitely have to try Spring 3.1 #Profile:
#Autowire
private MyService communicator;
//...
#Service
#Profile("prd")
class MyServiceImpl //...
#Service
#Profile("qa")
class MyServiceStub //...
Now depending on which profile is enabled, either DefaultMyService will be initialized or MyServiceStub.
You can choose between profile in various ways:
How to set active spring 3.1 environment profile via a properites file and not via an env variable or system property
using system property
programmatically
...
Spring AOP (explicit around every method)
In this example the aspect wraps around every single MyService method separately and returns stubbed value:
#Aspect
#Service
public class StubAspect {
#Around("execution(public * com.blogspot.nurkiewicz.MyService.foo(..))")
public Object aroundFoo(ProceedingJoinPoint pjp) throws Throwable {
if (stubMode()) {
return //stub foo() result
}
return pjp.proceed();
}
#Around("execution(public * com.blogspot.nurkiewicz.MyService.bar(..))")
public Object aroundBar(ProceedingJoinPoint pjp) throws Throwable {
if (stubMode()) {
return //stub bar() result
}
return pjp.proceed();
}
private boolean stubMode() {
//whatever condition you want here
return true;
}
}
The code is pretty straightforward, unfortunately the return values are buried inside the aspect and you need a separate #Around for every target method. Finally, there is no place for MyServiceStub.
Spring AOP (automatically around all methods)
#Aspect
#Service
public class StubAspect {
private MyServiceStub stub = //obtain stub somehow
#Around("execution(public * com.blogspot.nurkiewicz.MyService.*(..))")
public Object aroundFoo(ProceedingJoinPoint pjp) throws Throwable {
if (stubMode()) {
MethodSignature signature = (MethodSignature)pjp.getSignature();
Method method = signature.getMethod();
return method.invoke(stub, pjp.getArgs());
}
return pjp.proceed();
}
private boolean stubMode() {
//whatever condition you want here
return true;
}
}
This approach is more implicit as it automatically wraps every target method, including new methods added in the future. The idea is simple: if stubMode() is off, run the standard method (pjp.proceed()). If it is on - run the exact same method with exact same parameters - but on a different object (stub in this case).
This solution is much better as it involves less manual work (at the price of using raw reflection).
Note that if both MyService implementations are Spring beans (even when one is annotated with #Primary), you might run into weird troubles. But it should be a good start.
See also:
Spring 3.1 M1: Introducing #Profile
Maybe you can replace the class with a property and deploy your application with different property files. The production version would contain the name of the real class while the QA version would contain the name of a stub.
Maybe this http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-factory-extension-factory-postprocessors can help you.

Categories