I have to write a portlet for Liferay Portal. Liferay provides convenient class MVCPortlet which allows to use simple routing for action phase using "name" attribute of <portlet:actionURL> tag and simple routing for view phase using mvcPath parameter for <renderURL> tag. What I miss, though, is the ability to prepare view for render phase. For example load some entities from database for referencing from the JSP.
I understand that I can include Java code in the JSP, but that's considered bad practice.
I use Liferay 6.1.
There's more than 1 ways to do what you want.
Make sure that a specific action is called before rendering a specific page. Fetch your resources there, and pass them as render parameters, so the jsp can get them. practically, you'll use actions to redirect to different pages
Override your render function, check the page that will be loaded, and fetch your resources if needed for the rendered jsp
use the resource phase in your jsp to call for the required resources.
Also make sure to understand the 2-phase (3-phase actually if you count the resource phase too) architecture of the MVCPortlet.
Now, about keeping the jsp code clean.. I'm not sure that the MVCPortet helps you do that. I've never seen an mvcPortlet project that doesn't have injected java snippets inside the jsp code here and there. For example, you'll have to use java code to read the request attributes
You need to define your different views (jsp) in portlet.xml's init-param tag as following:
<init-param>
<name>view-jsp</name>
<value>/jsp/view.jsp</value>
</init-param>
<init-param>
<name>edit-jsp</name>
<value>/jsp/edit.jsp</value>
</init-param>
<init-param>
<name>search-jsp</name>
<value>/jsp/search.jsp</value>
</init-param>
Send extra parameter from your portlet:renderURL / portlet:actionURL to indentify your action-type as
<portlet:actionURL var="editPageURL">
<portlet:param name="action" value="edit"/>
</portlet:actionURL>
Then in your (Java file), set global variable that is accessible in both phases (render / action) and filter action through type parameter as:
String action = ParamUtil.getString(request, "action", "view");
if(action.equals("edit")){
// do your work here
}else if(action.equals("search")){
}else{
}
Related
In my web application I have to write the URL of the web application and specify the home.jsp page that I want to be the home page, I want to know how I can access the the application in the browser just by typing the project's root folder name.
I have set the welcome-file-list in web.xml to the home.jsp that I want to get to every time I access the application, but it is not helping. Probably because I am using Struts 2 framework, If it is possible should I set the welcome-file-list in struts.xml? How do I do it?
Another question, I have so far. For example in the execute method of one action class,
I want two different results SUCCESS or ERROR, as
if(message.getMessageBody() != null &&
message.getMessageDestinationEmail() != null &&
message.getMessageHead() != null){
return SUCCESS;
} else
return ERROR;
I mapped the error to error.jsp page and success to a different .jsp page in struts.xml as
<action name="message" class="com.mule.basik.action.PostOfficeAction"
method="execute">
<result name="success">/status.jsp</result>
<result name="error">/error.jsp</result>
</action>
But whether (in the browser form) I fill in the messageHead messageBody or not the success page (/status.jsp) is returned.
I am not sure how(when) struts2 instantiates the objects members of an ActionClass, so I decided to declare a non parameter constructor that instantiates the bean message (member of the action class) because I thought for every request an instance of the action class is created and thus there is a new instance of the beans the action class has (depends on). But it didn't help anything, what am I doing wrong? I guess I should try to use Log4j and print something in execute method before the return SUCCESS and before return ERROR to determine if the if statement is evaluating to true or false, but even if I find that it is evaluating to true when inputs are entered or not , I still don't understand what I will have to do next, to make the execute return ERROR beside testing as I showed above.
It's possible to use a welcome file list, you should configure it in web.xml or web server default configuration file the files used as welcome files. When you access the web folder that contains this file the content of the the file will be loaded by web server regardless struts2 maps this folder or not as its action. Then in the welcome file you should place the code that redirect to the action. For example index.jsp is welcome file
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.jsp</welcome-file>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
index.jsp
<% response.sendRedirect("showcase.action"); %>
Second question already discussed here, initializing the beans in the constructor is a bad idea, the constructor is used by the container to instantiate the action. At the moment when it's doing it you can't access some features or services because they are not available. Better do lazy initialization after the action is created and features are available, for example when the action is executed. If you want all actions to initialize before their execution than there's Preparable interface which is by default implemented by ActionSupport which you action should extend and override prepare method.
It's your responsibility to initialize the objects. Other objects that are submitted could be created by OGNL to populate your collections.
You could use Log4j with struts2 but it requires configuration in the project and on the server. For debug purposes you could write to the System.out which is redirected in the most IDEs to the console.
Really, I don't know what you are trying to do but normally return SUCCESS if no errors are found, or ERROR if there are errors.
You can use welcome-file-list with struts also in your web.xml:
<welcome-file-list>
<welcome-file>index.htm</welcome-file>
</welcome-file-list>
Since second part of your question is answered above , so i will not go in to details, i will try suggest a solution for the first part.
Since you want everything to be handle by Struts2 when some one hit your URL, so in that case as it is required that we should follow/execute complete flow by action so that Framework should get option to initialize its internal structure.
Here is one way to do that.
you can always declare Struts2 action name in you web.xml file and i am assuming that your first action name is welcome.action, here is what we need to have in web.xml
<welcome-file-list>
<welcome-file>welcome</welcome-file>
</welcome-file-list>
Now create an empty welcome file inside your web-app folder.
Create your welcome action say `
In Last part you need to configure you Welcome action in struts.xml file or through annotation like.
<action name="welcome" class="welcomeAction">
<result>welcome.jsp</result>
</action>
You can define all you logic which you want to execute inside your welcomeAction.Please take care of these points
action name inside struts.xml file should be same as you have in welcome file list.
Don't forget to make a file with same name as your welcome action name in web-app folder as this is a trick to let container call you Action.
Hope this will help you and many others who have same issue.
`
EDIT: Edited title to reflect new findings. It doesn't matter what type of portlet the second portlet is. See general behavior below:
I have a Struts2 portlet A on the page, its default "index" action is pageA1.
I click a link on A1 and go to pageA2.
I refresh the page and the portlet A still shows pageA2.
There are other portlets on the page. I choose any one portlet B and click a link on B1.
Portlet B navigates to B2. Meanwhile, Liferay refreshes the other portlets on the page, including Struts2 portlet A.
Expected result after portlet A refresh is pageA2, however the page shown is pageA1!
Symptoms:
I added some logging to the struts action to display the parameters passed. During normal navigation (i.e. I'm just using the struts2 portlet A by itself), the parameters struts.portlet.action and struts.portlet.mode are displayed. However, if an auto-refresh occurs on struts2 portlet A due to another portlet B's render/action phase, those parameters don't seem to be passed to struts2, and thus the portlet A defaults back to its index pageA1, instead of the page shown before the refresh (pageA2).
Does this mean that since there is no struts.portlet.action param detected by struts2, it will call the default action (in this case, i set it to "index"->pageA1)?
Old Details
I have a project set up that has two portlets, both using the Struts2 framework. Both portlets are actually quite similar to each other, but their code exists in different packages, and in struts.xml, their Actions are defined in separate modules with their own namespaces. They are still part of one project, though, and are packaged together in a single WAR file.
I deploy the WAR to Liferay, and add both portlets to a single page. The following behavior occurs when I use the two portlets:
Click a link on Portlet A.
Portlet A loads into screen A2.
Click a link on Portlet B.
Portlet B loads into screen B2.
However, as a side effect, Portlet A refreshes and the screen shown is its "index" page (instead of screen A2).
Is this the expected behavior, or is there anything else I should do to make this particular setup work in a single portal page?
EDIT:
The links I am clicking are renderURLs (generated using the s:url tag). The second page of both portlets contain forms, which I am not sure is of any significance.
I've added some simple logging, and based on it, I've discovered that on every page refresh, both portlets are being rendered twice. I don't think that's a natural behavior.
Here is my struts.xml if it's of any use:
<package name="portletA" extends="struts-portlet-default" namespace="/portletA">
<action name="index" class="my.a.DisplayFirstPageAction">
<result name="success">/pageA1.jsp</result>
</action>
<action name="displayForm" class="my.a.DisplaySecondPageAction">
<result name="input">/pageA2.jsp</result>
</action>
</package>
<package name="portletB" extends="struts-portlet-default" namespace="/portletB">
<action name="index" class="my.b.DisplayFirstPageAction">
<result name="success">/pageB1.jsp</result>
</action>
<action name="displayForm" class="my.b.DisplaySecondPageAction">
<result name="input">/pageB2.jsp</result>
</action>
</package>
On both portlets, pageA1.jsp has a link that calls the struts action "displayForm". The FirstPageAction's execute method returns SUCCESS, while the SecondPageAction's execute method returns INPUT.
I thought it might be because I don't have a "success" result in my second action (my execute() method returns "input" since I have a form in the page). Adding a result=success tag doesn't help, though.
Thinking out loud, if I click on A1's link while B2 is rendered, B2's render/execute action should be called, but it seems like B1's render/execute action is called instead.
Check http://www.liferay.com/documentation/liferay-portal/6.0/development/-/ai/understanding-the-two-phases-of-portlet-execution, specifically the paragraphs bellow:
The portlet specification defines two phases for every request of a portlet, to allow the portal to differentiate when an action is being performed (and should not be repeated) and when the content is being produced (rendered):
Action phase: The action phase can only be invoked for one portlet at
a time and is usually the result of an user interaction with the
portlet. In this phase the portlet can change its status, for
instance changing the user preferences of the portlet. It is also
recommended that any inserts and modifications in the database or
operations that should not be repeated are performed in this phase.
Render phase: The render phase is always invoked for all portlets in
the page after the action phase (which may or not exist). This
includes the portlet that also had executed its action phase. It's
important to note that the order in which the render phase of the
portlets in a page gets executedis not guaranteed by the portlet
specification. Liferay has an extension to the specification through
the element render-weight in liferay-portlet.xml. Portlets with a
higher render weight will be rendered before those with a lower
value.
[...]
There are three types of URLs that can be generated by a portlet:
renderURL: this is the type of URL that we have used so far. It invokes a portlet using only its render phase.
actionURL: this type of URL tells the portlet that it should execute its action phase before rendering all the portlets in the page.
resourceURL: this type of URL can be used to retrieve images, XML, JSON or any other type of resource. It is often used to generate images or other media types dynamically. It is very useful also to make AJAX requests to the server. The key difference of this URL type in comparison to the other two is that the portlet has full control of the data that will be sent in response.
So I guess you are using an actionURL to go to page A2 instead of a renderURL.
I am in the process of changing a project which used a simple java HTTPServer on port 8080 to that which uses the Spring MVC servlet framework.
There are lots of links in the application like so:
Manage rules<br/>
Which worked fine using requests such as:
http://localhost:8080/send
However, these now don't work unless I put the servlet name in front like so:
"Manage rules<br/>"
Just wondering is there any way around this, or is it a matter of just changing all the href links to add the servlet name in front of them?
Note that I dont have to add the servlet name in my #RequestMapping calls at the start of methods, its only links that are the problem. E.g. this works fine without adding the servlet name in front
#RequestMapping(value = "/send", method = RequestMethod.GET)
If you use JSPs in your view layer, use a tag library that has a link tag (or write one yourself). Basically, the link tag has to create the url by doing
HttpServletResponse.encodeURL(originalUrl)
The <c:url> tag in JSTL does that, but it only generates the URL, not the link tag, but it can get you there like this:
<c:url value="your/relative/url" var="somevar" />
Link Text
I'm getting a little bit frustrated since I can't find out which variables I can access with the ${...} syntax in a Struts tag, placed in a JSP page.
As an example I've got the following code:
<c:set target="${status.menue}" property="activeMenuePath" value="whatever" />
Where does the object "status.menue" have to be defined in order to can be accessed with a dollar sign and braces. Is it defined in another struts tile or in the form?
It should be placed in any of the page, request, session or application scopes using respectively JspContext#setAttribute(), ServletRequest#setAttribute(), HttpSession#setAttribute() or ServletContext#setAttribute(). You normally do that either directly or indirectly inside a Servlet. MVC frameworks do that indirectly, usually configureable by giving the model object a "request", "session" or "application" scope.
The Expression Language (EL) will access them using JspContext#findAttribute().
This all is by the way unrelated to Struts. It's just a legacy MVC framework which is built on top of the JSP/Servlet API. The <c:set> is not a Struts tag as well, it's a JSTL tag.
I like to know like whenever user requests a jsp page we write hello.jsp or any html file we write hello.html or any image hello.jpeg.
My question is does servlet not have any extension ? Is it called directly called by name?
For Servlets, you have to explicitly tell the Servlet Container what URLs (either specific URLs or wildcards) map to what servlet. For example:
<servlet>
<servlet-name>HelloWorld</servlet-name>
<servlet-class>com.example.HelloWorld</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloWorld</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
The above example would map the URL /hello to the servlet com.example.HelloWorld.
You can also do some wildcard mapping. For example:
<servlet-mapping>
<servlet-name>HelloWorld</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
This would map requests ending in ".html" to the HelloWorld servlet. But you aren't limited to any particular extensions. You could use anything you want:
<servlet-mapping>
<servlet-name>HelloWorld</servlet-name>
<url-pattern>*.foo</url-pattern>
</servlet-mapping>
All of this configuration takes place in your web application's web.xml file.
No, you have it fully in your own hands. It's not necessarily called by its name, it's called by its url-pattern. You can name it to whatever you want, e.g. /pages/* will run the servlet whenever you call http://example.com/pages/foo.jsp or http://example.com/pages/foo (which in turn gives the pathinfo back by request.getPathInfo(), so that you can determine what action to take and/or where to forward the request to). Or *.page which runs the servlet whenever you call http://example.com/foo.page (which in turn gives URI back by request.getRequestURI()).
To preprocess requests (when one requests a page for view) you normally use doGet() method. To postprocess requests (after a POST form submit), you normally use doPost() method.
You can in fact create as many servlets as you want, e.g. RegisterServlet listening on /register which is backed by a register.jsp as view and a LoginServlet listening on /login and backed by a login.jsp as view, etcetera. You can hide JSPs from direct access by placing them in /WEB-INF so that users are forced to call them through the servlet.
In MVC world, there's usually means of only one servlet listening on a certain url-pattern, which is called the Front Controller. In Sun JSF for example, there's the FacesServlet which runs whenever an URL matching by default *.jsf or /faces/* is called. In Apache Struts for example, there's the ActionServlet which listens on by default *.do. They determines which action to take and/or which view (the JSP file) to display based on the URL, request parameters and/or mappings. You're however free to change those default url-patterns. You can even change the default url-pattern of the JspServlet in servlercontainer's web.xml, which by default listens on *.jsp. It's however recommended to stick to a sensible and standardizedurl-pattern.
It might be interesting to know that any other "undefinied" URL patterns are covered by a "default" servlet. Check the servletcontainer's web.xml, you'll see one servlet which listens on / and thus in fact serves everything. It also manages display of directoy listings. In Tomcat for example it's called the DefaultServlet and described here.