I am working on a web application with spring. The application is configured with a properties file.
There are multiple instances of the application in different servers, each instance has a different configuration file (each instance is customized for a different customer) I am using controllers and services. Something like this:
public class Controller1 {
#Autowired
Service1 service1;
#RequestMapping(value = "/page.htm", method = { RequestMethod.GET, RequestMethod.POST })
public ModelAndView serve(HttpServletRequest request, HttpServletResponse response) {
service1.doSomething();
return new ModelAndView("/something");
}
}
#Service
public class Service1 {
#Autowired
Service2 service2;
public void doSomething () {
…
service2.doAnotherThing();
…
}
}
#Service
public class Service2 {
#Value("${propertyValue}")
private String propertyValue;
//doAnotherThing() will use propertyValue
public void doAnotherThing () {
…
//Do something with propertyValue
…
}
}
Now I have a new requirement. There won’t be multiple instances for each customer, but only one instance with multiple domains for all the customers.
The application must decide the configuration depending on the host name of the request object in the controller. So if the customer points the browser to www.app1.com I have to use configuration file 1 but if the customer uses www.app2.com I have to use configuration 2 and so on.
I moved the configuration files to the database, but then I realized that I do not know how to make the dependency injection. The services are linked, service1 uses service2 and service2 is the one who must use the value which depends on the configuration. Service 2 has no knowledge of the request object.
Is there a clean way to solve this?
Thanks,
One approach is to create configuration object for all customer as a singleton on spring config:
<bean id="customerAConfig"../>
<bean id="customerBConfig"../>
<bean id="customerCConfig"../>
And have a session scoped ConfigurationService that acts as a pointer to which configuartion is active
public class ConfigurationService {
private CustomerConfig activeConfig;
// getters & setters..
}
Configure a singleton proxy for this service on your spring config so it can be injected into singleton components. You need to have cglib in your classpath for Spring to create the proxy:
<bean class="com.mycompany.ConfigurationService" scope="session">
<aop:scoped-proxy/>
</bean>
And on your login controller, select which configuration should be used by virtual host name and store it into ConfigurationService for later retrieval (remember ConfigurationService is session scoped)
public class LoginController {
#Autowired private CustomerConfig[] custConfigs;
#Autowired private ConfigurationService configService;
#RequestMapping(method = POST)
public String login(HttpServletRequest request, ..) {
...
String host = request.getServerName();
CustomerConfig activeConfig = // decide which one based on host..
configService.setActiveConfig(activeConfig);
...
}
}
Below is a sample FooController that reads customer specific configuration
#Controller
#RequestMapping("/foo")
public class FooController {
#Autowired private ConfigurationService configService;
#RequestMapping(method = "GET")
public String get() {
...
CustomerConfig config = configService.getActiveConfig();
...
}
...
}
If your program does not have a single entry point like a login page, you can code similar logic as a filter. Check if an active configuration is set on session, if not look it up based on host name
Related
I'm currently working on a web application using Angular 7 for front-end, spring-boot for back-end (in which I'm developing a restful web service).
I'm using #Autowired annotation to inject my services into each other and my rest controller. The problem is that in some of my services, there are some attributes that get shared when the injection is done. How do I prevent that?
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
#Service
public class ServiceA {
private boolean test;
public ServiceA (){
test = true;
}
public changeValues(){
test = false;
}
}
#Service
public class ServiceB {
#Autowired
private ServiceA serivceA;
public void method1() {
serviceA.changeValues();
}
}
#Service
public class ServiceC {
#Autowired
private ServiceA serivceA;
public void method2(){
if(serviceA.getTest()){
doSomethingNeeded();
}
}
}
public class Application{
#Autowired
private ServiceB b;
#Autowired
private ServiceC c;
public static void main(String[] args) {
b.method1();
c.method2();
}
}
In this case the method doSomethingNeeded() in ServiceC won't be able to be executed because the ressource 'test' of ServiceA is shared between both ServiceB and ServiceC. How do I prevent That?
P.S. In my case, the dependency injections are way too complex for applying any modifications to the services, that's why I need a way to configure spring-ioc dependency injection in a way to create instances of private attributes to each client session.
Spring Beans are by default Singletons and the should not contain state.
Single Page Applications (like you create with Angular) should anyway hold the state on the client side and pass the information with every request.
The main reason is when your backend is stateless it's easy to scale and is more reliable because if a backend service is restarted you don't loose anything.
You just need change the scope of ServiceA to prototype by adding #Scope(scopeName = "prototype"):
#Scope(scopeName = "prototype")
#Service
public class ServiceA {
}
Then when ServiceB and ServiceC are instantiated , separated ServiceA will be created and inject into them.
P.S. Please note that a new instance of prototype bean will only be created during ServiceB and ServiceC are instantiated. It does not mean that a new instance of prototype bean will always be created whenever you access them. You need to use one of these technique if you want such behaviour.
I have a Class that accepts the following constructor
public Student(int id, String name, Map<String, List<String>> mapInject) {
super();
this.id = id;
this.name = name;
this.mapInject = mapInject;
}
And from spring Java Config, I am injecting the constructor args like below..
#Configuration
public class JavaConfig {
#Bean
public Employee getEmployeeBean() {
Map<String,List<String>> mapInject = new HashMap<String,List<String>>();
//Add map element
return new Employee(3123,"John",mapInject);
}
}
Am i doing constructor injection here? Is this the right way to do so?
I wouldn't use Spring to handle this bean creation, unless you want ALL employees to have the same id and name which I doubt.
The power behind Spring is its Dependency Injection (DI) where you define beans for providers such as database managers, services, etc. and inject those into your components. Defining a #Bean like you have there serves no purpose as now you can only inject employees with an id of 3123 and name John.
It's important to understand that just because you are using Spring it doesn't mean EVERYTHING needs to be handled as a bean - you will always need standard POJOs for housing and passing around state (such as your Employee class) which doesn't need to have anything to do with Spring.
Down the line you might have an EmployeeService for example which houses business logic to fetch employees from a database or something, this could then be configured as a bean so it can be injected across the application.
EDIT
#Configuration
public class JavaConfig {
#Bean
#Autowired //assuming a sessionfactory been is configured elsewhere
public EmployeeService employeeService(final SessionFactory sessionfactory) {
return new EmployeeService(sessionFactory);
}
}
You could then inject this anywhere (maybe in a controller for example):
#RestController
public class EmployeeController {
private final EmployeeService employeeService;
#Autowired
public EmployeeController(final EmployeeService employeeService) {
this.employeeService = employeeService;
}
}
Where the EmployeeController doesn't need to know or care that the userService has a DB connection and doesn't need to worry about configuring it as Spring will handle all of that.
I'm trying to disable a Zuul route to a microservice registered with Eureka at runtime (I'm using spring boot).
This is an example:
localhost/hello
localhost/world
Those two are the registered microservices. I would like to disable the route to one of them at runtime without shutting it down.
Is there a way to do this?
Thank you,
Nano
Alternatively to using Cloud Config, custom ZuulFilter can be used. Something like (partial implementation to show the concept):
public class BlackListFilter extends ZuulFilter {
#Override
public String filterType() {
return "pre";
}
...
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
String uri = ctx.getRequest().getRequestURI();
String appId = uri.split("/")[1];
if (blackList.contains(appId)) {
ctx.setSendZuulResponse(false);
LOG.info("Request '{}' from {}:{} is blocked",
uri, ctx.getRequest().getRemoteHost(), ctx.getRequest().getRemotePort());
}
return null;
}
}
where blackList contains list of application IDs (Spring Boot application name) managed for example via some RESTful API.
After a lot of efforts I came up with this solution. First, I used Netflix Archaius to watch a property file. Then I proceeded as follows:
public class ApplicationRouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator {
public ApplicationRouteLocator(String servletPath, ZuulProperties properties) {
super(servletPath, properties );
}
#Override
public void refresh() {
doRefresh();
}
}
Made the doRefresh() method public by extending SimpleRouteLocator and calling its method in the overridden one of the interface RefreshableRouteLocator.
Then I redefined the bean RouteLocator with my custom implementation:
#Configuration
#EnableConfigurationProperties( { ZuulProperties.class } )
public class ZuulConfig {
public static ApplicationRouteLocator simpleRouteLocator;
#Autowired
private ZuulProperties zuulProperties;
#Autowired
private ServerProperties server;
#Bean
#Primary
public RouteLocator routeLocator() {
logger.info( "zuulProperties are: {}", zuulProperties );
simpleRouteLocator = new ApplicationRouteLocator( this.server.getServletPrefix(),
this.zuulProperties );
ConfigurationManager.getConfigInstance().addConfigurationListener( configurationListener );
return simpleRouteLocator;
}
private ConfigurationListener configurationListener =
new ConfigurationListener() {
#Override
public void configurationChanged( ConfigurationEvent ce ) {
// zuulProperties.getRoutes() do something
// zuulProperties.getIgnoredPatterns() do something
simpleRouteLocator.refresh();
}
}
}
Every time a property in the file was modified an event was triggered and the ConfigurationEvent was able to deal with it (getPropertyName() and getPropertyValue() to extract data from the event). Since I also Autowired the ZuulProperties I was able to get access to it. With the right rule I could find whether the property of Zuul
zuul.ignoredPatterns
was modified changing its value in the ZuulProperties accordingly.
Here refresh context should work (as long as you are not adding a new routing rule or removing a currently existing one), if you are adding or removing routing rules, you have to add a new bean for ZuulProperties and mark it with #RefreshScope, #Primary.
You can autowire refreshEndpoint bean for example and apply refreshEndpoint.refresh() on the listener.
Marking a custom RouteLocator as primary will cause problems as zuul already has bean of same type marked as primary.
Hi Still learning hope someone can fill in the blanks
I have JSVC quartz spring batch system that has been running for over a year. The Job launcher needs to connect to 2 spring services that successfully run in other parts of the system. Each service has a number of sql repositories or services injected into it.
Please make note of package declarations. for the application context entry.
package com.mycompany.business.services.impl;
....
#Service
public class BatchProcessService {
private final DomainSepcificRepository1 rep1;
...
private final DomainSepcificRepositoryN repN;
#Inject
public BatchProcessService(Final final DomainSepcificRepository1 rep1,..., final DomainSepcificRepositoryN repN) {
// injected values assigned to instance variables.
}
public List <...> findByCriteria(.....)(
.....
}
}
and
package com.mycompany.business.services.impl;
....
#Service
public class EmailNotificationServiceImpl implements EmailNotificationService {
private UserService userService;
private final MailMessage mailMessage;
private final MailTransport mailTransport;
#Inject
public EmailNotificationServiceImpl(final UserService userService, final MailMessage mailMessage, final MailTransport mailTransport) {
this.userService = userService;
this.mailMessage = mailMessage;
this.mailTransport = mailTransport;
}
.....
public void notifySupportStaff(....){
.....
}
}
In my application context xml file, there is the following line that should allow my job launcher to see and instantiate the above services. I think "base-package=" specifies the packages to look for #services, #components and #repositories that can be injected.
<context:component-scan base-package="com.mycompany.common.batch, com.mycompany.batch, com.mycompany.business.services" >
<context:exclude-filter type="assignable" expression="com.mycompany.common.batch.scheduler.service.MyCompanySchedulerService"/>
</context:component-scan>
I think that #Component should allow this class to see the services and instaniate them and any dependancies (other services) they have.
For some reason the jsvc system only wants to invoke class below with the NO arg constructor. and it is not injecting the 2 services.
My unit tests are able to test the method using the service only if I provide a 2 argument constructor for MOCK services.
lll
Any thoughts on why batch system cannot inject the dependencies?
package com.mycompany.batch.scheduler;
....
#Inject
private BatchProcessService batchProcessService;
#Inject
private EmailNotificationService emailNotificationService;
#Component
public class MyCompanySchedulerJobLauncher extends SchedulerJobLauncher {
public MyCompanySchedulerJobLauncher() {
super();
}
// #Inject
public MyCompanySchedulerJobLauncher(final BatchProcessService batchProcessService, final EmailNotificationService emailNotificationService) {
super();
this.batchProcessService = batchProcessService;
this.emailNotificationService = emailNotificationService;
}
#Override
public int processJob(final JobExecutionContext context) throws JobRestartException, JobExecutionAlreadyRunningException, ParseException {
......
if(batchProcessSerive.findByCriteria(....).size() == 0) {
emailNotificationService.notifySupport(...)
}
}
Well Don't I feel silly.
The problem was that at the point where I was assuming I could/would inject dependancies. The application context was private. Once I made my application context protected and to get the services. all worked
I have a bean being created by a service with the following class:
#Configuration
public class AccessManager {
#Bean(name="access", destroyMethod="destroy")
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
#Autowired
public Access create(HttpServletRequest request) {
System.out.println(request.getRemoteAddr());
return new Access();
}
}
Everything works as expected, except that when the application is starting, this method is being called, probably because I have some other singleton beans that use the Access bean. At the start up there is no request bound to the Thread, and it's expected to get a java.lang.IllegalStateException when trying to access any property of the request parameter.
No problem. The question is, is it possible to check if the underlying HttpServletRequest of the proxy request is null before calling a property that raises the exception?
You probably want to take a look at RequestContextHolder#getRequestAttributes(). That will return null if you're not currently in a context where request scope could be used.
#Configuration
public class AccessManager {
#Bean(name="access", destroyMethod="destroy")
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
#Autowired
public Access create(HttpServletRequest request) {
if (RequestContextHolder.getRequestAttributes() != null) {
System.out.println(request.getRemoteAddr());
}
return new Access();
}
}
I think the issue here is with separation of concerns. Usually your service layer should not have any dependency on the servlet classes. This is very much a controller/UI concern.
Your service class should be provided with the properties which it needs to do its job. In this case a String. This service method should be called from a controller method which is injected with the servlet request.
Something like the following:
#Controller
public class MyController {
#Autowired
private AccessManager accessManager;
#RequestMapping
public void handleRequest(HttpServletRequest request) {
accessManager.create(request.getRemoteAddr());
}
}
and your service would then look like this:
#Service
public class AccessManager {
public Access create(String remoteAddress) {
return new Access();
}
}
To sum up, anything annotated as #Service shouldn't have access to the Request.