How do I implement a java socket in tapestry5?
What I want to do is create a socket which I can send an XmlHttpRequest over, through a piece of javascript code.
function sendPost(url, postdata, callback) {
xmlHttp=GetXmlHttpObject()
if (xmlHttp==null) {
alert ("Browser does not support HTTP Request")
return
}
xmlHttp.onreadystatechange=callback
xmlHttp.open("POST",url,true)
xmlHttp.send(postdata);
}
Where the URL is the socket i have just created.
So you want to do an AJAX request from your client code to the server, recieve a response and process it in some way? You will not need sockets. Instead, use Tapestry's built-in AJAX functionality.
If you're loading additional content inside your page via Javascript, chances are you will not need to write any code at all. Be sure you have read the AJAX section from the Tapestry docs, and you understand what a Zone is and how it works.
Here's a basic example. Template:
<div id="myZone" t:type="Zone" t:id="myZone">
... [Initial content, if any] ...
</div>
<a t:type="ActionLink" t:id="updateContent" t:zone="myZone">Update</a>
And class:
#Inject
private Zone myZone;
#Inject
private Request request;
#OnEvent(component = "updateContent")
Object updateContent() {
... [your code] ....
if (this.request.isXHR()) {
return this.myZone.getBody();
} else {
return this;
}
}
Tapestry will do everything else, like registering the proper event listener on the link and inserting the updated content in the proper place. The if (this.request.isXHR()) makes sure your page will degrade gracefully for clients without JavaScript enabled.
If you'd like to do something else entirely, like returning a JSON object and processing it with your own JavaScript code, you can return any of these JSON classes from your event handler.
Also, if you want to write your own client-side code, be sure to use the built-in, cross-browser AJAX functionality of Prototype, which ships with Tapestry.
Edit based on comment:
You won't be able to access a different server (host + port) through AJAX because of the same origin policy. You could, however, proxy the call through your Tapestry app. I've modified my code to illustrate this (assuming the thing listening on port 2112 is an HTTP server, otherwise change as needed):
#OnEvent(component = "updateContent")
Object updateContent() throws IOException {
final URL url = new URL("http://localhost:2112");
final HttpURLConnection con = url.openConnection();
final String content;
InputSteam input = null;
try {
input = con.getInputStream();
content = IOUtils.toString(input);
} finally {
IOUtils.closeQuietly(input);
}
return new StreamResponse() {
#Override
public String getContentType() {
return "text/javascript";
}
#Override
public InputStream getStream() throws IOException {
return new ByteArrayInputStream(content.getBytes("UTF-8"));
}
#Override
public void prepareResponse(Response response) {
response.setHeader("Expires", "0");
response.setHeader("Cache-Control",
"must-revalidate, post-check=0, pre-check=0");
}
}
}
Use of Sockets is independent of your webapp view framework - you would do it pretty much the same way regardless of how the view is coded. The only thing that changes is once you've implemented your code that uses sockets, is how that's invoked.
I used tapestry with spring, and so injecting services into the spring context is the most natural approach.
The services subpackage in tapestry is mostly for creating implementations that plug into tapestry, like encoders, property conduits, and binding factories. So whether you use this or not depends upon what you are trying to achieve.
For example, if you are creating a component that reads from a socket, and renders the data read in, then you can create that as a regular component, in the components subpackage.
The XmlHttpRequest will just do a web server request which can be handled perfectly well by whatever you use to run Tapestry in. There is no need to open sockets and stuff.
Just define a route in your wep application to accept the XmlHttpRequest and have a handler, servlet, controller, ... collect the necessary data, transform it to xml and send it to the Javascript component.
I found an example here
Related
I am switching routes in my react app using -
<Route path="/myBootDomain" render={props => {
//check route path
const onpathA = window.location.href.indexOf('/pathA') !== -1;
const onpathB = window.location.href.indexOf('/pathB') !== -1;
if (onpathA){ return <PathAComponent />;}
else if(onpathB){ return <onpathBComponent />;}
}}/>
When I run the app on localhost it works as expected, so I want my controllers to map the subdomain's to the correct route, example controller -
#CrossOrigin(maxAge = 3600)
#RestController
#RequestMapping("/pathA")
public class WebContentController {
#RequestMapping(method = RequestMethod.GET)
public void method(HttpServletRequest request, HttpServletResponse httpServletResponse) {
httpServletResponse.setHeader("Location", "/myBootDomain/pathA");
httpServletResponse.setStatus(302);
}
}
When trying to access http://localhost:8080/myBootDomain/pathA instead of redirecting to the index file I got redirected to the same controller(infinite loop).
My index.html is under /resources/static.
Thank for any help.
What you want to do is to serve same index.html file that contains scripts (React app) but from different paths, ie: /myBootDomain/pathA and /myBootDomain/pathA/foo/bar. It'll make sure that when user reloads the URL he gets the same React app which then executes routing logic. See #tashkhisi answer to get more context https://stackoverflow.com/a/62193394/906265
Now it depends how the server app is set up and which paths you want to make "catch-all" but there was a similar question already Spring catch all route for index.html
The way React works with route is different from something you might have worked before with server side frameworks like JSF, PrimeFaces and other simmilar frameworks.
Generally in React all routing is handled in client side(at least it should be done this way) and this approach is called single page application because you have one page in which components will be changed based on the route you have in client side and those components communicate with server and changed their content based on response they are delivered with server. Here you are handling your routing in client side with <Route> Component and in browser when path changed to /pathA, PathAComponent will be rendered and no request is sent to the server in this situation because React is controlling everything here). You should design your PathAComponent in such a way when it is rendered it call your method in the backend with Http Rest call and do something based on the response (for instance show status code of response to user).
check this link for further discussion
the more clear way to do something you want to do is shown bellow:
class App extends Component {
render() {
return (
<Router>
<Switch>
<Route path='/pathA' exact={true} component={PathAComponent}/>
<Route path='/pathB' exact={true} component={PathBComponent}/>
</Switch>
</Router>
)
}
}
here when path change to /pathA in client side PathAComponent will be rendered and when path changed to /pathB component PathBComponent will be rendered. no request send to server up to this point to communicate with server you should call your REST api directly in your PathAComponent and PathBComponent, generally in componentDidMount method of your components.
class PathAComponent extends Component {
constructor(props) {
super(props);
this.state = {status: false, isLoading: true};
}
componentDidMount() {
this.setState({isLoading: true});
fetch('/myBootDomain/pathA')
.then(response => this.setState({status: response.status, isLoading: false}));
}
render() {
return (
<div>
statusCode: {status}
</div>
);
}
}
Is there a way to make a request RESTfully to a service that is on the same application and display it's response too? I've created a UI that has form parameters to fill out. When the user clicks submit, I'd like to have the response be embedded in the same page, displaying it to the user as json. I'd also like it to be able to be called externally of course, as it will be a restful api.
I can define in the routes file a path to return some json, but I'm not sure about how to consume it from the application itself.
I hope this is clear. I'd be happy to provide more details if necessary.
Ok. First of all, let's consider that we have the route and controller that produce JSON response for us.
GET /foo controllers.FooController.foo
object FooController extends Controller
{
.....
implicit val fooWrites = Json.writes[Foo]
def foo = Action {
Ok(Json.toJson(Foo)).as("application/json")
}
}
Then we can use ajax from our page to get the response:
<script>
......
$.get("#routes.FooController.foo")
.done(function(data){
//do something with recieved data
});
</script>
Or if you want to consume your data within your Play App,you may use WS lib. For example.
object FooController extends Controller {
....
def fooConsumerController = Action.async {
val fooJsonResultFuture =
WS.url("http://localhost:9000/foo").get().map(_.body)
.....
fooJsonResultFuture.map { json =>
// do something with this result
Ok(.....)
}
}
}
It's not very clear from your question, what behavior you want to achieve, but I hope it will help you to figure out some directions.
Real situation is like this: Java web server (Weblogic) recieves a request from user for which it has to send a ZIP archive in response. Archive has to be dynamically generated from some files user asked for and one HTML report generated by the server itself. I would like to reuse JSF servlets the server already uses in other cases to generate this report. So, basically, what I use is:
HttpURLConnection self = new URL ("http://me.myself.com/report.jsf?...").openConnection ();
String report_html = fetchHtmlFromConnection (self);
and then create the requested ZIP, including the generated HTML in it.
The question is, can I somehow avoid making an internal HTTP request (to report.jsf) in this scenario? That involves basically pointless (since application just "talks" to itself anyway) roundtrips through operating system, HTTPD (which might be on a different machine), etc.
I am not very familiar with JSF, but from what I understand of them you can use a technic that is also applicable to JSP pages:
Create your own HttpServletResponseWrapper (a class used by the container that lets you modify the response)
Use it to override the default Writer (that writes the rendered page to the output) and provide one that writes the output to a String or a temporary file that will feed the compressing code.
There is a nice and simple tutorial that shows you how to do that:
http://blog.valotas.com/2011/09/get-output-of-jsp-or-servlet-response.html
Then
As hinted by gyan, get a ServletRequestDispatcher from your servlet that will let you invoke the rendering of the JSF
Forward the servlet call in order to provide your own HttpServletResponseWrapper
Use your HttpServletResponseWrapper to get the rendered HTML and feed it to the zipping code.
So the zipping Servlet would be like:
TempFileRespWrapper respWrapper = new TempFileRespWrapper();
RequestDispatcher dispatcher = getServletContext().getRequestDispatcher( "/report.jsf");
dispatcher.forward(request, respWrapper);
File f = respWrapper.getOutputPath();
addFileToZip(f);
You should have a business service layer so that a "generateReport" service could be used inside multiple presentation views (even the "zip file" one).
You could do this in standard J2EE way via EJBs or through any custom framework that let you specify injectable business services (e.g. spring).
I think the main problem here is that you can generate the report only through the client interface (http). That makes it an isolated service, not available to other parts of the application. Basically you need a refactoring.
Do not code business inside JSFs or similar. (by the way, try not using jsf at all :D )
BUSINESS LAYER PRESENTATION
generateReportService---|---jsf-HtmlReport
\__|___
| \
someOtherContentService-|----jsf-Zip
Think about request dispatcher strategy, where request/response object would be sent to the report servlet from the entry servlet. In turn report servlet would generate the report and control can be sent to next servlet, which completes the rest of zip and send process.
For constructing a RequestDispatcher object, you can use either the ServletRequest.getRequestDispatcher() method or the ServletContext.getRequestDispatcher() method.
RequestDispatcher dispatcher=getServletContext().getRequestDispatcher( "/report.jsf" );
dispatcher.forward( request, response );
The next servlet set mime type as 'application/zip' and write the zip binary to browser. The user's browser would handle the content in the form of download depending on the browser settings.
Make sure that your web server is configured to cache (generated) html and you will not have to worry about it. First request to full or partial URL will generate a call to jsf generator and later it will be fetched from web server cache.
Yes, there will be a little overhead involved
(your source -> we server -> generator page or cache)
But it is going to be easier in the end.
Tested Solution!
This solution actually get ideas already posted here (specially from #gyan).
Write a Servlet to zip
(you could use an filter for that too. For example, suppose that you have a ZipFilter. You could map the filter for all *.zip and chain that filter with a URLRewrite filter for respective .jsf URL).
public class ZipServlet
extends HttpServlet {
#Override
public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException,
IOException {
ZipEntry zipEntry = new ZipEntry("helloWorld.html");
ZipHttpServletResponseWrapper respWrapper = new ZipHttpServletResponseWrapper(response, zipEntry);
RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/helloWorld.jsf");
dispatcher.forward(request, respWrapper);
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "inline; filename=output.zip;");
response.flushBuffer();
respWrapper.getOutputStream().close();
}
}
NOTE : Yes, you should use RequestDispatcher
ZipHttpServletResponseWrapper
There's no much to say about that. From Java 7, you can use a native Class to create zip files properly. Using Decorator pattern with ZipOutputStream on top of response.getOutputStream() should be recommended way.
Remember that HttpServletResponseWrapper is a decorator. You should not use that if you don't want to reuse the target "servlet" output directly (you could use an stunt HttpServletResponse rather than use HttpServletResponseWrapper).
public class ZipHttpServletResponseWrapper
extends HttpServletResponseWrapper {
private ZipEntry entry;
private ZipServletOutputStreamWrapper streamWrapper;
private ZipOutputStream outputStream;
private PrintWriter printWriter;
public ZipHttpServletResponseWrapper(HttpServletResponse response, ZipEntry entry) {
super(response);
this.entry = entry;
}
#Override
public ServletOutputStream getOutputStream() throws IOException {
if (streamWrapper == null) {
outputStream = new ZipOutputStream(this.getResponse().getOutputStream());
outputStream.putNextEntry(entry);
streamWrapper = new ZipServletOutputStreamWrapper(outputStream);
}
return streamWrapper;
}
#Override
public PrintWriter getWriter() throws IOException {
if (printWriter == null) {
printWriter = new PrintWriter(getOutputStream());
}
return printWriter;
}
private class ZipServletOutputStreamWrapper
extends ServletOutputStream {
private ZipOutputStream outputStream;
public ZipServletOutputStreamWrapper(ZipOutputStream outputStream) {
this.outputStream = outputStream;
}
#Override
public void close() throws IOException {
outputStream.closeEntry();
outputStream.finish();
}
#Override
public void write(int b) throws IOException {
outputStream.write(b);
}
}
}
Now, the secret: mapping wisely!
Some important parts of JSF could use Filter chain (for example, myfaces from Apache use some extensions to provide some JSF components). In this case, you should specify in these filters digest FORWARD and REQUEST
<filter-mapping>
<filter-name>extensionsFilter</filter-name>
<url-pattern>*.jsf</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
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;
}
}
How can I stream text output to the page on the browser to show the progress of an operation that may take about 15 - 20 seconds? I've tried writing to the output stream of HttpServletResponse directly, but the user still sees the full output after the whole process is finished.
This is what I've tried so far
#RequestMapping(value = "/test")
public void test(HttpServletResponse response)
throws IOException, InterruptedException {
response.getOutputStream().println("Hello");
response.getOutputStream().flush();
Thread.sleep(2000);
response.getOutputStream().println("How");
response.getOutputStream().flush();
Thread.sleep(2000);
response.getOutputStream().println("are");
response.getOutputStream().flush();
Thread.sleep(2000);
response.getOutputStream().println("you");
response.getOutputStream().flush();
}
I am no Spring MVC expert, but I would think you would do something like send a 202 response code of "accepted", which indicates the server has received the request and is going to do some asynchronous processing. Usually, the server provides a URL to allow the client to issue requests about the status of the operation. What you are trying to do violates the usual way server/client relationships work. The client calls the server, and the server responds and then the connection is closed. In what context are you trying to do this and for what reason? Perhaps I could offer some more insight or think of another way to do it?
try to use:
response.flushBuffer();
as JavaDoc says:
Forces any content in the buffer to be written to the client. A call to this method automatically commits the response, meaning the status code and headers will be written.
This worked for me when testing with Chrome
response.setBufferSize(0);
response.setContentType("text/event-stream");
... write content ...
#Controller
public class MyController{
#RequestMapping(value = "/test", method = RequestMethod.GET)
public #ResponseBody String getTest() {
return "hello how are you";
}
}
If you are using spring controller you could do the above with the response body annotation.