How to control submit url action in Wicket form? - java

I have a wicket web application with Page mounted to bookmarkable alias. The page contains a form object with submit action.
The problem is that though the form belongs to the page the action url doesn't contain page alias, but rather created in cryptic form of wicket action.
Is there a way to adjust that behavior, so link will be like page_alias/submit ?
...
setRenderStrategy(IRequestCycleSettings.ONE_PASS_RENDER);
mountBookmarkablePage("/resetpwd", ResetPasswordPage.class);
...
public ResetPasswordPage(final String id, final PageParameters parameters) {
final Form form = new StatelessForm();
form.add(new Button("submit") {
public void onSubmit() {
...
});
add(form);

If you subclass StatelessForm instead of Form, this will take you part of the way. Rather than having something like
action="myapp/?wicket:interface=:1:eventEditor::IFormSubmitListener::"
with the page containing the form mounted at a bookmarkable URL, you'll get something like, for example,
action="myapp/mount/path/some/params/?wicket:interface=:0:eventEditor::IFormSubmitListener::"
This uses a MixedParamUrlCodingStrategy for the mount in WebApplication.init()
You can then override encodeUrlInHiddenFields() to return true, which will give you a clean URL in the action attribute.
However, all this doesn't really change the way Wicket works with forms, i. e., you still have some Wicket-specific state data in the client's markup. The reason why this is so hard, I believe, is that Wicket is meant to help you build a web app that has state. I noticed that Wicket does a lot of stuff (like comparing submitted form values with what the model's getters return before the setters are called) behind the scenes, that I know too little about to be comfortable when just cutting it out.
You can use Wicket to provide RESTful web services, though, as outlined in this blog post. There's also a project on Google code called wicket-rest that expands on this idea. Note that this seems to work as simple as it does because it just never uses the whole component based UI building stuff.
The guy who wrote this post had a different problem, but it helped me understand Wicket forms a little better anyway.

you can hide a lot of the request mumbo jumbo by using a HybridUrlCodingStrategy like so:
mount(new HybridUrlCodingStrategy("/resetpwd", ResetPasswordPage.class));
Then when the click submit, assuming you don't redirect to a new page, the url would change from
mysite.com/DocRoot/resetpwd
to
mysite.com/DocRoot/resetpwd.1
or if you really want it to be mysite.com/DocRoot/resetpwd/submit you could create a new bookmarkable page, say ResetPasswordResult.class, set your response page to that and mount it at "/resetpwd/submit"
You might look at other encoding strategies to see if their is another that suits you better:
http://cwiki.apache.org/WICKET/url-coding-strategies.html

You can take a look at http://day-to-day-stuff.blogspot.com/2008/10/wicket-extreme-consistent-urls.html and try to adapt that for Forms.

Related

Custom Post Login class in Liferay is not invoked

I'm a newbie in Liferay and I'm creating a hook to authenticate using a expando column instead of an email address.
My authentication class works fine, but one problem still remains:
After a successful authentication, I must redirect the page to the user's public or private page (any of theese is enough for me)
I followed the common of instructions for this on any forum on Internet:
Create a class that extends com.liferay.portal.kernel.events.Action and do the logic there. In my case, my class is named CustomPostLoginAction
Modify portal.properties adding the entries
login.events.post=com.liferay.sample.hook.action.CustomPostLoginAction
auth.forward.by.last.path=true
default.landing.page.path=/web/guest/home
Redeploy and "voilá"
When I reboot my web server, everything is fine, but when I run my hook using any browser, once I get successfully authenticated, it stills showing me the default login error messages. When I check my console, I found that my action class is never called and no special action is executed after my authentication class. So I have the following questions:
Where can I found an example to class to be used as a value for the property "auth.pipeline.post" if needed?
On the method authenticateByEmailAddress on my authentication class, the last argument is a java.util.Map containing parameters like "doActionAfterLogin", "redirect", "structsAction", etc. Do I get something if I assign values to those keys on that map? If yes, where can I found an example of valid values to assign to each one of them?
Do I have to change something in my custom login page? (it works, but still I have to ask this)
Is it necessary to work with the class DefaultLandingPageAction? If yes, how can I do it? Because I have only the portal's bytecodes (.class)
And most important: What am I doing wrong?
For the record:
I'm using Liferay 6.1 bundle with Tomcat 7 and SDK included with Liferay's default database.
If any of you need to watch any of my source code and/or properties files, just let me know and I will publish them.
Thanks in advance to all of you.
I can't add a comment to your original post so I'm gonna have to post an answer to ask you for some additional information (will update my answer accordingly).
Did you modify portal.properties directly or did you create a new portal.properties inside your hook?
Once you extended com.liferay.portal.kernel.events.Action, did you override the run method?

Add Freemarker support to customized JSP tag

I have a customized JSP tag library with a Java class (extending TagSupport) that generates the output for my web application. It has some parameters that are formed into HTML code using a StringBuilder.
Now the generated HTML is becoming more complex and hard to handle with calls of StringBuilder.append, so I'd like to replace the code generation with a Freemarker template.
I already found out that I could use a generic Struts component tag instead, because the Struts tags already use Freemarker template files, so I could write a tag like:
<s:component template="/components/myStruct.ftl">
<s:param name="myParam" value="%{'myParam'}" />
</s:component>
Then writing the specified template file myStruct.ftl would probably solve my problem. I actually did not try if Struts really finds and uses that file correctly, but I optimistically expect it to work.
My question is, if it's also possible to retain the current code with the customized tag
<my:struct param="myParam" />
and only change the Java class linked to that tag.
I've found code that reads a Freemarker template:
Configuration config = FreemarkerManager.getInstance().getConfiguration(pageContext.getServletContext());
config.setServletContextForTemplateLoading(pageContext.getServletContext(), "/components");
Template templ = config.getTemplate("myStruct.ftl");
templ.process(params, pageContext.getOut());
but it seems very circuitously to me and I wondered what would be the "standard" way to do it. Additionally it seemed that you cannot use tags from the Struts tag library in a template used like this. (I ran into an ArrayIndexOutOfBoundException, caused by Sitemesh... I did not analyze it yet.)
My intention was to keep the Java class as some kind of wrapper around the Struts component tag. Maybe somthing like:
OgnlValueStack stack = TagUtils.getStack(pageContext);
Component c = new Component(stack);
c.addParameter("param", param);
But I don't know how to continue this code stub. It may be crap anyway.
Is there an easy/"standard" way to do this or do I simply have to get rid of the customized tag?
Thanks in advance.
A friend of mine sent me this link:
http://cppoon.wordpress.com/2013/02/27/how-to-create-a-struts-2-component-with-freemarker/
This is what I was looking for. The gist is to change the customized tag to not extend TagSupportbut AbstractUITag which makes it a Struts tag instead of a JSP tag, roughly speaking.
This enables the automatic linkage (by name and path conventions) to my Freemarker template. I basically followed the instructions on that page. I only added the methods that are abstract in the super class, so they had to be implemented.
IMO the site lacks of a description of how the UI bean class is linked to the tag class. But as the IDE forces you to implement the getBean method inside the tag class, you quickly get to this code (using the classes described on that site):
#Override
public Component getBean(OgnlValueStack stack, HttpServletRequest request, HttpServletResponse response)
{
Pagination pagination = new Pagination(stack, request, response);
pagination.setList(list);
return pagination;
}
This might not be completely correct for the recent Struts, but it worked for the ancient version I've got to use.
Thanks again to the guy who sent me the link :)

Personalized URL in Struts 2

I'm currently developing a Struts 2 web application that allow anonymous usage. I want that with anonymous user, the URL will be like:
http://localhost:8080/myapp
But after user logged in, the URL will be personalized base on user name, for example:
http://localhost:8080/myapp/Cuong-Doan
Please suggest me a plugin/technique that can help me to achieve it.
You can try to do it with Tuckey's UrlRewriteFilter. After loging add session attribute, for example loggedUsername=Cuong-Doan. Then analyze this attribute in UrlRewriteFilter rule using session-attribute parameter of condition element. If it is present -> do redirect, add it to the URL using backreferences.
Personalized could be done via setting the parameter specifying the persona. Looking at the URLs in the question I decided to give you imagination about technique used to reproduce SEO optimized URLs. This technique is called "Parameters after action names". For example to map your persona with the action you can use
#Action(value = "/*", params = {"person", "{1}"})
so, after that action is mapped you will get the information about person via the person parameter that could be used in the action.

Struts2 - Different browsers, different machines, same form data

I believe it has something to do with Struts2 ValueStack, but if you guys could give me a hand with that would be great.
I have the following setup: Spring 3 + Struts 2.
My struts actions are pretty much like this:
#ParentPackage("struts-default")
public class StepOneAction extends ActionSupport {
...
}
Such Action has a method that is my forward:
#Action(value = "/bla", results = { #Result(name = "ble", location = "/bli.jsp") })
public String populate() {
...
return BLE;
}
Finally such Action is #Autowired.
What's puzzling me is that if I open, say Firefox, navigate to the first page - it's a 3 step wizard - fill the form and submit THEN open another browser, say Opera, and navigate to the first page, Opera has the data populated from Firefox.
How's that possible? What am I missing?
The ValueStack is per-request, so I don't see how it could possibly be a factor here.
It sounds like Spring is treating StepOneAction as a singleton, which would account for the behavior you are seeing. Actions need to be instantiated per-request.
Check that you haven't defined a member on the JSP page or Servlet that keep the same value among multiple sessions

Struts session form bean doesn't retain state

I am creating a wizard-like interface consisting of 3 jsp pages and 3 Struts actions using Struts 1.3. The flow is like below:
page1>action1 ->page2>action2 -> page3>action3
I use a session form bean (an action form with session scope) to share data between requests. The problem I am having is that the data I submitted in page2 is available in action 2, but not in action 3. I am in doubt it might be I don't have a form on page3 to hold those data, or because I call action3 via jQuery post method instead of a regular form submit, but I am really not sure.
I have been digging all the internet for almost a day and still no luck. Could anyone offer some help. Thanks a lot.
The reset() method on the form is being called with each request and thus you are losing state. You can programmatically control this.
public class MyForm extends ActionForm {
boolean reset = true;
private String[] checkboxes = {};
#Override
public void reset(ActionMapping mapping, HttpServletRequest request) {
if (reset) {
this.checkboxes = new String[];
// etc
}
reset = true;
}
public void doNotReset() {
reset = false;
}
}
Have action2 call doNotReset() on the form.
I suppose that you might have assigned a same form to both the action in StrutsConfig.xml and hence it is not giving the ClassCastException. By the way, if you want to access the same form bean which was filled on action 2 stuff, do the following
Look at the strutsConfig file for actionMapping of both the actions (2 and 3). keep the name of form different for separate action (e.g. form2 for action2 and form3 for action3).
In Action3, instead of casting the form, use this form2 = (FormBean2) session.getAttribute("form2");
The reason for above is since both the actions are using the same form, struts might have overwriting it. Hopefully above will solve your problem.
Thank you for all your inputs. Here is how I solved my problem. I don't really like this solution, but it possibly the neatest one I can find.
In page 3 I added hidden fields for what ever property I want to be available in action 3. Struts will store the values in those hidden field and when the form is submitted again, the data will then re-populated to the action form.
It seems to me that Struts works like this: when it loads page 3, it try to populate the form in page 3 with values of myForm. When the form is submitted, the process is reversed, it populate myForm with values from the user's form. The problem is that, before populating myForm with values submitted by user, it resets myForm's properties. And because after reseting, it doesn't find the value for those fields, it leaves it empty.
I don't think it makes sense for Struts to work that way, but... so be it.
How are you accessing the form bean of page2 in action2 as well as in action3.
I suppose you are accessing the wrong way. Are you getting an exception regarding invalidCast or something.

Categories