I've searched on this and found a few near misses. I've created a java client to consume a web service using JAX-WS. Is there a way when using JAX to set the HTTP_USER_AGENT value? I would like to have my web service log when specific clients (mine) access it so I wanted a customized value.
I've seen options where you set it in the system properties but this doesn't seem to work. The generated JAX classes don't seem to have a direct reference to the connection object so I don't see how I can manipulate those classes.
Any help would be great.
Thanks
ST
The solution to this kind of problem in JAX-WS is to implement a SoapMessage Handler (Interface: SOAPHandler< SOAPMessageContext >).
Within that handler you insert your HTTP header into maybe already existing headers, then you give control to the next handler in the handler chain.
The concept of this handler chain is kind of nice, you can have small classes for a very specific purpose (Security, Logging etc.).
In your client you configure the handler chain prior to sending any request:
// HandlerChain installieren
Binding binding = ((BindingProvider) port).getBinding();
List hchain = binding.getHandlerChain();
if (hchain == null) {
hchain = new ArrayList();
}
hchain.add(new HTTPUserAgentHandler());
binding.setHandlerChain(hchain);
And here is the code for the HTTPUserAgentHandler:
public class HTTPUserAgentHandler implements SOAPHandler<SOAPMessageContext> {
#Override
public boolean handleMessage(SOAPMessageContext context) {
boolean request = ((Boolean) context.get(SOAPMessageContext.MESSAGE_OUTBOUND_PROPERTY)).booleanValue();
if (request) {
#SuppressWarnings("unchecked")
Map<String, List<String>> headers = (Map<String, List<String>>) context
.get(MessageContext.HTTP_REQUEST_HEADERS);
if (null == headers) {
headers = new HashMap<String, List<String>>();
}
headers.put("HTTP_USER_AGENT", Collections.singletonList("user_agent"));
context.put(MessageContext.HTTP_REQUEST_HEADERS, headers);
}
return true;
}
#Override
public boolean handleFault(SOAPMessageContext context) {
return true;
}
#Override
public void close(MessageContext context) {}
#Override
public Set<QName> getHeaders() {
return null;
}
}
Let me question the idea of having HTTP header first.
A more correct (WS-centric) approach is to set SOAP Header, not HTTP header. Consider this: SOAP messages can be delivered not only by HTTP, but by JMS, SMTP or custom transports. By requiring to have user-agent HTTP Header, you unnecessary tie you code to only one transport, albeit currently prevailing.
This is the reason BTW why JAX-WS have no notion of HTTP headers except in handlers.
And (of course) StackOverlow knows how to create SOAP headers.
not sure if this is the best/most direct way to do it, but i think you could add a custom javax.xml.ws.handler.Handler to the handler chain in the dispatch javax.xml.ws.Binding. in the Handler, you should be able to set a custom map of extra http headers on the outgoing MessageContext using the MessageContext.HTTP_REQUEST_HEADERS property.
Related
In my application based on REST API, I need to return for all results of all requests, a custom field in the Response Headers. Now I use this approach:
response().setHeader("custom-field",valuateSender());
return ok(response.addData(body));
In this way, however, I am forced to call the result() method in all my actions, I'm looking for a more general and more intelligent approach.
You can add a Filter which will be global and applies to all of your routes.
then delegate the response and add your custom header before returning data to the client.
this could be a part of your filter:
public class AddCustomHeaderFilter extends Filter {
#Override
public CompletionStage<Result> apply(
Function<Http.RequestHeader, CompletionStage<Result>> nextFilter,
Http.RequestHeader requestHeader) {
return nextFilter
.apply(requestHeader)
.thenApply(
result -> {
return result.withHeader("custom-key", "custom-data");
});
}
}
more info on Filters:
https://www.playframework.com/documentation/2.8.x/JavaHttpFilters
I have a SessionListener on a CometD server. I want to pass data from a client to the server when the listener's sessionAdded() method is called.
The sessionAdded() method receives a ServerSession and ServerMessage object. ServerSession has an Attribute map that always seems to have nothing in it.
I would like to get some unique client data to the server. This data should be accessed by the server when the sessionAdded() method is invoked.
The documentation talks about basic use of a SessionListener, but says nothing about attributes. All the javadocs for client and server say about it is to describe how setAttribute() sets an attribute and how getAttribute() gets it.
Is there a way to do this? Can the ServerSession's attribute map be used to transfer attributes from the client to the server, and if so, how?
Someone please advise...
The ServerSession attributes map is a map that lives on the server.
It is an opaque (from the CometD point of view) map that applications can populate with whatever they need.
If you want to send data from a client to the server, you can just put this additional data into the handshake message, and then retrieve it from the message when BayeuxServer.SessionListener.sessionAdded() is called.
The client looks like this:
BayeuxClient client = ...;
Map<String, Object> extraFields = new HashMap<>();
Map<String, Object> ext = new HashMap<>();
extraFields.put(Message.EXT_FIELD, ext);
Map<String, Object> extraData = new HashMap<>();
ext.put("com.acme", extraData);
client.handshake(extraFields);
extraData.put("token", "foobar");
This creates an extra data structure that in JSON looks like this:
{
"ext": {
"com.acme": {
"token": "foobar"
}
}
}
It is always a very good practice to put your data under a namespace such as com.acme, so that you don't mess up with CometD fields, nor with other extensions that you may use.
Put your fields inside extraData, like for example field token in the example above.
Then, on the server:
public class MySessionListener implements BayeuxServer.SessionListener {
#Override
public void sessionAdded(ServerSession session, ServerMessage message) {
Map<String, Object> ext = message.getExt();
if (ext != null) {
Map<String, Object> extra = (Map<String, Object>)ext.get("com.acme");
if (extra != null) {
String token = (String)extra.get("token");
session.setAttribute("token", token);
}
}
}
#Override
public void sessionRemoved(ServerSession session, boolean timedout) {
}
}
This listener puts into the session attributes data that has been sent by the client, in the example above the token field.
Then, elsewhere in the application, you can access the session attributes and use that data.
I'm using CXF/JAXB to autogenerate java classes from wsdl.
All of the generated requests extend BaseRequest, which can take the user credentials required for the soap auth:
CityRequest req = new CityRequest();
req.setUsername("admin");
req.setPassword("test");
req.setPostalCode("1234");
new WsService().getCityPort().getTown(req);
Now I'd like to somehow "intercept" the outgoing Request, and automatically add the required credentials. So that in my client implementation I don't have to care about setting the authentication stuff. Is it possible with CXF?
Then credentials are not provided as a header, but just as normal xml fields.
You can use a CXF Interceptor for that, as long as it executes before the object is marshalled in the MARSHAL phase. You should be able to choose any phase before that one. See the complete list in the CXF Interceptors documentation.
In the Interceptor, you can find your outgoing object with something like the following code:
#Override
public void handleMessage(Message message) throws Fault {
List mcl = message.getContent(List.class);
if (mcl != null) {
for (Object o : mcl) {
if (o instanceof BaseRequest) {
BaseRequest baseRequest = (BaseRequest) o;
}
}
}
}
H.e.l.l.o community, i hope someone can help me ... i am using apache tomcat 8.0.0-RC5 and JSR-356 web socket API ...
I have 2 questions:
1) Is it possible to get the client ip on #OnOpen method ??
2) Is it possible to get the origin of the connection ???
I followed the websocket example which comes with the distribution of tomcat and i was not able to find the answers .... My java class is basically as follow
#ServerEndpoint(value = "/data.socket")
public class MyWebSocket {
#OnOpen
public void onOpen(Session session) {
// Here is where i need the origin and remote client address
}
#OnClose
public void onClose() {
// disconnection handling
}
#OnMessage
public void onMessage(String message) {
// message handling
}
#OnError
public void onError(Session session, Throwable throwable) {
// Error handling
}
}
Repeating the answer I already gave you on the Tomcat users mailing list...
Client IP. No. Generally this type of information is available at the handshake
which occurs before OnOpen but client IP is not one of the pieces of
information exposed. You might be better blocking these earlier e.g. with iptables or similar.
Origin. ServerEndpointConfig.Configurator.checkOrigin(String) You'll need a custom Configurator. Keep in mind that a malicious client can forge the origin header.
I know this question is old, but just in case someone else finds it in a web search:
Yes there is an easy workaround. A Servlet can receive and forward a WebSocket upgrade request. The trick is to get the client IP address and expose it as a parameter.
Here's your servlet code:
#WebServlet("/myExternalEntryPoint")
public class WebSocketServlet extends HttpServlet {
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
var dispatcher = getServletContext().getRequestDispatcher("/myInternalEntryPoint");
var requestWrapper = new MyRequestWrapper(request);
dispatcher.forward(requestWrapper, response);
}
}
And here's MyRequestWrapper:
class MyRequestWrapper extends HttpServletRequestWrapper {
public RequestWrapper(HttpServletRequest request) {
super(request);
}
public Map<String, String[]> getParameterMap() {
return Collections.singletonMap("remoteAddr", new String[] {getRequest().getRemoteAddr()});
}
}
Now in your WebSocket implementation, you'll be able to get remoteAddr via javax.websocket.Session.getRequestParameterMap().
Naturally, if your original request has parameters that you care about, you'll need to create a map that includes those as well. Also I recommend you append a separate, secret parameter and check for it in your WebSocket code to prevent anyone from hitting the internal entry point directly.
I figured out this was possible because of this thoughtful comment in the Tomcat source code (WsFilter.java):
// No endpoint registered for the requested path. Let the
// application handle it (it might redirect or forward for example)
I am trying to nest two request factory calls in each other. I retrieve a post object and in the success-method i use the same object again (just for testing purposes, I get the same behavior for other request like for example persisting).
The problem is: Only the first request reaches the server.
I don't get any error message. If I debug the code, everything works until the second request is fired. Nothing happens then. The method on the backend is not called, the frontend shows no error, even if I implement the "onFailure"-method for the receiver of the second request.
public class RequestFactoryFindTest extends GWTTestCase{
/**
* must refer to a valid module that sources this class.
*/
public String getModuleName() {
return "com.Test.MyTest";
}
public void test(){
final ClientFactory clientFactory = GWT.create(ClientFactoryImpl.class);
final MyRequestFactory requestFactory = clientFactory.getRequestFactory();
final PostRequest request = requestFactory.postRequest();
request.findPost(1l).fire(new Receiver<PostProxy>() {
#Override
public void onSuccess(PostProxy response) {
final ClientFactory clientFactory = GWT.create(ClientFactoryImpl.class);
final MyRequestFactory requestFactory = clientFactory.getRequestFactory();
final PostRequest request = requestFactory.postRequest();
System.out.println("outer success");
request.findPost(1l).fire(new Receiver<PostProxy>() {
#Override
public void onSuccess(PostProxy response) {
System.out.println("inner success");
}
});
}
});
}
}
Can someone explain this?
Edit:
I tried a lot of stuff like to fire an event on the event bus, catch the event and do my inner request factory call there. But nothing worked. I think this is some Issue with the GWTTestcase in combination with RequestFactory.
I also changed my code, so i use only one clientFactory.
Try to create an event in the first onSuccess method. When your event is handled, you could send another request to the server.
Check out How to use the GWT EventBus to use the eventbus.
Thomas Broyer statement is also right. You should only use one RequestFactory and one ClientFactory!
This may be a problem when you are constructing your second client factory as per Thomas Broyer. You should probably go into your ClientFactory.java interface and at the top add the the single client factory instance. Also put a GWT.log("ON SUCCESS") at the top of your onSuccess(PostProxy response) to make sure it is getting there.
public interface ClientFactory {
public static final ClientFactory INSTANCE = GWT.create(ClientFactory.class);
...
Then you can simple do somehting like the following
final PostRequest request = ClientFactory.INSTANCE.getRequestFactory().postRequest();