Mapping websockets endpoints at runtime - java

I know we can map a servlet at runtime since servlet api 3.0, that can be achieved as below:
#Override
public void contextInitialized( ServletContextEvent sce ) {
ServletContext sc = sce.getServletContext();
String servletMapping = "/yourURL";
ServletRegistration sr = sc.addServlet( servletMapping, "org.yourdomain.yourclass" );
sr.setInitParameter( "key", "value" );
sr.addMapping( servletMapping );
}
Is there a similar way of doing this with websockets (using javax.websockets.api)?

The Websocket equivalent of ServletContainerInitializer is the ServerApplicationConfig. It only doesn't require a services file, the Websocket API will proactively scan the WAR and JARs in WAR for any classes implementing ServerApplicationConfig interface and use them. You can in getEndpointConfigs() use ServerEndpointConfig.Builder to programmatically build a Websocket endpoint and return it.
Here's a kickoff example, given that YourEndpoint.class represents the Websocket endpoint you'd like to programmatically add, and you'd like to ignore any scanned classes.
public class YourServerApplicationConfig implements ServerApplicationConfig {
#Override
public Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> scannedClasses) {
Set<ServerEndpointConfig> configs = new HashSet<>();
configs.add(ServerEndpointConfig.Builder.create(YourEndpoint.class, "/yourPath").build());
return configs;
}
#Override
public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scannedClasses) {
return Collections.emptySet();
}
}

Related

Embedded JAX-RS CDI (Undertow, RestEasy & Weld)

I've been trying in the last couple of days to setup an embedded Undertow server, with JAX-RS, using RestEasy, and CDI using weld.
No matter what I do, nothing seems to work.
I've read through every possible answer, and demo available I could find; Nothing worked!
The JAX-RS resource:
#Path("/")
#Produces(MediaType.TEXT_PLAIN)
#RequestScoped
public class EchoResource {
#Inject
#Named("upperCase")
private UpperCaseTextProcessing upperCaseTextProcessing;
#Inject
#Named("lowerCase")
private LowerCaseTextProcessing lowerCaseTextProcessing;
#Inject
BeanManager manager;
#GET
public void greet( #Suspended AsyncResponse response, #BeanParam Aggregator queryParams ) {
final Response.ResponseBuilder responseBuilder = Response.ok();
if ( queryParams.async ) {
CompletableFuture.runAsync( () -> {
try {
Thread.sleep( 1500 );
}
catch ( InterruptedException e ) {
e.printStackTrace();
}
responseBuilder.entity( upperCaseTextProcessing.processText( queryParams.message ) );
response.resume( responseBuilder.build() );
} );
}
else {
responseBuilder.entity( upperCaseTextProcessing.processText( queryParams.message ) );
response.resume( responseBuilder.build() );
}
}
public static class Aggregator {
#QueryParam("async")
public boolean async;
#QueryParam("msg")
public String message;
#QueryParam("lower")
#DefaultValue("false")
public boolean lower;
}
}
The "UpperCaseTextProcessing" bean:
#ApplicationScoped
#Named( "upperCase" )
public class UpperCaseTextProcessing implements TextProcessing {
public UpperCaseTextProcessing() {}
#Override
public String processText( String text ) {
return text.toUpperCase();
}
}
I've used http://docs.jboss.org/weld/reference/latest/en-US/html/environments.html#_undertow as a reference
and the injection there works, but there are two things which make this example partially ok for me
The example there is using a simple HttpServlet, and not a proper JAX-RS servlet.
The "BeanManager" is injected, but trying to inject my own object fails miserably (used with #ApplicationScope)
I've setup a complete repo, with tests to ease to process of helping point me to my error.
https://github.com/eladchen/rest-easy-cdi
For bonus points, I've been wondering if it is possible to work with Weld in both contexts SE and EE, and if so, how?

Redirect HTTP to HTTPS with Ninja framework on Heroku

How to configure a Ninja web application running on Heroku to force the use of SSL, that is, redirect all requests to HTTPS?
Here is the class to add in the conf package:
public class Filters implements ApplicationFilters {
#Override
public void addFilters (List<Class<? extends Filter>> list) {
list.add (HttpsFilter.class);
}
public static class HttpsFilter implements Filter {
#Override
public Result filter (FilterChain filterChain, Context context) {
if ("http".equals (context.getHeader ("X-Forwarded-Proto"))) {
return Results.redirect ("https://" + context.getHostname ()
+ context.getRequestPath ());
}
return filterChain.next (context);
}
}
}
If you look good in the ninja framework documentation it is indicated how to configure it to get what you want
http://www.ninjaframework.org/documentation/configuration_and_modes.html

Dynamic applicationpath

A new application of ours uses multi-tenancy with multiple database. By providing a tenant id in the URL, we can select the right datasource.
But by using that kind of method, the namespace of the URL becomes dynamic (e.g.: instead of /api the url changes to /{id}/api). So is it possible to use a dynamic #ApplicationPath?
Just as it is possible to use a variable in the #Path annotation, could I write something like #ApplicationPath("/tenants/{id}/api")?
Seems applicationpath does not support dynamic segments. In the end we fixed it by using sub-resources:
Config
#ApplicationPath("tenants")
public class TenantConfig extends ResourceConfig {
public TenantConfig(ObjectMapper mapper) {
//set provider + add mapper
register(TenantsController.class);
}
}
TenantsController
#Path("/{id}/api")
public class TenantsController {
//register all your controllers including path here
#Path("/somethings")
public Class<SomethingController> something() {
return SomethingController.class;
}
}
SomethingController
#Component
//Don't use #Path, as path info is already defined in the TenantsController
public class SomethingController {
//do your stuff here;
#GET
#Path("/{id}") //Path for this example would be /tenants/{id}/api/somethings/{id}
public JsonApiResult get(#PathParam("id") int id) {
//retrieve one something
}
}

How to configure a default #RestController URI prefix for all controllers?

I know you can set the server.contextPath in application.properties to change the root context.
Also, I can add an additional context in the application config for Spring Boot like the following example (in Groovy) to add an "/api" to the URL mappings of the root context:
#Bean
ServletRegistrationBean dispatcherServlet() {
ServletRegistrationBean reg = new ServletRegistrationBean(new DispatcherServlet(), "/")
reg.name = "dispatcherServlet"
reg.addInitParameter("contextConfigLocation", "")
reg.addUrlMappings("/api/*")
reg.loadOnStartup = 2
reg
}
}
I am trying to have a separate base URI "/api" specifically for web service calls, that I can leverage for security, etc. However using the above approach will mean that any of my URIs, web service or not, can be reached with "/" or "/api", and provides no concrete segregation.
Is anyone aware of a better approach to set a base path for all #RestController(s) using configuration, without having to formally prefix every controller with /api/? If I am forced to manually prefix the URI for each controller, it would be possible to mistakenly omit that and bypass my security measures specific to web services.
Here is a reference in Stack Overflow to the same type of question, which was never completely answered:
Spring Boot: Configure a url prefix for RestControllers
In continuation to the currently accepted solution the github issue addresses the same.
Spring 5.1 and above you can implement WebMvcConfigurer and override configurePathMatch method like below
#Configuration
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix("/api",
HandlerTypePredicate.forAnnotation(RestController.class));
}
}
Now all the #RestControllers will have /api as the prefix path alongside the path configured.
Official Documentation
There's a new solution to solve this kind of problem available since Spring Boot 1.4.0.RC1 (Details see https://github.com/spring-projects/spring-boot/issues/5004)
The solution of Shahin ASkari disables parts of the Auto configuration, so might cause other problems.
The following solution takes his idea and integrates it properly into spring boot. For my case I wanted all RestControllers with the base path api, but still serve static content with the root path (f.e. angular webapp)
Edit: I summed it up in a blog post with a slightly improved version see https://mhdevelopment.wordpress.com/2016/10/03/spring-restcontroller-specific-basepath/
#Configuration
public class WebConfig {
#Bean
public WebMvcRegistrationsAdapter webMvcRegistrationsHandlerMapping() {
return new WebMvcRegistrationsAdapter() {
#Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new RequestMappingHandlerMapping() {
private final static String API_BASE_PATH = "api";
#Override
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
Class<?> beanType = method.getDeclaringClass();
RestController restApiController = beanType.getAnnotation(RestController.class);
if (restApiController != null) {
PatternsRequestCondition apiPattern = new PatternsRequestCondition(API_BASE_PATH)
.combine(mapping.getPatternsCondition());
mapping = new RequestMappingInfo(mapping.getName(), apiPattern,
mapping.getMethodsCondition(), mapping.getParamsCondition(),
mapping.getHeadersCondition(), mapping.getConsumesCondition(),
mapping.getProducesCondition(), mapping.getCustomCondition());
}
super.registerHandlerMethod(handler, method, mapping);
}
};
}
};
}
}
Also You can achieve the same result by configuring WebMVC like this:
#Configuration
public class PluginConfig implements WebMvcConfigurer {
public static final String PREFIX = "/myprefix";
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix(PREFIX, c -> c.isAnnotationPresent(MyCustomAnnotation.class));
}
}
Implement WebMvcConfigurer on any #Configuration class.
Override configurePathMatch method.
You can do many useful things with PathMatchConfigurer e.g. add prefix for several classes, that satisfy predicate conditions.
I had the same concern and was not a fan of the Spring EL option due to the issues documented and I wanted the prefix to be tightly controlled in the controllers but I did not want to depend on the developers doing the right thing.
There might be a better way these days but this is what I did. Can you guys see any downsides, I am still in the process of testing any side-effects.
Define a custom annotation.
This allows a developer to explicitly provide typed attributes such as int apiVersion(), String resourceName(). These values would be the basis of the prefix later.
Annotated rest controllers with this new annotation
Implemented a custom RequestMappingHandlerMapping
In the RequestMappingHandlerMapping, I could read the attribute of the custom annotation and modify the final RequestMappingInfo as I needed. Here are a few code snippets:
#Configuration
public class MyWebMvcConfigurationSupport extends WebMvcConfigurationSupport {
#Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
return new MyCustomRequestMappingHandlerMapping();
}
}
And in the MyCustomRequestMappingHandlerMapping, overwrite the registerHandlerMethod:
private class MyCustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
private Logger myLogger = LoggerFactory.getLogger(MyCustomRequestMappingHandlerMapping.class);
public MyCustomRequestMappingHandlerMapping() {
super();
}
#Override
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
// find the class declaring this method
Class<?> beanType = method.getDeclaringClass();
// check for the My rest controller annotation
MyRestController myRestAnnotation = beanType.getAnnotation(MyRestController.class);
if (myRestAnnotation != null) {
// this is a My annotated rest service, lets modify the URL mapping
PatternsRequestCondition oldPattern = mapping.getPatternsCondition();
// create a pattern such as /api/v${apiVersion}/${resourceName}
String urlPattern = String.format("/api/v%d/%s",
myRestAnnotation.apiVersion(),
myRestAnnotation.resourceName());
// create a new condition
PatternsRequestCondition apiPattern =
new PatternsRequestCondition(urlPattern);
// ask our condition to be the core, but import all settinsg from the old
// pattern
PatternsRequestCondition updatedFinalPattern = apiPattern.combine(oldPattern);
myLogger.info("re-writing mapping for {}, myRestAnnotation={}, original={}, final={}",
beanType, myRestAnnotation, oldPattern, updatedFinalPattern);
mapping = new RequestMappingInfo(
mapping.getName(),
updatedFinalPattern,
mapping.getMethodsCondition(),
mapping.getParamsCondition(),
mapping.getHeadersCondition(),
mapping.getConsumesCondition(),
mapping.getProducesCondition(),
mapping.getCustomCondition()
);
}
super.registerHandlerMethod(handler, method, mapping);
}
}
Slightly less verbose solution which doesn't duplicate the logic of checking the annotation, but only changes the mapping path:
private static final String API_PREFIX = "api";
#Bean
WebMvcRegistrationsAdapter restPrefixAppender() {
return new WebMvcRegistrationsAdapter() {
#Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new RequestMappingHandlerMapping() {
#Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo mappingForMethod = super.getMappingForMethod(method, handlerType);
if (mappingForMethod != null) {
return RequestMappingInfo.paths(API_PREFIX).build().combine(mappingForMethod);
} else {
return null;
}
}
};
}
};
}
Side effects
Your error controller will also be mapped under /api/error, which breaks error handling (DispatcherServlet will still redirect errors to /error without prefix!).
Possible solution is to skip /error path when adding /api prefix in the code above (one more "if").
Someone has filed an issue in the Spring MVC Jira and come up with a nice solution, which I am now using. The idea is to use the Spring Expression Language in the prefix placed in each RestController file and to refer to a single property in the Spring Boot application.properties file.
Here is the link of the issue: https://jira.spring.io/browse/SPR-13882

Perform service multiplexing with Apache Thrift and TServlet

I have a system (Java with Spring Framework) that exposes 7 different Apache Thrift servlets over HTTP using the TServlet class. Currently they all need their own Servlets, ServletMappings, Processors, Handlers etc. so implementing clients have to also keep an internal list of all the various URLs for the different services.
I understand that Apache Thrift supports multiplexing when using TServer and its derivatives by using TMultiplexingProcessor, however since I am using Spring and my Servlet, Handler and Processor are all Spring Beans that get autowired into one another, I'm unsure how to proceed.
Here's an example of how one of the services gets wired up:
UserServiceHandler.java
#Component
public class UserServiceHandler implements UserService.Iface {
#Override
public User getUser(String userId) throws TException {
// implementation logic goes here
}
}
UserServiceProcessor.java
#Component
public class UserServiceProcessor extends UserService.Processor<UserServiceHandler> {
private UserServiceHandler handler;
#Autowired
public UserServiceProcessor(UserServiceHandler iface) {
super(iface);
handler = iface;
}
public UserServiceHandler getHandler() {
return handler;
}
public void setHandler(UserServiceHandler handler) {
this.handler = handler;
}
}
UserServiceServlet.java
#Component
public class UserServiceServlet extends TServlet {
private UserServiceProcessor processor;
#Autowired
public UserServiceServlet(UserServiceProcessor p) {
super(p, new TBinaryProtocol.Factory());
processor = p;
}
}
Servlet Registration
ServletRegistration.Dynamic userService = servletContext.addServlet("UserServiceServlet", (UserServiceServlet) ctx.getBean("userServiceServlet"));
userService.setLoadOnStartup(1);
userService.addMapping("/api/UserService/*");
// This same block repeated 7 times for each *ServiceServlet with different mappings
I would like to have all 7 service handlers map to a single URL like /api/*. Is this even possible? I suppose I would have to create a single servlet and processor, but I'm unsure what they should look like. My processors extend UserService.Processor and the like.
OK, figured it out. Might not be the best way, so I welcome criticism.
Here were my rough steps:
Keep the handler classes the way they were.
Create a new class that extends TMultiplexedProcessor
Create a new class that extends TServlet
All Processors (e.g. the UserServiceProcessor have a handler property and a corresponding getter and setter
Here is my ApiMultiplexingProcessor:
#Component
public class ApiMultiplexingProcessor extends TMultiplexedProcessor {
UserServiceHandler userServiceHandler;
ReportServiceHandler reportServiceHandler;
// ... more service handlers can go here
#Autowired
public ApiMultiplexingProcessor(UserServiceProcessor userServiceProcessor, ReportServiceProcessor reportServiceProcessor) {
this.registerProcessor("UserService", userServiceProcessor);
this.registerProcessor("ReportService", reportServiceProcessor);
// add more registerProcessor lines here for additional services
userServiceHandler = userServiceProcessor.getHandler();
reportServiceHandler = reportServiceProcessor.getHandler();
// set any additional service handlers here
}
// getters and setters for the handlers
public UserServiceHandler getUserServiceHandler() {
return userServiceHandler;
}
public void setUserServiceHandler(UserServiceHandler userServiceHandler) {
this.userServiceHandler = userServiceHandler;
}
public ReportServiceHandler getReportServiceHandler() {
return reportServiceHandler;
}
public void setReportServiceHandler(ReportServiceHandler reportServiceHandler) {
this.reportServiceHandler = reportServiceHandler;
}
}
So to explain the above a bit, if you add any additional services, you need to add the *ServiceHandler classes as fields on this class, and create the getters and setters etc.
So now that we have that, we can create a new single servlet that will be added to the servlet context.
Here is my ApiServlet:
#Component
public class ApiServlet extends TServlet {
private ApiMultiplexingProcessor processor;
#Autowired
public ApiServlet(ApiMultiplexingProcessor p) {
super(p, new TBinaryProtocol.Factory());
processor = p;
}
}
And then you just add this servlet to the servlet context (from a bean) as before:
ServletRegistration.Dynamic api = servletContext.addServlet("ApiServlet", (ApiServlet) ctx.getBean("apiServlet"));
api.setLoadOnStartup(1);
api.addMapping("/api/*");
// yay now we have a single URL and a single servlet
This all could be helpful to someone else in my situation, so enjoy!
P.S. make sure when adapting your clients you use the TMultiplexedProtocol so that you can pass the service name through when talking to the server e.g.
TTransport transport = new THttpClient(new Uri("https://myapp.com/api/"));
TProtocol protocol = new TBinaryProtocol(transport);
TMultiplexedProtocol mp = new TMultiplexedProtocol(protocol, "UserService");
UserService.Client userServiceClient = new UserService.Client(mp);

Categories