I am looking for a way to configure during runtime (say, via a properties file, or deployment descriptor) which services in my Jersey-based application are available, i.e. so that services can be enabled or disabled by an administrator at the container level.
Our app presently exposes 15 different endpoints. It has 1 Application annotated with #ApplicationPath with 3 classes annotated with #Path, and within those 3 classes are 15 different methods annotated with the usual #Path/#/#Produces.
My app has a few different techniques for reading its runtime configuration, e.g. settings to connect to database resources, watching a properties file for changes, etc. What I want to do is add some configuration values so that administrators can enable/disable the 3 classes or any of the individual endpoints within those classes at the Jersey level. Could someone suggest the best way to do this?
Taking that a step further, we want to control this configuration in runtime, so if the configuration changes, we can update the jersey configuration to enable/disable the changed services without having to restart our container (which is Tomcat in this case).
Any advice appreciate! Thanks!
I don't know if this is possible natively with Jersey, but one thing I can think of is to just use a Jersey prematching filter to determine if the endpoints are disabled. You can use a service that can also act as a listener and update the disabled endpoints accordingly. If the endpoint is disabled, then just return a 404.
#Provider
#PreMatching
public class DisabledEndpointsFilter implements ContainerRequestFilter {
#Inject
private ConfigurationService configuration;
#Override
public void filter(ContainerRequestContext request) throws IOException {
final List<String> disabledEndpoints = this.configuration.getDisabledEndpoints();
final String path = stripLeadingSlash(request.getUriInfo().getPath());
for (String endpoint: disabledEndpoints) {
endpoint = stripLeadingSlash(endpoint);
if (path.startsWith(endpoint)) {
request.abortWith(Response.status(404).build());
return;
}
}
}
}
I was playing around with this and put together a working POC. You can checkout the Github Repo.
I would recommend use apache camel for this. With camel you can control the http requests combined with a embedded jetty server as proxy.
http://camel.apache.org/jetty.html
Related
I am playing around with Spring Cloud Data Flow. I have successfully deployed SCDF on Kubernetes using the related documentation. When registering the 1.5.x based starter apps, everything is working as expected, no further configuration of the starter apps during the deployment of a stream definition is needed.
When using the 2.x based starter apps, there are some changes introduced by the switch to Spring Boot 2.0 that need to be accommodated for, e.g. the actuator endpoints changed. For reference, here are the properties that I provide during the deployment of the stream:
app.*.management.endpoints.web.exposure.include=health,info,binders
deployer.*.cpu=2
deployer.*.memory=4096
deployer.http.count=2
deployer.*.kubernetes.livenessProbePath=/actuator/health
deployer.*.kubernetes.readinessProbePath=/actuator/info
However, the readiness probe fails since the health and the info endpoint now seem to be protected by default. Therefore, the pods end up in crashloops since from the Kubernetes perspective they get never ready.
I worked around the situation by following the guide on patching the starter apps that my stream definition relies on (e.g. throughput sink) like this:
#SpringBootApplication
#Import({org.springframework.cloud.stream.app.throughput.sink.ThroughputSinkConfiguration.class})
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Configuration
protected static class ThroughputSinkSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.requestMatchers(EndpointRequest.to("health", "info")).permitAll();
}
}
}
Is there a way to specify this kind of security configuration via flags or properties? Shouldn't such a WebSecurityConfigurerAdapter be there by default to make the health and info endpoints accessible for Kubernetes?
Artem's response is very relevant. I wanted to also share a few other approaches specific to security and OOTB apps.
In 1.6 SNAPSHOTs, we have recently added support via spring-cloud/spring-cloud-deployer-kubernetes#236 to plug basic-auth realm to interact with secured actuator endpoints. They are applicable to both liveness and readiness probes. Here's the commit/docs for your reference.
If you don't really want security at all, though not recommended, you can explicitly disable the Security configuration.
dataflow:>stream create foo -- definition "http | throughput"
dataflow:>stream deploy foo --properties app.*.spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration"
(i.e., all the Apps in the foo stream definition will start with SecurityAutoConfiguration excluded)
I would suggest to look into the situation from the other angle and provide credential from the Kubernetes to get access to your secured Microservice.
The problem of the current status-quo that all the resources has to be protected.
You can generate your own static password and store it in the application.properties do not reconfigure Kubernetes for each application restart: https://docs.spring.io/spring-boot/docs/2.0.3.RELEASE/reference/htmlsingle/#boot-features-security
I'm trying to make a basic project using spring cloud with the netflix addons such as Hystrix, Eureka and Ribbon to learn how this works. The project I'm trying to make is a simple message server that will keep messages. And a message-client that will just ask the server for a message, and I want to use the auto discovery client for this, or the RestTemplate discovery. But I can't get either to work.
I have the following structure:
message-client (eureka client)
message-server (eureka client)
configuration-service (config server)
discovery-service (eureka server)
What I currently do is I start up the configuration-service, and expose the application.yml details to all of these "apps/clients" when they are connecting by the following structure:
config-service\src\main\resources\config\appname.yml
app\src\main\resources\bootstrap.yml (contains the appname and url to cloud config)
This is working just fine, and my apps start up on the port they receive from the config server, as well as they all connect to my eureka server, and all of them are visible there. As well as the Hystrix failover is also working, not that it is related to this but it tells me that it can't be completely wrong then.
But here comes my confusion...
When using the #Autowired annotation in my service class (#Service annotated) inside my client module, I get a discoveryClient object, but I am unable to find any other services using that one.
Message Client - boot class:
#EnableAutoConfiguration
#EnableHystrix
#EnableEurekaClient
#ComponentScan("cloud.rest.resources, spring.cloud.client")
public class ClientBoot {
public static void main(String[] args) {
SpringApplication.run(ClientBoot.class, args);
}
}
Message Client - REST Resource:
#RestController
public class MessageResource {
#Autowired
private MessageClient messageClient;
#RequestMapping(value = "/message/{client}", method = RequestMethod.GET)
public Message getMessage(#PathVariable String client) {
return messageClient.getMessage(client);
}
}
Message Client - MessageClient:
#Service
public class RestMessageClient implements MessageClient {
#Autowired
private DiscoveryClient discoveryClient;
#Autowired
private RestTemplate restTemplate;
#Override
public Message getMessage(String client) {
return restTemplate.getForObject(String.format("http://message-server/message/%s", client), Message.class);
}
}
My message server boot class that is holding the messages has the same annotations as my client one.
And as I said, my service class are unable to find anything..
Which leads me to all of my questions:
What is required to actually use ribbon load balancer?
Do I have to use ribbon to be able to use the "auto discovery", I thought not but now I'm just confused.
From what I've understood, when using EnableEurekaClient I should not need to use the EnableDiscoveryClient as well?
Can I change the yml files on my config-server for the clients in runtime and just have to reboot the client?
How much configuration is really meant to be shared by the config-server, because currently all of my clients just contain a super basic bootstrap.yml file.
Does anyone have a good link to where I can read more about all the properties that is being set in my yml files? Both a documentation of what the properties that exists actually do as well as some documentation on how I can use them in combination with spring cloud?
Do I need specific properties to enable my apps/clients to find other apps/clients?
Edited information
Thank you for your quick and excellent reply, I've gone through this over and over today and I finally got my application working..
The problem (I can't understand why and was hoping you could help me understand that) is that my discovery service contains yml files for each of my other clients where I specify things like port and eureka information.. What I specified here as well was:
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka
So, when I set this value it seems to override something that makes my service discovery not working.. Even tho I can see all my applications in the eureka server, they were unable to find each other when I had this value set.
I set this value by having a message-server.yml file in my configuration service that is sent out to my message-server application after bootstrap..
So then I have two new questions.
How do I override this eureka server property?
Why does my discovery client stop working when I set this value, what is it that it actually does?
What is required to actually use ribbon load balancer?
The ribbon-loadbalancer must be on the classpath (e.g. via "spring-cloud-starter-ribbon"). Then you can inject one as a LoadBalancerClient or you can inject a RestTemplate (it will be load-balancer aware if you have a LoadBalancerClient).
Do I have to use ribbon to be able to use the "auto discovery", I thought not but now I'm just confused.
What is "auto discovery"? You don't need to use Ribbon to use the DiscoveryClient (Ribbon is a load balancer, not a service registry).
From what I've understood, when using EnableEurekaClient I should not need to use the EnableDiscoveryClient as well?
Correct. #EnableEurekaClient is annotated with #EnableDiscoveryClient so it is only there to express a preference.
Can I change the yml files on my config-server for the clients in runtime and just have to reboot the client?
Yes. Or you can use the /refresh or /restart endpoints (a full reboot is probably best in production, at least periodically).
How much configuration is really meant to be shared by the config-server, because currently all of my clients just contain a super basic bootstrap.yml file.
As much as you want. How long is a piece of string? If I were you I would try and keep the central config to a minimum (only the things that change between environments, or at runtime).
Does anyone have a good link to where I can read more about all the properties that is being set in my yml files? Both a documentation of what the properties that exists actually do as well as some documentation on how I can use them in combination with spring cloud?
Spring Boot and Spring Cloud have autogenerated metadata for externalized properties. The new generation of IDEs understands them (so get STS 3.6.4 or IDEA 14.1), and they are listed in the user guide (for Spring Boot at least) under http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#common-application-properties
Do I need specific properties to enable my apps/clients to find other apps/clients?
You need to be able to locate your service registry (Eureka in this case). If you are using Eureka and your clients have registered then that is enough.
I've built an application that loads multiple plugins over SPI. Each plugin has to implement the FrameworkPlugin interface.
public interface FrameworkPlugin {
...
ContextHandler getWebContent();
String getPluginID();
...
}
An example implementation would be
...
ContextHandler getWebContent() {
WebAppContext wac=new WebAppContext();
wac.setBaseResource(new ResourceCollection(new String[] {"./src/main/webapp"}));
return wac;
}
...
The Main-Project runs a single Jetty instance, which should be used by all plugins providing web-based services. A plugin has to be able to configure it's individual environment within its own context, except the base URL, which is managed by the main project.
...
for (FrameworkPlugin p : PLUGINS) {
ContextHandler h= p.getWebContent();
h.setContextPath("/plugin/"+p.getPluginID().toString());
collection.addHandler(h);
}
jettyInstance.setHandler(collection);
...
So in theory, plugins should be accessible under /plugin/<id>. Unfortunately Jetty throws an IllegalArgumentException, because plugins set ./src/main/webapp as resource base, which does not exist in the main project.
So how to supply a context handler via the plugin interface?
Deploying WARs is not an option, as the loading mechanism can't be changed. Everything has to be done by using the plugin interface.
If this isn't going to be a WAR based project, then skip the use of WebAppContext entirely, and just use the Handler system in Jetty itself.
The WebAppContext is a specialized Handler meant to be used with a full blown and complete Web Application that follows the servlet spec. It mandates behavior that is mandated by the servlet spec, from the mundane (how url mapping order behaves), past the black art (per element descriptor order lookup and override), to the complex (webapp classloader isolation).
The Handler system in Jetty can be 1..n Handlers, in a Tree (via HandlerCollections), sometimes with Context, mutable Handler collections, and all exists at the same classloader as the server (if you want it to). Feel free to url map with regex, or your own implementation of level 3 URI templates. Completely up to you what you want to do with it. You have many pre-built Handlers and base Handler to build up from.
You'll still have access to the standard HttpServletRequest and HttpServletResponse, along with the async processing and I/O that those provide. But you'll also have access to the raw internal Request object which offers even more features over the HttpServletRequest.
Incidentally, many servlet based applications support plugins now. Most accomplish this using Controller Servlets, or Filters, or manipulation of the ServletContext's registrations.
I have an OSGi bundle (that is not owned by me - so I cannot change it!) that exposes (exports) a service EchoService, and I want to attach an aspect to methods of this service (so as to perform some pre/post processing around it). These are deployed on the Apache Felix container.
I've written my own OSGi bundle (that obviously imports the EchoService), and attaches Spring aspects to it using standard Spring AOP. However, looks like the aspects are not attached and my interceptor is not being invoked.
I suspect that this is because I'm trying to intercept a service that does not belong to my bundle (which seems reasonable). Is that correct? How can I overcome this?
Here's what my interceptor/aspect looks like:
#Before("serviceOperation()")
public void before(JoinPoint jp) {
logger.debug("Entering method: " + jp.toShortString());
}
#AfterReturning("serviceOperation()")
public void after(JoinPoint jp) {
logger.debug("Exiting method: " + jp.toShortString());
}
I'm not an AOP nor a Spring expert, but maybe I could give you some ideas. As far as I see Spring use standard J2SE dynamic proxies for AOP proxies. Hence your clients should use the proxy instead of the original EchoService object. This is also true when you're using CGLIB proxies because "the proxies are created by sub-classing the actual class".
If your client bundles asking for an EchoService you have to pass them the proxy somehow. For this inside an OSGi container you should also export an EchoService (proxy) and make sure that the clients use the proxied service/bundle, not the original. You can accomplish this by setting a different version number for the (proxied) package and set this version as an import requirement in your client bundles. (I suppose you can modify the clients of EchoService.) For services you can set a property when you're registering it and modify the clients to query only for services which have this property.
If you are not able to modify the client bundles another solution could be wrapping the original bundle as an internal jar in your bundle. You can call the wrapped bundle's activator from your activator and pass them a modified BundleContext. This BundleContext should catch the registering service calls and register the proxy object instead of the original EchoService. You can use simple delegate pattern since BundleContext, ServiceListener etc. are usually interfaces. I suppose it could work but it maybe has other challenges.
I'm writing a project for academic purposes which among other irrelevant stuff, includes writing a filter which monitors servlet/jsp response times.
The thing is that the filter should work on every deployed web application in the server and not only over a specific one, I just couldn't find any information regarding applying "global" filters.
Is it even possible?
NOTE:
It's important to mention that i'm using Apache Tomcat 7 as the server of choice.
Thanks!
Mikey
You could provide the filter in Tomcat's common classpath and edit Tomcat's own /conf/web.xml to add the filter, but this does not run on non-existing webapp contexts (i.e. it does not cover all possible requests) and it is overrideable in all deployed webapps. The more robust solution depends on the servlet container used. In case of Tomcat, you need the Valve component.
Kickoff example:
import org.apache.catalina.valves.ValveBase;
public class MyValve extends ValveBase {
#Override
public void invoke(Request request, Response response) throws IOException, ServletException {
// ...
getNext().invoke(request, response);
}
}
register it as follows in server.xml:
<Valve className="com.example.MyValve" />
Filters are configured per-web app, but Tomcat itself may have a mechanism for timing request/response processing times.
You can config the url-pattern of your filter to process any request/response that you want. Please check http://www.caucho.com/resin-3.0/config/webapp.xtp#filter-mapping