How to inject a Spring bean into a google cloud endpoint class? - spring

I want to inject a Spring Data JpaRepository into a goodle cloud endpoint class.
How can I do this? Because I think currentliy the cloud endpoint class is not under Spring control and the autowired annotated repository remains always to null.
Some one found a solution for guice Appengine with Google Cloud Endpoints and Guice
What I want to do is the Same with Spring. So I want to bring up the cloud endpoints with the spring context.
Currently I do it over the Spring context to get my repositories like:
#Api(name = "myapi", version = "v1", scopes = { Constants.EMAIL_SCOPE }, clientIds = {
Constants.WEB_CLIENT_ID, Constants.ANDROID_CLIENT_ID,
Constants.IOS_CLIENT_ID, Constants.API_EXPLORER_CLIENT_ID }, audiences = {
Constants.ANDROID_AUDIENCE })
public class TestServiceImpl {
// #Autowired
private TestRepository repository;
public TestServiceImpl () {
repository = ApplicationContextProvider.getApplicationContext()
.getBean(TestRepository.class);
}
....
}
But I want to use Autowired, is that possible?

I encountered the same problem today. I found the solution by adding the following constructor:
public TestServiceImpl() {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
Hope it helps.

Related

Programmatic RedissonClient in Spring boot project

I am trying to implement Hibernate second level caching in a Spring boot project using Redisson.
I have followed this blog as a reference
https://pavankjadda.medium.com/implement-hibernate-2nd-level-cache-with-redis-spring-boot-and-spring-data-jpa-7cdbf5632883
Also i am trying to initialize the RedissionClient programmatically and not through declaratively /through a config file
Created a spring bean to be initialized which should create the RedissonClient instance.
#Configuration
#Lazy(value = false)
public class RedissonConfig {
#Bean
public RedissonClient redissionClient() {
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
return Redisson.create(config);
}
}
However this bean is never intialized and i get the following error while application startup.
Caused by: org.hibernate.cache.CacheException: Unable to locate Redisson configuration
at org.redisson.hibernate.RedissonRegionFactory.createRedissonClient(RedissonRegionFactory.java:107) ~[redisson-hibernate-53-3.12.1.jar:3.12.1]
at org.redisson.hibernate.RedissonRegionFactory.prepareForUse(RedissonRegionFactory.java:83) ~[redisson-hibernate-53-3.12.1.jar:3.12.1]
It seems Spring boot Hibernate still trying to load the Redisson config through a config file.
is it possible to load the Redission config in spring boot programmatically ?
Best Regards,
Saurav
I just did exactly this, here is how:
you need a custom RegionFactory that is similar to the JndiRedissonRegionFactory but gets its RedissonClient injected somehow.
an instance of this Class, fully configured, is put into the hibernate-properties map. Hibernates internal code is flexible: if the value of hibernate.cache.region.factory_class is a string it is treated as a FQDN. If it is an instance of Class<?>, it will be instantiated. If it is an Object, it will be used.
Spring offers a rather simple way to customize hibernate properties with a bean:
#AutoConfiguration(after = RedissonAutoConfiguration.class, before = JpaAutoConfiguration.class)
#ConditionalOnProperty("spring.jpa.properties.hibernate.cache.use_second_level_cache")
public class HibernateCacheAutoConfiguration {
#Bean
public HibernatePropertiesCustomizer setRegionFactory(RedissonClient redisson) {
return hibernateProperties -> hibernateProperties.put(AvailableSettings.CACHE_REGION_FACTORY, new SpringBootRedissonRegionFactory(redisson));
}
}
My RegionFactory is really simple:
#AllArgsConstructor
public class SpringBootRedissonRegionFactory extends RedissonRegionFactory {
private RedissonClient redissonClient;
#Override
protected RedissonClient createRedissonClient(Map properties) {
return redissonClient;
}
#Override
protected void releaseFromUse() {
}
}
I used the redisson-starter to get a RedissonClient, hence the reference to RedissonAutoConfiguration, but you could just create an instance by hand.
It is possible, but then you need to provide a custom implementation of RegionFactory to Hibernate, which can extends RedissonRegionFactory but uses your own client instance.

JAX-RS, Spring & ServletConfig: How to access Servlet Config in Configurator

I have troubles getting a javax.servlet.ServletConfig into a class annotated with org.springframework.context.annotation.Configuration.
My team decided that we should use spring for dependency injection and I'm trying to use it to migrate one of our simple Rest services.
My constraints are:
JAX-RS: We have several REST Services implemented JAX-RS and we don't really want to change that.
Not bound to a specific implementation of JAX-RS (Jersey & RESTEasy work fine for us and we can change from one to the other without changing underlying code)
Import as few dependencies as possible from spring: at the moment I import only org.springframework:spring-context from the spring project.
No API breakage: Deprecated is fine but the service should keep working during the transition, using our old way of doing things.
A string parameter is defined in the service's web.xml. I need to get it, instantiate a Bean with it and inject the resulting bean at several place in the code.
I don't want to mess with Spring Boot/MVC/... as the service already works and I just want the Dependency Injection part.
What I already have:
The code use javax.ws.rs.core.Application, with a class that look like that:
public class MyApplication extends Application {
#Context
private ServletConfig cfg;
public DSApplication() {
}
#Override
public Set<Class<?>> getClasses() {
return new HashSet<>();
}
#Override
public Set<Object> getSingletons() {
Set<Object> set = new HashSet<>();
String injectionStr = cfg.getInitParameter("injection");
boolean injection = false;
if (null != injectionStr && !injectionStr.isEmpty()) {
injection = Boolean.valueOf(injectionStr);
}
if (injection) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
DSServiceProducer.class,
CContextBeanProvider.class
);
IDSService service = context.getBean(IDSService.class);
set.add(service);
} else {
set.add(new DSService()); //Old way
}
return set;
}
}
I need the servlet config in CContextBeanProvider, which look like:
#Configuration
public class CContextBeanProvider {
private ServletConfig cfg; // How to get this here ?
#Bean
public CContextBean cContextBean() {
String bean = cfg.getInitParameter("cpuContext");
return new CContextBean(bean);
}
}
CContextBean is a setting bean initialized from a string found in the web.xml of the service.
Is it possible ?
Do you have any idea how ?
Would it be easier with CDI, knowing that we run on base Tomcat ? (I've already find this if I need to use tomcat with CDI)
Could you please try to add all jersey CDI related jars to your applications ?

Define different Feign client implementations based on environment

I have a Spring boot application which uses Feign to call an external web service via Eureka. I'd like to be able to run the application using a mocked out implementation of the Feign interface, so I can run the application locally without necessarily having Eureka or the external web service running. I had imagined defining a run configuration that allowed me to do this, but am struggling to get this working. The issue is that the Spring "magic" is defining a bean for the Feign interface no matter what I try.
Feign interface
#FeignClient(name = "http://foo-service")
public interface FooResource {
#RequestMapping(value = "/doSomething", method = GET)
String getResponse();
}
Service
public class MyService {
private FooResource fooResource;
...
public void getFoo() {
String response = this.fooResource.getResponse();
...
}
}
I tried adding a configuration class that conditionally registered a bean if the Spring profile was "local", but that was never called when I ran the application with that Spring profile:
#Configuration
public class AppConfig {
#Bean
#ConditionalOnProperty(prefix = "spring.profile", name = "active", havingValue="local")
public FooResource fooResource() {
return new FooResource() {
#Override
public String getResponse() {
return "testing";
}
};
}
}
At the point my service runs, the FooResource member variable in MyService is of type
HardCodedTarget(type=FoorResource, url=http://foo-service)
according to IntelliJ. This is the type that is automatically generated by the Spring Cloud Netflix framework, and so tries to actually communicate with the remote service.
Is there a way I can conditionally override the implementation of the Feign interface depending on a configuration setting?
the solution is like below:
public interface FeignBase {
#RequestMapping(value = "/get", method = RequestMethod.POST, headers = "Accept=application/json")
Result get(#RequestBody Token common);
}
then define your env based interface:
#Profile("prod")
#FeignClient(name = "service.name")
public interface Feign1 extends FeignBase
{}
#Profile("!prod")
#FeignClient(name = "service.name", url = "your url")
public interface Feign2 extends FeignBase
{}
finally, in your service impl:
#Resource
private FeignBase feignBase;
Having posted the same question on the Spring Cloud Netflix github repository, a useful answer was to use the Spring #Profile annotation.
I created an alternative entry point class that was not annotated with #EnabledFeignClients, and created a new configuration class that defined implementations for my Feign interfaces. This now allows me to run my application locally without the need to have Eureka running, or any dependent services.
I'm using a simpler solution to avoid having multiples interfaces for a variable parameter like url.
#FeignClient(name = "service.name", url = "${app.feign.clients.url}")
public interface YourClient{}
application-{profile}.properties
app.feign.clients.url=http://localhost:9999

Spring Boot with Spring Social Google provider

Any example how to integrate Spring Boot application with Spring Social Google (GabiAxel/spring-social-google) provider? I found this project, but it seems to be unfinished. Spring Boot explains how to get it working with Spring Facebook, Twitter, but is it the same for log in with Google?
As you have mentioned in your question, you can use that project hosted on github.
You can use this dependency
In a Configuration class, you will have to extend SocialConfigurerAdapter, override the addConnectionFactories method and add GoogleConnectionFactory. For example :
#Configuration
#EnableSocial
public class SocialConfig extends SocialConfigurerAdapter {
#Override
public void addConnectionFactories(ConnectionFactoryConfigurer connectionFactoryConfigurer, Environment environment) {
GoogleConnectionFactory googleConnectionFactory = new GoogleConnectionFactory(environment.getProperty("spring.social.google.app-id"), environment.getProperty("spring.social.google.app-secret"));
googleConnectionFactory.setScope("https://www.googleapis.com/auth/plus.login");
connectionFactoryConfigurer.addConnectionFactory(googleConnectionFactory);
}
#Bean
#Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
public Google google(ConnectionRepository repository) {
Connection<Google> connection = repository.findPrimaryConnection(Google.class);
return connection != null ? connection.getApi() : null;
}
}
You can use this along with the Spring Social examples.

Spring Cloud AWS InstantiationException thrown when using #Autowire

I like the the ability to use constructors to add dependencies. Especially autowiring those dependencies.
e.g.
public class MyClass {
private final Dependency dependency;
#Autowired
public MyClass(#Qualifier("bean-id") Dependency dependency) {
this.dependency = dependency;
}
}
What I'm finding is that the Spring Cloud AWS framework throws an "InstantiationException" if the "Dependency" class above happens to be a class which is passed to a Workflow worker and is missing a default, empty constructor.
concrete example:
public class MyClass {
private final DependencyWorkflowClientExternalFactory clientFactory;
#Autowired
public MyClass(#Qualifier("bean-id") DependencyWorkflowClientExternalFactory clientFactory) {
this.clientFactory = clientFactory;
}
}
public class WorkflowInitializer {
#Autowired
private WorkflowWorker workflowWorker; //assume wired with correct credentials
public WorkflowInitialiser() {
init();
}
public init() {
workflowWorker.addWorkflowImplementationType(MyClass.class);
}
}
the above fails with:
java.lang.InstantiationException: com.mypackage.MyClass
at java.lang.Class.newInstance(Class.java:359)
I have to do something like:
public class MyClass {
#Autowired
#Qualifier("bean-id")
private Dependency dependency;
public MyClass() {
}
}
The question is:
Is it possible in the current release of the Spring Cloud framework to use the #Autowire annotation on a constructor? Is it a requirement that the annotation is added to the instance field?
I ask (and assume "yes") because Workflow workers take a class types, rather than instantiations of objects for their implementations of workflows.
As a secondary question:
Why do ActivityWorkers take instances of an object but WorkflowWorkers take classes?
This question is actually not related to Spring Cloud AWS but AWS Flow Framework for Java.
You are using the "default" WorkflowWorker which instantiates workflow implementations as POJOs. Therefore your class MyClass is not created as a Spring bean but as a POJO.
You should use the SpringWorkflowWorker provided by the AWS Flow Framework for a better integration with Spring. For more information, have a look at the AWS Flow Framework documentation explaining the integration with Spring.

Resources