I've been struggling with this issue as I'm new to Struts2 development, and just started using this naming Convention Plugin recently.
I'm trying to create a simple webapp which at first will only consist in two pages:
Login page (login.jsp)
Home page (home.jsp)
First a login page is shown to the user, and if the correct username and password are provided, they log in and get redirected to the home page.
I've successfully managed to create my small webapp, writing down a custom login interceptor and everything's OK and working as expected. I'm able to redirect the user to the login page if he/she tries to call the HomeAction( which results in home.jspif you previously logged in) directly like http://myserver/homeAction.
Problem comes when I try to access JSPs directly like this:
http://myserver/home
As I'm using this Convention plugin, Struts fetches my home.jspplugin and displays it. This is not the behaviour I expected, as home.jspshould be shown only as a loginAction successful result.
Things I tried to solve this issue
Well, as far as I googled, putting my JSPs inside /WEB-INF/directory should prevent them to be accessed, but it doesn't, as all my JSPs are in /WEB-INF/content/.
Another thing I tried was blocking access to any JSPresource (blocking *.JSP requests). This does the trick as long as you try to access myserver/home.jsp , but fails (as expected) when accessing myserver/home.
EDIT: There's another question in stackoverflow about this issue but I can't understand the answer:
Struts 2 Convention Plugin and JSP files under WEB-INF
INFORMATION UPDATE: I've found that Struts2 convention plugin uses something called "actionless results" so you can access your JSPs inside your WEB-INF/contentdirectory by invoking the JSP without it's extension and it will deal with it as a dummy action which returns that JSP on success. This is an example to illustrate what I'm trying to explain:
If I have home.jsp in my WEB-INF/contentdirectory and call http://myserver/home, Struts2 will "trigger" an action whose result is going to be home.jsp. The solution for the problem then is going to be disabling this "actionless" results.
I'll keep updating the question as I head towards the solution if nobody provides an answer.
Here how d'you want to disable this feature.
Create a dummy bean:
package com.struts.handler;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.Result;
import com.opensymphony.xwork2.UnknownHandler;
import com.opensymphony.xwork2.XWorkException;
import com.opensymphony.xwork2.config.entities.ActionConfig;
/**
* Created by Roman C on 22.03.2015.
*/
public class MyUnknownHandler implements UnknownHandler {
#Override
public ActionConfig handleUnknownAction(String namespace, String actionName) throws XWorkException {
return null;
}
#Override
public Result handleUnknownResult(ActionContext actionContext, String actionName, ActionConfig actionConfig, String resultCode) throws XWorkException {
return null;
}
#Override
public Object handleUnknownActionMethod(Object action, String methodName) throws NoSuchMethodException {
return null;
}
}
Then configure it in the struts.xml:
<bean type="com.opensymphony.xwork2.UnknownHandler" name="handler" class="com.struts.handler.MyUnknownHandler"/>
<unknown-handler-stack>
<unknown-handler-ref name="handler"/>
</unknown-handler-stack>
Explained here:
The convention plugin along with configuration it creates mentioned above also put an unknown handler which should handle URLs for which a configuration is not exist (i.e. not created by the convention). This is the source of the problem.
Now putting your own handler will disable convention's one. Thus it will no longer handle results by convention.
Related
Expectation:
When i attempt booking, booking has been failed due to price change and throwing below price change exception.
Then i need to capture that errors in event and redirect the request to previous page and display the error message on top of the page.
As per my below logic i can able to redirect the request to previous page, while throwing price change exception but the issue is i can't able to
display my error message (Your booking price has been changed, please choose another) on previousPage.
Anybody can help me out. Thanks in advance.
BookAction.java
public class BookAction {
private static final String PRICE_CHANGED_ERROR = "Your booking price has been changed, please choose another";
public Event book(RequestContext context) {
try {
// Booking attempt
// Consider booking attempt failed and throwing price change exception
}catch (PriceChangeException priceChangeException) {
return handlePriceChange(context);
}
}
public Event handlePriceChange(RequestContext context) {
Errors pageErrors = getPageErrors(context);
if (true) { // Consider if its true and returning new Event "searchPage"
pageErrors.reject(PRICE_CHANGED_ERROR);
return new Event(this, "searchPage");
}
return error();
}
}
booking.xml
<webflow:flow>
<webflow:action-state id="book">
<webflow:evaluate expression="bookAction.book" />
<webflow:transition on="success" to="confirm" />
<webflow:transition on="error" to="handleFrd" />
<webflow:transition on="searchPage" to="searchPage" />
</webflow:action-state>
<webflow:end-state id="searchPage" view="externalRedirect:#{flowScope.bookState.searchResultURI}" />
</webflow:flow>
Unfortunately, this is a poorly documented part of SWF. What you are looking for is to use the flashScope. flashScope was specifically designed for this purpose after a variable in the flashScope is read once it is automatically deleted.
But... the design problem in SWF is that the flashScope only works between a Parent flow and an embedded child flow inside of it. It will not hold save your variables if you use an "externalRedirect" and try to access the error using "flashScope.error"... because technically the first flow ended and a new flow has begun. So how to solve this problem?
I was only able to solve it with 2 ways and both are hacks.
Option 1.
if the message is simple then in the 'externalRedirect:#{flowScope.bookState.searchResultURI}' append a HTTP GET param such as
?msg=An Error Occurred.
Although this will look ugly in the url bar, it is simpliest and the least painful solution.
Option 2.
is a bit more painful but provides the most flexibility if you want complex error msgs. Basically you have to merge the flashScope of Spring Web Flow and the flashMap of Spring MVC and send your externalRedirect to a spring mvc controller first.
You will need to create and register a FlowExecutionListenerAdapter
to merge (hack) the flashScope and the flashMap see the link directly below for step by step instructions (ignore the stuff about sessionMap)
Spring web flow how to add flash attribute when exiting from flow to external redirect
Then once you achieve that you can easily access your flash variable error msg using Spring MVC like so:
How to read flash attributes after redirection in Spring MVC 3.1?
Note: the variable will already be in the flashMap of spring mvc. So all you have to do is render a view in spring mvc and access the variable from the view.
My 2 cents: because of limitations like this I've personally stopped using webflow and only use Spring MVC exclusively now. I think Spring Web flow is still useful for very simple use cases (flow A -> flow B -> flow C) but if you're trying to to do something more complex or are trying to learn SWF from scratch right now... my advice to you is to just use Spring MVC... it will save you a lot of time in the long run. I think they are working on a major revision for SWF (3.0) maybe then all these limitations will get worked out
Indeed Spring Web Flow has a number of scopes where variables can be stored but when a flow ends all scopes end as well, unless the flow is a child flow delegating control back to the parent flow in which case you can use flash scope in addition to any parent flow scoped variables.
Spring Web Flow also knows how to interact with the Spring MVC flash scope. So when a top-level flow ends and the redirect is to a Spring MVC endpoint, there is a way to indicate that output variables should be put in Spring MVC flash scope, which would make them available after the redirect.
This is mentioned in the reference documentation http://docs.spring.io/autorepo/docs/webflow/current/reference/html/spring-mvc.html#spring-mvc-flash-output.
in booking.xml, add below changes
<webflow:transition on="searchPage" to="searchPage">
<set name="flowScope.error" value="'text u wanna show in next page'" type="string" />
</webflow:transition
And now in the redirected page try reading that text. hope it would work!
Use flash scope:
In xml
<set name="flashScope.flashScopeAttribute" value="'f'" />
In JSP
flash scope: #{flashScopeAttribute}
Well, this is from a developer newly using Play. When it came to using session, I found its not at all like I have been doing in servlets or jsps.
I have tried reading documentation and found session in Play are stored in HTTP cookies rather. I have tried importing HTTP class of play.
My problem however is I am unable to initialise a new session to set values in it.
I have obviously tried using 'new' session as in Java and that obviosly didnt work out.
Session session = new session();
Also after looking somewhere I have used:
Session session = Http.Context.current().session();
which shows me error in identifying context and current
I have tried looking at sample codes and codes on net. each of them however is different and I don't get the basic way of using sessions in Play, so that after that I can use put and get to keep and retrieve.
I know the question seems too basic but believe me there is no exact answer available anywhere to what I need. So please help me regarding this.
Any answer, any piece of code, or any Link on this will be highly appreciated.
Forget everything about the sessions from the jsp and servlets world while working with the Play's session. Play doesn't store anything on the server side and by design it's completely stateless. The Play session is just a cookie attached to every http request and it's stored on the client side. Word 'session' may be misleading in your case.
Working with the session is pretty straight forward. All you need is inherited from play.mvc.Controller which you have to extend when creating your own controller. To put a value in it you simply call the session(String key, String value) method from within a controller. For example:
public class Application extends Controller {
public static Result login() {
session("key", "example value");
return ok("Welcome!");
}
}
If there is no session cookie stored on client side this method will create new one and attach it to the HTTP response. Otherwise it will modify the existing one.
To read stored value use:
String value = session("key");
You can also remove value from the session:
session().remove("key");
or completely destroy it:
session().clear();
These are helper methods to work with the particular cookie witch in Play's terminology is called session. Nothing stops you from creating another cookie with similar purpose. But it'll require more writing. These helper methods saves your time and in many cases are more than enough.
You can specify session cookie name in your application.conf by setting session.cookieName property.
In play 2.8 the Http.Context was deprecated. This means, among other things, that the method "session()" is no longer available in a controller.
This is the updated way of doing it:
public Result info(Http.Request request) {
//This is the equivalent to the old session()
request.session() ...
}
The Http.Request needs to be passed down through the route defined in routes. More information here.
Does anybody know if there's play framework module that allows you to authorize pages dynamically in Java?
I'm using the play-authenticate/deadbolt modules (http://joscha.github.io/play-authenticate/ and https://github.com/schaloner/deadbolt-2) for the the authentication/authorization mechanism. However, deadbolt doesn't have a straight forward sample on how to implement authorization per page or resource. It does have the ability to create dynamic constrains but the docs are limited and don't tell you if you can create per page authorization using a model or something else.
I'm thinking of creating a model "Page" that allows you to keep track of all the permissions per page dynamically. Is there a better way ?
Thanks.
You could do this in Deadbolt by wrapping your page content in a dynamic tag. The name given to the tag maps to a DynamicResourceHandler, which can then do a lookup in the DB to see if the current user has access to the page.
For example, you view would look like this:
#(handler: my.app.MyDynamicResourceHandler)
#dynamic("handlerName", "pageKey", handler) {
Your page content goes here
}
And the handler's isAllowed method would be implemented along the lines of
public boolean isAllowed(String name,
String meta,
DeadboltHandler deadboltHandler,
Http.Context context)
{
Subject subject = deadboltHandler.getSubject();
result = // check your user's access to the page key (provided as the meta argument)
return result;
}
The documentation is being improved at the moment, and in the meantime you can see more examples at http://deadbolt-2-java.herokuapp.com/#template-dynamic
I've created a abstract base class Page that figures out how to construct a dynamic web page. I'm trying to come up with a good way to generate a Page based off the GET request that comes in as a HttpServletRequest. For example...
public class RootServlet extends HttpServlet {
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response) {
Page page = Page.generatePage(request);
// do stuff with page and write back response
}
}
In the generatePage() method, I somehow have to figure out what page is being requested, build the correct page, and then return an instance of it. But I'm not sure how to do this well... for example, I need to handle these kinds of URLs coming in:
http://example.com/ : build the default home page
http://example.com/ab123 : build the page corresponding to the given token "ab123"
http://example.com/about/ : build the "about" page
http://help.example.com/ : build the "help" page
Each of these "pages" extend the abstract base class Page so they know how to build themselves, but I'm not sure how to determine that the AboutPage needs to be built, or the HelpPage, as opposed to the default HomePage.
I'm using Apache Velocity as the template engine, so these Page objects really contain only the important information needed to generate that page, like which styles and scripts to use, and the relevant content to be displayed on the page.
I would think there are better ways to do this than to look at the end of the URL and see if "about" is a substring to build the AboutPage, for example. Any suggestions?
There are dozens of off the shelf tools frameworks that do this for you. In the very least I'd suggest Spring MVC which will work with velocity.
Spring MVC has a great way to deal with this kind of stuff using controllers with annotated methods to handle the specific pattern that you want.
They have a great example application here:
https://github.com/SpringSource/spring-mvc-showcase
Anyway, it is not a good practice to build your pages using java code.
I have a dynamic Facelets page that needs to show information from database when the page loads. At this point in the flow, there have not been any form submissions. Every JSF example I can find only shows a form submission with dynamic results on the next page.
Every call I make to our database is currently takes place after an action has been triggered by a form submission. Where should this code go if there hasn't been a form submission, and how do I trigger it? A code snippet would really help me out!
You should be able to do your initialization work in the constructor (or lazily in one of your accessors) of your managed bean.
If you're using Spring integration (see here also), it's easy.
In your backing bean, simply use something like:
public class BackingBean implements InitializingBean
{
public void afterPropertiesSet()
{
loadInitialData();
}
}
If you're not integrating with Spring there are two options:
Load the initial data in the class constructor;
In your faces-config.xml, you can set properties to be injected. Properties are guaranteed to be set in the order they're specified in the config file. So, just create a dummy property and then within that method load up your default data. i.e. create a method public void setLoaded(boolean loaded) { loadInitialData(); }, and in your faces-config.xml have 'loaded' being set as a property on that backing bean.
Hope this is all clear!
You write (with my emphasis added):
Every call I make to our database is currently takes place after an action
has been triggered by a form submission. Where should this code go
if there hasn't been a form submission, and how do I trigger it? A
code snippet would really help me out!
It sounds to me that you want to retrieve information from the database prior to form submission.
It seems to me that you want to make an Ajax call to query the database. The Ajax call can fire on a different event than the form submisson event. This will probably entail using Javascript rather than the Faces framework.