Cannot register HttpRequestHandlerServlet with Spring Boot - spring-integration

I'm trying to use HttpRequestHandlerServlet in common with HttpRequestHandlingMessagingGateway to expose simple REST URL to browser. But I cannot register HttpRequestHandlerServlet, I'm doing it in the following way:
#Bean
public ServletRegistrationBean inboundServletRegistration(ApplicationContext context) {
final HttpRequestHandlerServlet servlet = new HttpRequestHandlerServlet();
ServletRegistrationBean registration = new ServletRegistrationBean(
servlet, "/demo/*");
registration.setName("inboundServletRegistration");
return registration;
}
Spring boot application start's ok, but when try to access HttpRequestHandlingMessagingGateway endpoint with mapping:
#Bean
public HttpRequestHandler httpInboundEndPoint() {
// Http Rest gateway expecting reply.
final HttpRequestHandlingMessagingGateway restGateway = new
HttpRequestHandlingMessagingGateway(true);
// Mapping of URL this gateway consumes...
restGateway.setRequestMapping(
mapping(new HttpMethod[]{HttpMethod.GET}, "/context/{param}"));
at address http://localhost:8080/demo/context/{param} I get total nonsense crash:
org.springframework.beans.factory.BeanNotOfRequiredTypeException:
Bean named 'inboundServletRegistration' must be of type [org.springframework.web.HttpRequestHandler], but was actually of type [org.springframework.boot.context.embedded.ServletRegistrationBean]
Did you come across to this problem? Can you please help me out?

Okay, I figured it out.
Key to pass this problem is to register HttpRequestHandler under the same bean name as the HttpRequestHandlerServlet registration bean...Going rather back to XML config...:-(

Related

Best way to automatically add stomp native headers to headers

Our javascript websocket clients adds "custom" headers to all STOMP messages.
My project handles websocket endpoints using spring-websocket #Controller.
#MessageMapping(value = "/mymessages")
public void save(#Payload ToBeSaved payload, #Headers MessageHeaders headers) {
service.save(toMsg(payload, headers));
}
protected <P> Message<P> toMsg(P payload, MessageHeaders headers) {
return MessageBuilder.createMessage(payload, headers);
}
The controller modifies the payload and then passes the new payload and original websocket headers (including the custom ones) to a spring-integration #MessagingGateway.
The underlying IntegrationFlow tries to access the "custom" headers by accessing the message headers with the SPLExpression headers['custom'].
Unfortunately headers['custom'] is always null because custom is actually contained in the nativeHeaders.
I haven't found a way to tell IntegrationFlow to look into nativeHeaders.
Is there a way in spring-websocket to copy all native headers as normal headers ?
Thanks in advance
The spring-websocket can do nothing for your on the matter. It isn't its responsibility.
If you would really like to have access to something in the nativeHeaders, you should do that manually.
For your particular case that SpEL may look like:
headers['nativeHeaders']['custom']
Because nativeHeaders is a Map as well.
From other side you can use <header-enricher> in your down stream flow to pop all those nativeHeaders to top level.
And one more point: since Spring Integration 4.2 we provide native support for STOMP adapters. And there is a StompHeaderMapper which does exactly what you want and the code there looks like:
else if (StompHeaderAccessor.NATIVE_HEADERS.equals(name)) {
MultiValueMap<String, String> multiValueMap =
headers.get(StompHeaderAccessor.NATIVE_HEADERS, MultiValueMap.class);
for (Map.Entry<String, List<String>> entry1 : multiValueMap.entrySet()) {
name = entry1.getKey();
if (shouldMapHeader(name, this.outboundHeaderNames)) {
String value = entry1.getValue().get(0);
if (StringUtils.hasText(value)) {
setStompHeader(target, name, value);
}
}
}
}

How to enrich the message headers with uri variables or parameters in Spring Integration java dsl

I'm trying to enrich the headers of the messages coming from an http inbound gateway ;
my uri looks like this:
requestMapping.setPathPatterns("/context/{fooId}");
But I don't know how to use the setHeaderExpressions method of the HttpRequestHandlingMessagingGateway to catch the uri variable and put its value in the header.
I have no more success with the .enrichHeaders(...) since this code generates an exception:
IntegrationFlows.from(requestNotificationChannel())
.enrichHeaders(h -> h.header("fooId", "#pathVariables.fooId")
What is the good way to extract the values from the uri-variables and/or from the parameters ?
Thanks !
Well, you missunderstood a bit how HttpRequestHandlingMessagingGateway works or we missed something in the documentaiton.
Each SpEL evaluation is done withing EvaluationContext and it is fresh for each component. The #pathVariables EvaluationContext varialbe is available only from the HttpRequestHandlingMessagingGateway during request processing. Other similar variables from the request and available for message building from the HttpRequestHandlingMessagingGateway are:
requestAttributes
requestParams
requestHeaders
cookies
matrixVariables
What I want to say that it doesn't work for regular .enrichHeaders() because it uses a new fresh EvaluationContext and all those variable aren't available already. That's why HttpRequestHandlingMessagingGateway provides setHeaderExpressions. and here is a sample how to use it for you case:
private final static SpelExpressionParser PARSER = new SpelExpressionParser();
....
#Bean
public HttpRequestHandlingMessagingGateway httpInboundGateway() {
....
httpInboundGateway.setHeaderExpressions(Collections.singletonMap("fooId", PARSER.parseExpression("#pathVariables.fooId")));
....
}
From other side, if your requestNotificationChannel() is DirectChannel, you don't leave the HTTP Request Thread in the .enrichHeaders(), therefore you can do something like this:
.enrichHeaders(h -> h.headerFunction("fooId", m ->
((Map<String, String>) RequestContextHolder.currentRequestAttributes()
.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, 0)).get("fooId")))

Web API attribute routing HTTP 404

I can't make attribute routing to work in Web API 2. I did not start with Web API template project, I think it was Empty project. Convention based routing works, but when I add Route attribute to controller actions, I get 404 error. I find a lot of posts regarding this, but none of them helped me. It could be related to the project template, something I have to do manually since I started with Empty project. I do call MapHttpAttributeRoutes method in WebApiConfig and the class iheriting ApiController is public. What else do I have to do in WebApiConfig class and Application_Start method?
Thanks in advance.
Here is my WebApiConfig file:
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{oId}/{oTypeCode}/{oTypeName}/{email}",
defaults: new { controller = "Xrm", email = RouteParameter.Optional, oId = RouteParameter.Optional, oTypeCode = RouteParameter.Optional, oTypeName = RouteParameter.Optional}
);
}
And Application_Start method:
protected void Application_Start()
{
System.Web.Mvc.AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
log4net.Config.XmlConfigurator.Configure();
}
Of course, my fault. I was trying to pass an email address as a path parameter. Sending it as query string parameter works. I am not sure how would it look like to send as part of the path, but it works for me this way.

RMI calls by multiple concurrent users to JBoss AS 4.2.3

I want to write a web frontend that wants to "propagate" the HTTP authentication received from the browser to a JBoss AS 4.2.3 that exposes numerous #Remote interfaces.
Consider the following trivial simulation of RMI call concurrency:
Properties user1 = new Properties();
user1.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"org.jboss.security.jndi.JndiLoginInitialContextFactory");
user1.setProperty(Context.URL_PKG_PREFIXES, "org.jboss.naming");
user1.setProperty(Context.PROVIDER_URL, "127.0.0.1:1099");
user1.setProperty(Context.SECURITY_PRINCIPAL, "user1");
user1.setProperty(Context.SECURITY_CREDENTIALS, "pass1");
Properties user2 = new Properties();
user2.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"org.jboss.security.jndi.JndiLoginInitialContextFactory");
user2.setProperty(Context.URL_PKG_PREFIXES, "org.jboss.naming");
user2.setProperty(Context.PROVIDER_URL, "127.0.0.1:1099");
user2.setProperty(Context.SECURITY_PRINCIPAL, "user2");
user2.setProperty(Context.SECURITY_CREDENTIALS, "pass2");
InitialContext ctx1 = new InitialContext(user1);
Mine bean1 = (Mine) ctx1.lookup("myear/MyBean/remote");
InitialContext ctx2 = new InitialContext(user2);
Mine bean2 = (Mine) ctx2.lookup("myear/MyBean/remote");
System.out.println(bean1.whoami());
System.out.println(bean2.whoami());
Call uses jbossall-client 4.2.3 and goes to a JBoss AS 4.2.3.
The .whoami() method simply echoes the logged-in username. As it turns our, this results in both calls saying they are made by "user2". Presumably, the underlying connection is shared and only authenticated using the last seen properties bundle.
In short, this sucks. Some preliminary testing indicates that the same problem remains in JBoss AS 7 so no luck.
Is there any other RMI client implementation I can use or any parameter I can pass in the prop bundle to make the InitialContexts not share their login info? Alternatively, can someone point me to the code that needs to be hacked to make this possible?
UPDATE:
As per request:
public class Worker extends Thread {
private final String pass, user;
private int correct = 0;
public Worker(String user, String pass) { this.user = user; this.pass = pass; }
public void run() {
Properties props = new Properties();
props.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"org.jboss.security.jndi.JndiLoginInitialContextFactory");
props.setProperty(Context.URL_PKG_PREFIXES, "org.jboss.naming");
props.setProperty(Context.PROVIDER_URL, "127.0.0.1:1099");
props.setProperty(Context.SECURITY_PRINCIPAL, this.user);
props.setProperty(Context.SECURITY_CREDENTIALS, this.pass);
try {
InitialContext ctx = new InitialContext(props);
for(int i = 0; i < 100; i++) {
Mine bean = (Mine) ctx.lookup("myear/MyBean/remote");
if(bean.whoami().equals(this.user)) this.correct++;
Thread.sleep(2); }
ctx.close();
} catch (Exception e) { throw new RuntimeException(e); }
System.out.println("Done [id="+this.getId()+", good="+this.correct+"]");
}
}
Running with two workers yields:
public static void main(String[] args) throws Exception {
new Worker("user1", "pass1").start();
new Worker("user2", "pass2").start();
}
Done [t=9, good=0]
Done [t=10, good=100]
Running with 5 threads yields:
public static void main(String[] args) throws Exception {
new Worker("user1", "pass1").start();
new Worker("user2", "pass2").start();
new Worker("user3", "pass3").start();
new Worker("user4", "pass4").start();
new Worker("user5", "pass5").start();
}
Caused by: javax.ejb.EJBAccessException: Authentication failure
at org.jboss.ejb3.security.Ejb3AuthenticationInterceptor.handleGeneralSecurityException(Ejb3AuthenticationInterceptor.java:68)
at org.jboss.aspects.security.AuthenticationInterceptor.invoke(AuthenticationInterceptor.java:70)
at org.jboss.ejb3.security.Ejb3AuthenticationInterceptor.invoke(Ejb3AuthenticationInterceptor.java:110)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.ENCPropagationInterceptor.invoke(ENCPropagationInterceptor.java:46)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.asynchronous.AsynchronousInterceptor.invoke(AsynchronousInterceptor.java:106)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.stateless.StatelessContainer.dynamicInvoke(StatelessContainer.java:304)
at org.jboss.aop.Dispatcher.invoke(Dispatcher.java:106)
at org.jboss.aspects.remoting.AOPRemotingInvocationHandler.invoke(AOPRemotingInvocationHandler.java:82)
at org.jboss.remoting.ServerInvoker.invoke(ServerInvoker.java:809)
at org.jboss.remoting.transport.socket.ServerThread.processInvocation(ServerThread.java:608)
at org.jboss.remoting.transport.socket.ServerThread.dorun(ServerThread.java:406)
at org.jboss.remoting.transport.socket.ServerThread.run(ServerThread.java:173)
at org.jboss.remoting.MicroRemoteClientInvoker.invoke(MicroRemoteClientInvoker.java:163)
at org.jboss.remoting.Client.invoke(Client.java:1634)
at org.jboss.remoting.Client.invoke(Client.java:548)
at org.jboss.aspects.remoting.InvokeRemoteInterceptor.invoke(InvokeRemoteInterceptor.java:62)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.aspects.tx.ClientTxPropagationInterceptor.invoke(ClientTxPropagationInterceptor.java:67)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.aspects.security.SecurityClientInterceptor.invoke(SecurityClientInterceptor.java:53)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.remoting.IsLocalInterceptor.invoke(IsLocalInterceptor.java:74)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.stateless.StatelessRemoteProxy.invoke(StatelessRemoteProxy.java:107)
at $Proxy0.whoami(Unknown Source)
at net.windwards.Worker.run(TestRMIClient.java:31)
at org.jboss.aspects.remoting.InvokeRemoteInterceptor.invoke(InvokeRemoteInterceptor.java:74)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.aspects.tx.ClientTxPropagationInterceptor.invoke(ClientTxPropagationInterceptor.java:67)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.aspects.security.SecurityClientInterceptor.invoke(SecurityClientInterceptor.java:53)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.remoting.IsLocalInterceptor.invoke(IsLocalInterceptor.java:74)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.stateless.StatelessRemoteProxy.invoke(StatelessRemoteProxy.java:107)
at $Proxy0.whoami(Unknown Source)
at net.windwards.Worker.run(TestRMIClient.java:31)
Making the initial connection takes about 100 ms, so I tried the following (sleeping 10 ms between calls to get good overlap):
public static void main(String[] args) throws Exception {
new Worker("user1", "pass1").start();
Thread.sleep(200);
new Worker("user2", "pass2").start();
Thread.sleep(200);
new Worker("user3", "pass3").start();
Thread.sleep(200);
new Worker("user4", "pass4").start();
Thread.sleep(200);
new Worker("user5", "pass5").start();
}
Done [t=9, good=1]
Done [t=14, good=12]
Done [t=15, good=14]
Done [t=16, good=15]
Done [t=17, good=100]
From the docs for org.jboss.security.jndi.JndiLoginInitialContextFactory :
During the getInitialContext callback from the JNDI naming, layer security context identity is populated with the username ... and the credentials ... There is no actual authentication of this information. It is merely made available to the jboss transport layer for incorporation into subsequent invocations
in this case, by the time you get to invoke your beans, user2 is the last principal set and so is the one available to be used by the jboss transport layer.
However, from the jboss4 source, it looks like you can make the security context scoped to the thread context, in which case your threaded test should work, simply add this property:
userN.setProperty("jnp.multi-threaded", "true");
Another solution would be using org.jboss.security.jndi.LoginInitialContextFactory instead of org.jboss.security.jndi.JndiLoginInitialContextFactory, unlike JndiLoginInitialContextFactory, LoginInitialContextFactory will try to authenticate when the look up is made, not when the EJB is invoked, you could give it a try, even though in the docs, they recommend JndiLoginInitialContextFactory when it comes to EJB authorization on remote clients
The basic problem here is that you haven't close the first context before you use the second one in the same thread. I doubt that this is a fair test. It would be more interesting to actually make the two concurrent, by running them both in separate threads.
When the getInitialContext() is being called from the JNDI, the Security Layer invokes a wrapper with the credential tiles; which is factually never verified with a source, it is just type of a virtual representation of the tiles to JBOSS for subsequent calls to the same entity model.
In your case, user2 is the last one to be available to JBOSS.
Alternatively, you can also use multiple instances of JBOSS on the
same machine by using ServiceBindingManager. This could help you
keep a track of all RMI calls you make, also the properties for the
Connector Object do work because itself is a JMX Bean Object.
You can also use a threaded model which can give you additional security by
adding a property
userN.setProperty("jnp.multi-threaded", "true");
And just as a suggestion, I found online Use JndiLoginInitialContextFactory for EJB Authentication on remote clients.
Hope this helps!

How do I call my own service from a request/response filter in ServiceStack?

My problem is...
...I have a DTO like this
[Route("/route/to/dto/{Id}", "GET")]
public class Foo : IReturn<Bar>
{
public string Id { get; set; }
}
and need to call the service that implements the method with this signature
public Bar Get(Foo)
from a request and/or response filter. I don't know what class implements it (don't want to need to know). What I need is something like the LocalServiceClient class in the example below:
var client = new LocalServiceClient();
Bar bar = client.Get(new Foo());
Does this LocalServiceClient thing exists? JsonServiceClient has a pretty similar interface, but using it would be inneficient (I need to call my own service, I shouldn't need an extra round-trip, even to localhost, just to do this).
I'm aware of ResolveService method from Service class, but it requires me to have a service instance and to know what class will handle the request.
I think this LocalServiceClient is possible because I have all the data that a remote client (e.g. JsonServiceClient) needs to call the service - request DTO, route, verb - but couldn't find how to do it. Actually, it should be easier to implement than JsonServiceClient.
JsonServiceClient would do it, but there must be a better way, using the same request context.
What I want to do (skip this if you're not curious about why I'm doing this)
Actually, my DTOs are like this:
[EmbedRequestedLinks]
[Route("/route/to/dto/{Id}", "GET")]
public class MyResponseDto
{
public string Id { get; set; }
public EmbeddableLink<AResponseDto> RelatedResource { get; set; }
public EmbeddableLink<AnotherResponteDto> AnotherRelatedResource { get; set; }
}
EmbedRequestedLinksAttribute is a request/response filter. This filter checks if there is a query argument named "embed" in the request. If so, the filter need to "embed" the comma-separated related resources referenced by the argument into the response to this request. EmbeddableLink<T> instances can be obtained by using extension methods like these:
1) public static EmbeddableLink<T> ToEmbeddableLink<T>(this IReturn<T> requestDto)
2) public static EmbeddableLink<T> ToEmbeddableLink<T>(this T resource)
Assume a client places this request:
GET /route/to/dto/123456?embed=relatedResource HTTP/1.1
The service that will handle this request will return an instance of MyResponseDto with EmbeddableLinks created using signature (1). Then my response filter will see the embed query argument and will call the Get method of the appropriate service, replacing the RelatedResource with another instance of EmbeddableLink, this time created using extension method (2):
var client = new LocalServiceClient();
response.RelatedResource = client.Get(response.RelatedResource.RequestDto)
.ToEmbeddableLink();
The serialization routine of EmbeddableLink takes care of the rest.
In case an embeddable link is not included in the embed list the serialization routine will call the extension method ToUrl (provided by ServiceStack), that takes a verb and converts a request DTO into a URL. In this example the client will get this response:
{
"id": "9asc09dcd80a98",
"relatedResource": { "id": "ioijo0909801", ... },
"anotherRelatedResource":
{
"$link": { "href": "/route/to/another/dto/1sdf89879s" }
}
}
I know the creators of ServiceStack think that polymorphic request/responses are bad things but this case seems OK to me because I'm not creating services, instead I'm extending the framework to help me create services the way I (and possibly other users of ServiceStack) need. I'm also creating other hypermedia extensions to ServiceStack. (I hope my boss allow me to publish these extensions on github)
If you really want to do this then look the source code for ServiceStack. Look at the ServiceManager and ServiceController. These classes are responsible for registering and resolving services. You might even be able to use reflection to create services on the fly with the static EndpointHost.Metadata like so:
var operation = EndpointHost.Metadata.Operations
.FirstOrDefault(x => x.RequestType == typeof(Person));
if (operation != null)
{
var svc = Activator.CreateInstance(operation.ServiceType);
var method = operation.ServiceType.GetMethod("Get");
var response = method.Invoke(svc, new[] { new Person() });
}
This kinda works but you will get NULL exceptions if there is other code calling
var httpRequest = RequestContext.Get<IHttpRequest>();
But I would not suggest this.
Instead if you create your own Business Service classes that do all the CRUD operations (POST/PUT/GET ect). Then make the ServiceStack Services thin wrappers over them. Now you can call your own services whenever you want without worrying about the HTTP Request and ServiceStack. Only use the ServiceStack Service when you are dealing with HTTP requests
You can call the static AppHostBase.Resolve() method as demonstrated here, calling a SeviceStack Service from an MVC controller:
var helloService = AppHostBase.Resolve<HelloService>();
helloService.RequestContext = System.Web.HttpContext.Current.ToRequestContext();
var response = (HelloResponse)helloService.Any(new HelloRequest { Name = User.Identity.Name });
However, I would take #kampsj's approach of making your ServiceStack services a thin wrapper around your application service classes and only deal with HTTP/Session specific stuff in the ServiceStack service.

Resources