4
votes

Scenario:

The Spring Boot application should expose its REST endpoints only when a specific action occurs.

Is there any way in Spring to lazily expose endpoints, or even the whole HTTP subsystem?

In Apache CXF, we can do something like this:

void exposeEndpoints() {
    EndpointImpl endpoint = new EndpointImpl(cxfBus, serviceImpl);
    endpoint.publish();
}

How to do the same thing in Spring?

3
You might need to refresh ApplicationContext. Did you have a look at @RefreshScope? - Hasan Can Saral
@HasanCanSaral sorry, but i don't get it. Could you provide more detail? - MGorgon
Can you explain what kind of action is it and whether we can store it in DB as boolean value. - CodeRider

3 Answers

2
votes

The solution I have provided below is one of the ways and it may or may not be suitable in your case. It situation seems like more of design concern rather than particular implementation. I would suggest you to go with little change in the design.

  1. Store the event in DB or Session (as per requirement) as Boolean.
  2. Create Aspect using AspectJ or Spring's own AOP.
  3. Create Before aspect with specific package pointcuts.
  4. In the @Before advice, check for the boolean flag, if the condition for publishing satisfies than use joinpoint.proceed() else throw some kind of error saying service not available.

Another way is to create custom Annotation with Aspects. You can use that annotation as per requirement and not on the whole service layer. The benefit of first approach is that you have the control at generic level and the second approach at service level.

1
votes

You could take a look at @RefreshScope.

I would define my @RestController beans as follows:

@Configuration
@RefreshScope
public ControllerConfig {

    @Bean
    @ConditionalOnProperty(value = "should.initialize.rest", havingValue = true)
    public SomeController someController(){
        ....
    }

    @Bean
    @ConditionalOnProperty(value = "should.initialize.rest", havingValue = true)
    public SomeOtherController someOtherController(){
        ....
    }
}

and if you start your application with property should.initialize.rest with value false in your application.properties:

should.initialize.rest=false

Then your controllers won't be registered/initialized. When the application is running, you could then update your application.properties to:

should.initialize.rest=true

and make a call to /refresh, then your ApplicationContext will reload/refresh, this time with your REST controllers. You can find more about @RefreshScope below:

0
votes

I would suggest to create a new child context with your HTTP subsystem. Something like this:

@Service
public class MyBusinessService {

  @Autowired
  private final ApplicationContext parentContext;

  private AnnotationConfigWebApplicationContext webContext;

  public void myBusinessMethod() {
    this.webContext = new AnnotationConfigWebApplicationContext();
    this.webContext.setParent(parentContext);
    this.webContext.scan("com.mybusiness.service.webcomponents");
    this.webContext.refresh();
    this.webContext.start();
  }

}

DISCLAIMER: This is proof-of-concept code, I did not try to compile or run this. But hopefully it is enough to illustrate the concept.