I have to develop a Java web service that get a request and sends immediately an acknowledgment (synchronous), so that far, it is simple.
Next, the web service has to do multiple checks on the request, then send a response according to that (synchronous too, because i don't have a callback endpoint from the client).
The problem is that i can send the ackowledgment, and i launch the multiple checks in another thread, but when the checks are done, the client already recieved his response, and i can't send another one.
Here's what i did for now:
#WebService
public class Configuration {
#Resource WebServiceContext context;
#WebMethod
public ReqAckType configure(#XmlElement(required = true) #WebParam(name = "reqType")
ReqType req) {
ReqAckType ack = new ReqAckType();
ack.setReceptionTime(Calendar.getInstance());
ChecksScheduler cs = ChecksScheduler.getInstance();
Checks checks = cs.schedule(req);
ack.setInternalId(checks.getId());
return ack;
}
}
If anyone can help me figure out how to send two separate message (ack and response), knowing that i have to send them separately and the checks take too much time (it's because of that, that i have to send and ack), i would be thankful.
I am using Oracle Fusion Middleware (Weblogic, JDeveloper, ..)
Related
I want to send notifications to mobile application using rabbitmq, the problem is that i never used amqp protocol, so i need some advices
1) As i read from here http://www.rabbitmq.com/alarms.html if i send message all cosumers will get it, do i need to create separate queue for each user?
2)I want to send push using GCM only when mobile application is turn off, can i do it using this structure(spring boot)?
#Controller
public class SampleController {
Logger logger = Logger.getLogger(SampleController.class);
#Autowired
RabbitTemplate template;
#RequestMapping("/")
#ResponseBody
String home() {
return "Empty mapping";
}
#RequestMapping("/process/{message}")
#ResponseBody
String error(#PathVariable("message") String message) {
logger.info(String.format("Emit '%s'",message));
String response = (String) template.convertSendAndReceive("query-example-2",message);
logger.info(String.format("Received on producer '%s'",response));
if(response==null) {
sendPushViaGCM(message);
}
return String.valueOf("returned from worker : " + response);
}
3) If mobile appliction is turn off and i send push using gcm how to delete message from rabbitmq queue to avoid double push when application is turn on
4)As i suggested, when client connect to my rabbitmq service all others will don't have permission to listen other queues until first one is not finished. Am i right?
Some code examples will be grateful
I have 2 java projects. The first one is a RESTFUL webservice, that should handle CRUD requests. The second is a dynamic web project (which has the gui).
Let's say I have this html gui in my web project.
(Remember I don't care about security and authority principles, I just wan't to understand this first).
When I fill the information and click "Sign in" I call my login_servlet inside the web project. Inside the servlet I create a client object and call the RESTFUL web service (inside the doPost method):
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Client client = Client.create();
WebResource webR = client.resource("http://localhost:8080/MyNote/api/login/get");
ClientResponse resp = webR.accept("text/html").get(ClientResponse.class);
if (resp.getStatus() == 200){
System.out.println("****** 200 ******");
String output = resp.getEntity(String.class);
//System.out.println("****" + output + "****");
}
}
As for now the provided URL calls the following code inside the RESTFUL web service, which successfully get printed out:
#Path("/login")
public class UserLogin {
#GET
#Path("/get")
public void login(){
System.out.println("**** I'm checking if user exist in DB *****");
}
}
What I instead want to do, is to send the inserted username and password from the login_servlet as parameters to the Restful web service, and then return a response. How can I do that? (Just the part of sending parameters from one place to another + Response)
All security aside, you have a few options to send params.
As query params as Duran mentioned above.
In your Jersey request method you would handle those as:
#GET
#Path("/get")
public void login(#QueryParam("foo") String var1, #QueryParam("bar") String var2){
// do something
}
Note that the variable names do not have to match, the string you pass to #QueryParam() is what gets matched and the value injected into the variable.
As path params you would do:
#GET
#Path("/get/{foo}/{bar}")
public void login(#PathParam("foo") String var1, #PathParam("bar") String var2){
// do something
}
Here make sure that what you have as var name in {} matches what you pass to #PathParam.
As far as Jersey/JAX-RS goes this is only the tip of the iceberg, there are other options. Hope this helps you get started.
EDIT: People seem to take issue with password being passed openly so let me say this: NO, you should never pass a password in the url, this is just to serve as an example
EDIT2: Changed username to foo and password to bar.
Using path params:
//Rest API
#GET
#Path("/get/{username}/{password}")
public void login(#PathParam("username") String userName, #PathParam("password") String pwd){
}
//Jersey
ClientResponse resp = webR.accept("text/html")
.path(userName)
.path(password)
.get(ClientResponse.class);
Using Query params
//Rest API
#GET
#Path("/get")
public void login(#QueryParam("username") String username, #QueryParam("password") String pwd){
//Jersey
ClientResponse resp = webR.accept("text/html")
.queryParam("username", userName)
.queryParam("password", pwd)
.get(ClientResponse.class);
Just append the parameters to the service url like:
http://localhost:8080/MyNote/api/login/get&username=duran&password=password
Although it is possible to send parameters in a GET request (as described in previous answers), it is usually better to send a POST request and to send a JSON payload in the body.
Here, you only have 2 parameters (login and password), so it's not too bad, but I still prefer to send an object in a POST. If you want to do that, then in your RESTful service, you just have to have method annotated with POST (and check the annotations that allow you to retrieve the de-serialized object).
Having said that, 2 comments/questions:
1) why do you mix servlets and JAX-RS? You could implement everything with JAX-RS and I would recommend that. Move the registration to a JAX-RS resource.
2) #Path("/get") is an anti-pattern: you don't want to have /get in the url. You rarely want VERBS in URLs (purists would say never). Typically, to register a new user, I would send a POST request to /api/registrations, because I want to create a new registration.
I'm using Jooby's MVC routes for an API. I have also set up a websocket, to which a few clients connect. What I'm trying to do is send a message to all connected websocket clients whenever a specific http request is received in the server. This is how my route method looks like:
#Path("/player")
#Produces("application/json")
public class PlayerRoute {
#POST
public Result newPlayer(Request req, #Body Player player) {
//do some process here
//this is what I'm trying to achieve..
allWebsocketSessions.foreach(session ->
session.send("a new player has been created")
);
return Results.ok();
}
}
I've read jooby's documentation but can't figure out how to do it.
Thanks in advance.
It seems for "session you can call "set" and "get" methods only. Method "send" you can call for "response".
I would like to understand how convertAndSendToUser works in Spring SockJS+Websocket framework.
In client, we would connect as
stompClient.connect(login, password, callback())
which will result in connect request with "Stomp credentials" of login and password, that can be seen e.g. if we handle SessionConnectEvent http://www.sergialmar.com/2014/03/detect-websocket-connects-and-disconnects-in-spring-4/
But it remains unclear to me whether this will be the "user" meant in server-side send operation to a queue:
simpMessagingTemplate.convertAndSendToUser(username, "/queue/reply", message);
The closest I can get is to read this thread Sending message to specific user on Spring Websocket, answer by Thanh Nguyen Van, but it is still unclear.
Basically what I need to do, is to subscribe some clients to same topic, but on server, send them different data. Client may supply user identifier.
We know we can send messages to the client from a stomp server using the topic prefixes that he is subscribed to e.g. /topic/hello. We also know we can send messages to a specific user because spring provides the convertAndSendToUser(username, destination, message) API. It accepts a String username which means if we somehow have a unique username for every connection, we should be able to send messages to specific users subscribed to a topic.
What's less understood is, where does this username come from ?
This username is part of a java.security.Principal interface. Each StompHeaderAccessor or WebSocketSession object has instance of this principal and you can get the user name from it. However, as per my experiments, it is not generated automatically. It has to be generated manually by the server for every session.
To use this interface first you need to implement it.
class StompPrincipal implements Principal {
String name
StompPrincipal(String name) {
this.name = name
}
#Override
String getName() {
return name
}
}
Then you can generate a unique StompPrincipal for every connection by overriding the DefaultHandshakeHandler. You can use any logic to generate the username. Here is one potential logic which uses UUID :
class CustomHandshakeHandler extends DefaultHandshakeHandler {
// Custom class for storing principal
#Override
protected Principal determineUser(
ServerHttpRequest request,
WebSocketHandler wsHandler,
Map<String, Object> attributes
) {
// Generate principal with UUID as name
return new StompPrincipal(UUID.randomUUID().toString())
}
}
Lastly, you need to configure your websockets to use your custom handshake handler.
#Override
void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
stompEndpointRegistry
.addEndpoint("/stomp") // Set websocket endpoint to connect to
.setHandshakeHandler(new CustomHandshakeHandler()) // Set custom handshake handler
.withSockJS() // Add Sock JS support
}
That's It. Now your server is configured to generate a unique principal name for every connection. It will pass that principal as part of StompHeaderAccessor objects that you can access through connection event listeners, MessageMapping functions etc...
From event listeners :
#EventListener
void handleSessionConnectedEvent(SessionConnectedEvent event) {
// Get Accessor
StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage())
}
From Message Mapped APIs
#MessageMapping('/hello')
protected void hello(SimpMessageHeaderAccessor sha, Map message) {
// sha available in params
}
One last note about using convertAndSendToUser(...). When sending messages to a user, you will use something like this
convertAndSendToUser(sha.session.principal.name, '/topic/hello', message)
However, for subscribing the client, you will use
client.subscribe('/user/topic/hello', callback)
If you subscribe the client to /topic/hello you will only receive broadcasted messages.
I did not do any specific configuration and I can just do this:
#MessageMapping('/hello')
protected void hello(Principal principal, Map message) {
String username = principal.getName();
}
Similar to Wenneguen I was able to do just by injecting Principal in the MessageMapping method
public void processMessageFromClient(#Payload String message, Principal principal) {
the principal.getName() implementation is from org.springframework.security.authentication.UsernamePasswordAuthenticationToken
class
I have two Java web applications that have a single servlet that gets mapped to a specific URL:
red.war/
WEB-INF/classes
com.me.myorg.red.RedServlet (maps to http://red.example.com/doStuff)
blue.war/
WEB-INF/classes
com.me.myorg.blue.BlueServlet (maps to http://blue.example.com/doStuff)
I want to put these application (I'm calling them my "backend apps") behind a "proxy app" (servlet) that will decide which of these two apps will ultimately service a client-side request.
This proxy web app would take an incoming HTTP request, and determines which of the 2 "backend apps" (red or blue) to forward the request onto. The request would then be forwarded on to either http://red.example.com/doStuff (and then processed by RedServlet#doGet(...)) or http://blue.example.com/doStuff (and then processed by BlueServlet#doGet(...)). The returned response from the backend app (again, either RedServlet#doGet(...) or BlueServlet#doGet(...)) would then be returned to the proxy servlet, and ultimately returned to the client.
In other words, in pseudo-code:
public class ProxyServlet extends HttpServlet {
#Override
public doGet(HttpServletRequest request, HttpServletResponse response) {
String forwardingAddress;
if(shouldBeRed(request))
forwardingAddress = "http://red.example.com/doStuff";
else
forwardingAddress = "http://blue.example.com/doStuff";
PrintWriter writer = response.getWriter();
writer.write(getResponseFromBackend(forwardingAddress, request));
}
private String getResponseFromBackend(String addr, HttpServletRequest req) {
// Somehow forward req to addr and get HTML response...
}
}
Is this possible? If so, how and what code would I need to write to make it work?
You could use a RequestDispatcher to forward your request in the following way:
RequestDispatcher dispatcher = httpRequest.getRequestDispatcher(forwardingAddress);
// here you have the choice whether to use include(..) or forward(..) see below
if(useInclude)
dispatcher.include(httpRequest, httpResponse);
else
dispatcher.forward(httpRequest, httpResponse);
... where useInlcude is set to your choice with the following options:
includeThis is probably what you want to do: Load the content from the forwardingAdress into your response.
This means you could even include multiple targets into a single response.
The client will not even realize this procedure nor does he need to be able to see the target document.
forwardSend a forward to the forwardingAddress. This will tell the client to submit a new request to the specified URL.
If you do it in a browser with developer tools, you will see a second request.
The client must be able to see and load the target URL.
You can only forward to a single target.
See, the following links, too:
RequestDispatcher javadoc, especially for the notes:
forward should be called before the response has been committed to the client (before response body output has been flushed). If the response already has been committed, this method throws an IllegalStateException. Uncommitted output in the response buffer is automatically cleared before the forward.
include: The request and response parameters must be either the same objects as were passed to the calling servlet's service method or be subclasses of the ServletRequestWrapper or ServletResponseWrapper classes that wrap them.
URLRewriteFilter examplealthough this example is implemented using a Filter instead of a Servlet the behavior is the same (Note: this example is part of a framework of mine and hence contains some overhead in the parent classes. Just have a look at the relevant section...)
Since there is not yet an approved answer I try to write how I see the solution to this request use apache-http-commons library. In addition I suggest to add a flush on writer.
public class ProxyServlet extends HttpServlet {
#Override
public doGet(HttpServletRequest request, HttpServletResponse response) {
String forwardingAddress;
if(shouldBeRed(request))
forwardingAddress = "http://red.example.com/doStuff";
else
forwardingAddress = "http://blue.example.com/doStuff";
PrintWriter writer = response.getWriter();
writer.write(getResponseFromBackend(forwardingAddress, request));
**writer.flush();**
}
private String getResponseFromBackend(String addr, HttpServletRequest req) {
HttpClient client = new HttpClient();
HttpMethod method = new GetMethod(url);
client.executeMethod(method);
String body=method.getResponseBodyAsString();
return body;
}
}