Redirect to the same page after performing different operations in Liferay? - liferay

I have created a portlet in which i am doing CRUD Operations (User & Organization). But whenever I Add, Edit or Delete an Organization. I am redirect to the add user page after the operation. How can I stay on the same page after every operation ?
I tried using request dispatcher method and LastPath but couldn't make then work.
Now, I am using send redirect method which is working but whenever I signout and again signin this doesn't work (maybe because of Instance).
So how can i make this work properly please help.
Last Path Method Not Working.
HttpSession httpSession = httpServletRequest.getSession();
User user = UserLocalServiceUtil.fetchUser(UserId);
LastPath last_path = new LastPath("http://localhost:8080/web/my-site/one?p_p_id=my_registration_form_MyRegistrationFormPortlet_INSTANCE_HQMU9wIdWhH5&p_p_lifecycle=0&p_p_state=normal&p_p_mode=view&_my_registration_form_MyRegistrationFormPortlet_INSTANCE_HQMU9wIdWhH5_mvcPath=%2FaddOrganization.jsp"," ");
httpSession.setAttribute(WebKeys.LAST_PATH, last_path);
Working but have to set again after Signing out.
actionResponse.sendRedirect("http://localhost:8080/web/my-site/one?p_p_id=my_registration_form_MyRegistrationFormPortlet_INSTANCE_HQMU9wIdWhH5&p_p_lifecycle=0&p_p_state=normal&p_p_mode=view&_my_registration_form_MyRegistrationFormPortlet_INSTANCE_HQMU9wIdWhH5_mvcPath=%2FaddOrganization.jsp");
EDIT:
// For Adding Organizations
#ProcessAction(name = "addOrganization")
public void addOrganization(ActionRequest actionRequest, ActionResponse actionResponse) throws IOException, PortletException
{
long UserId = themeDisplay.getUserId();
String organizationName = ParamUtil.getString(actionRequest, "organizationName");
String country = ParamUtil.getString(actionRequest, "country");
String type = "organization";
long countryId = ParamUtil.getLong(actionRequest, "countryId");
long statusId = ParamUtil.getLong(actionRequest, "statusId");
try
{
Organization organization = OrganizationLocalServiceUtil.addOrganization(UserId, 0, organizationName, type, 0,countryId, 12017, null, false, null);
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
In my view.jsp
<!-- For Displaying Add Organization Page -->
<portlet:renderURL var = "addOrganizations">
<portlet:param name = "mvcPath" value = "/addOrganization.jsp"/>
</portlet:renderURL>
In my addOrganization.jsp
<!-- For Displaying Add Organization Page -->
<portlet:renderURL var = "addOrganizations">
<portlet:param name = "mvcPath" value = "/addOrganization.jsp"/>
</portlet:renderURL>
<!-- For redirecting to the addOrganization method in MyRegistrationFormPortlet -->
<portlet:actionURL var = "addOrganization_actionURL">
<portlet:param name = "javax.portlet.action" value = "addOrganization"/>
</portlet:actionURL>
<form>
// my form
</form>

Ok I did quick checks and you can add the mvcPath as you do it for rendering directly in the actions.
#ProcessAction(name = "addOrganization")
public void addOrganization(ActionRequest actionRequest, ActionResponse actionResponse)
throws IOException, PortletException {
long UserId = themeDisplay.getUserId();
String organizationName = ParamUtil.getString(actionRequest, "organizationName");
String country = ParamUtil.getString(actionRequest, "country");
String type = "organization";
long countryId = ParamUtil.getLong(actionRequest, "countryId");
long statusId = ParamUtil.getLong(actionRequest, "statusId");
try {
Organization organization = OrganizationLocalServiceUtil.addOrganization(UserId, 0, organizationName, type,
0, countryId, 12017, null, false, null);
} catch (Exception ex) {
ex.printStackTrace();
}
actionResponse.getRenderParameters().setValue("mvcPath", "/addOrganization.jsp");
}

Are you using MVCActionCommands for this? Example for adding a user.
You can specify a mvcRenderCommandName parameter which will redirect to a render command. In the render command you can return a String with your jsp to call.
#Component(immediate = true, property = { "javax.portlet.name=" + PortletKeys.Test,
"mvc.command.name=/addUser" }, service = MVCActionCommand.class)
public class AddUserActionCommand implements MVCActionCommand {
#Override
public boolean processAction(ActionRequest actionRequest, ActionResponse actionResponse) throws PortletException {
//adding user to db logic
actionResponse.setRenderParameter("mvcRenderCommandName", "/users");
return false;
}
}
RenderCommand
#Component(immediate = true, property = { "javax.portlet.name=" + PortletKeys.Test,
"mvc.command.name=/users" }, service = MVCRenderCommand.class)
public class UserRenderCommand implements MVCRenderCommand {
#Override
public String render(RenderRequest renderRequest, RenderResponse renderResponse) throws PortletException {
//doing some stuff
return "/users/view.jsp";
}
}
Just be sure that the annotation "mvc.command.name" matches the render parameter which you set in "mvcRenderCommandName".

As I wrote in my first answer I would recommend to split the actions in own classes. If you try to put everything into the Portlet class you can try to include a jsp with the include() method. This is how we did that in 6.2 but I see include() is still available in 7.x.
// For Adding Organizations
#ProcessAction(name = "addOrganization")
public void addOrganization(ActionRequest actionRequest, ActionResponse actionResponse) throws IOException, PortletException
{
long UserId = themeDisplay.getUserId();
String organizationName = ParamUtil.getString(actionRequest, "organizationName");
String country = ParamUtil.getString(actionRequest, "country");
String type = "organization";
long countryId = ParamUtil.getLong(actionRequest, "countryId");
long statusId = ParamUtil.getLong(actionRequest, "statusId");
try
{
Organization organization = OrganizationLocalServiceUtil.addOrganization(UserId, 0, organizationName, type, 0,countryId, 12017, null, false, null);
}
catch (Exception ex)
{
ex.printStackTrace();
}
include('/html/myJsp.jsp',actionRequest,actionResponse);
}
Check:
https://docs.liferay.com/ce/portal/7.2-latest/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCPortlet.html#include-java.lang.String-javax.portlet.ActionRequest-javax.portlet.ActionResponse-

Related

JSF ExceptionHandler - Response already committed in some cases

We have implemented a custom ExceptionHandler that attempts to intercept any NonexistentConversationException that are thrown if a user requests a URL with an old cid in it (eg. bookmarked link etc.).
The handler simply removed the cid from the URL and performs a redirect to the updated URL.
This solution seems to work in most cases, we have a number of different user 'roles' within the App each with their own login areas and for one of these user types it seems that fc.getExternalContext().isResponseCommitted() is always true when the ExceptionHandler fires which means we are unable to perform the redirect. All other user types it works ok.
I am not sure exactly what the difference is with this user type for this to happen, I am guessing some CDI bean we using setup differently
Is there a way to unsure the ExceptionHandler kicks in earlier before the response is committed?
Below is the handler...
public class ConversationExpiredExceptionHandler
extends ExceptionHandlerWrapper {
final static Logger log = LoggerFactory.getLogger(ConversationExpiredExceptionHandler.class);
private ExceptionHandler wrapped;
private static String CONVERSATION_PARAM = "cid";
public ConversationExpiredExceptionHandler(ExceptionHandler wrapped) {
this.wrapped = wrapped;
}
#Override
public ExceptionHandler getWrapped() {
return this.wrapped;
}
#Override
public void handle() throws FacesException {
for (Iterator<ExceptionQueuedEvent> i =
getUnhandledExceptionQueuedEvents().iterator();
i.hasNext();) {
ExceptionQueuedEvent event = i.next();
ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();
Throwable t = context.getException();
if (t instanceof javax.enterprise.context.NonexistentConversationException
|| t instanceof org.jboss.weld.contexts.NonexistentConversationException
|| t.getCause() instanceof org.jboss.weld.contexts.NonexistentConversationException /* can be wrapped in FacesException */) {
final FacesContext fc = FacesContext.getCurrentInstance();
try {
if (!fc.getExternalContext().isResponseCommitted()) {
if(Faces.getRequestQueryStringMap().containsKey(CONVERSATION_PARAM)) {
String requestedUrl = Faces.getRequestURLWithQueryString();
String updatedUrl = this.removeQueryParameter(requestedUrl,CONVERSATION_PARAM);
log.debug("No conversation active for {}, redirecting to {}",requestedUrl,updatedUrl);
fc.getExternalContext().redirect(updatedUrl);
}
}
fc.renderResponse();
break;
} catch (Exception ex) {
throw new FacesException(ex);
} finally {
i.remove();
}
}
}
getWrapped().handle();
}
private String removeQueryParameter(String url, String parameterName) throws URISyntaxException {
URIBuilder uriBuilder = new URIBuilder(url);
List<NameValuePair> queryParameters = uriBuilder.getQueryParams();
for (Iterator<NameValuePair> queryParameterItr = queryParameters.iterator(); queryParameterItr.hasNext();) {
NameValuePair queryParameter = queryParameterItr.next();
if (queryParameter.getName().equals(parameterName)) {
queryParameterItr.remove();
}
}
uriBuilder.setParameters(queryParameters);
return uriBuilder.build().toString();
}

JSF <protected-views> token inconsistency

I'm currently having trouble regarding the token generated by <protected-views> of JSF.
I added the page I want to protect in faces-config.xml
<protected-views>
<url-pattern>/restricted/account-management/users.xhtml</url-pattern>
<url-pattern>/restricted/account-management/users.jsf</url-pattern>
</protected-views>
Then for example when I go the users page using an <h:link>
<h:link outcome="users" title="View">
<f:param name="user" value="#{e.id}" />
</h:link>
the token generated in the URL is this
/restricted/account-management/users.jsf?javax.faces.Token=OW5KkkfJZrrfmZSXwA%253D%253D&user=4
The page returns a ProtectedViewException
Then I found out that the correct token is actually:
/restricted/account-management/users.jsf?javax.faces.Token=OW5KkkfJZrrfmZSXwA%3D%3D
The token was encoded in the URL, where % became %25. When I copy-paste the correct token into the URL, I get into the users page successfully.
Any help would be appreciated.
This is a problem with the versions 2.2.11 and above of Mojarra JSF Implementation, you can see the details about issue in https://github.com/javaee/javaserverfaces-spec/issues/1161 and here https://github.com/javaserverfaces/mojarra/issues/4139
One of the alternatives to handle the problem is to create a CustomExternalContext to handle the double encoding.
First you need declare in faces-config.xml a CustomExternalContextFactory:
<factory>
<external-context-factory>com.proitc.config.CustomExternalContextFactory</external-context-factory>
</factory>
In the ExternalContextFactory you define the CustomExternalContext:
public class CustomExternalContextFactory extends ExternalContextFactory {
private ExternalContextFactory externalContextFactory;
public CustomExternalContextFactory() {}
public CustomExternalContextFactory(ExternalContextFactory externalContextFactory) {
this.externalContextFactory = externalContextFactory;
}
#Override
public ExternalContext getExternalContext(Object context, Object request, Object response)
throws FacesException {
ExternalContext handler = new CustomExternalContext((ServletContext) context,
(HttpServletRequest) request, (HttpServletResponse) response);
return handler;
}
}
The CustomExternalContext override the methods encodeBookmarkableURL and encodeRedirectURL:
public class CustomExternalContext extends ExternalContextImpl {
public CustomExternalContext(ServletContext sc, ServletRequest request,
ServletResponse response) {
super(sc, request, response);
}
#Override
public String encodeBookmarkableURL(String baseUrl, Map<String, List<String>> parameters) {
FacesContext context = FacesContext.getCurrentInstance();
String encodingFromContext =
(String) context.getAttributes().get(RIConstants.FACELETS_ENCODING_KEY);
if (null == encodingFromContext) {
encodingFromContext =
(String) context.getViewRoot().getAttributes().get(RIConstants.FACELETS_ENCODING_KEY);
}
String currentResponseEncoding =
(null != encodingFromContext) ? encodingFromContext : getResponseCharacterEncoding();
UrlBuilder builder = new UrlBuilder(baseUrl, currentResponseEncoding);
builder.addParameters(parameters);
String secureUrl = builder.createUrl();
//Handle double encoding
if (parameters.size() > 0 && baseUrl.contains("javax.faces.Token")) {
try {
int beginToken = secureUrl.indexOf("javax.faces.Token");
int endToken = secureUrl.indexOf("&") - 1;
String doubleEncodeToken = secureUrl.substring(beginToken, endToken);
String encodeToken = URLDecoder.decode(doubleEncodeToken, currentResponseEncoding);
secureUrl = secureUrl.replace(doubleEncodeToken, encodeToken);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
return secureUrl;
}
#Override
public String encodeRedirectURL(String baseUrl, Map<String, List<String>> parameters) {
FacesContext context = FacesContext.getCurrentInstance();
String encodingFromContext =
(String) context.getAttributes().get(RIConstants.FACELETS_ENCODING_KEY);
if (null == encodingFromContext) {
encodingFromContext =
(String) context.getViewRoot().getAttributes().get(RIConstants.FACELETS_ENCODING_KEY);
}
String currentResponseEncoding =
(null != encodingFromContext) ? encodingFromContext : getResponseCharacterEncoding();
UrlBuilder builder = new UrlBuilder(baseUrl, currentResponseEncoding);
builder.addParameters(parameters);
String secureUrl = builder.createUrl();
//Handle double encoding
if (parameters.size() > 0 && baseUrl.contains("javax.faces.Token")) {
try {
int beginToken = secureUrl.indexOf("javax.faces.Token");
int endToken = secureUrl.indexOf("&") - 1;
String doubleEncodeToken = secureUrl.substring(beginToken, endToken);
String encodeToken = URLDecoder.decode(doubleEncodeToken, currentResponseEncoding);
secureUrl = secureUrl.replace(doubleEncodeToken, encodeToken);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
return secureUrl;
}
}
You can find a working example in https://github.com/earth001/jsf-protected-view

Managed bean property becomes null in a different request

I am facing a problem when initializing a List with new ArrayList<E>(). This is my class:-
#ManagedBean(name = "clientBean")
#SessionScoped
public class ClientBean {
public String msg = "", input, pendingMsg = "", user = "";
public GossipClient client;
public boolean inputDisabled = true, sendBtnDisabled = true, startBtnDisabled = false;
String key = null;
public List<ClientBeanDto> dtos;
ClientBeanDto dto;
String style;
public List<OnlineList> onlineList;
OnlineList userInfo;
OnlineList newUser = null;
public void start() throws UnknownHostException, IOException {
client = new GossipClient(this);
if (user != null && user != "" && client.connect()) {
try {
BufferedReader reader = new BufferedReader(new FileReader("D://key.txt"));
key = reader.readLine();
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
client.start();
inputDisabled = sendBtnDisabled = false;
startBtnDisabled = true;
msg = pendingMsg = "";
pendingMsg = user + ", you are connected!";
client.sendNick(key + user);
onlineList = new ArrayList<OnlineList>();
dtos = new ArrayList<ClientBeanDto>();
}
.
.
.
}
The problem is with public List<OnlineList> onlineList. In start() method when the line onlineList = new ArrayList<OnlineList>(); is executed, in debugging mode I find its value null whereas a similar object dtos in the very next line gets initialized and assigned a id successfully.
I can't seem to find any reason for this behavior and I am not getting any error/exception. Any wisdom will be appreciated.
UPDATE: Found out that the list is becoming null whenever a button of xhtml (which the bean is backing) is clicked. I cannot find the reason though. The bean is SessionScoped and the other list(dtos) object is retained.

sendRedirect in custom action class

I am overriding one of the actions of the calendar portlet . I want to do some functionality then redirect to another JSP after that. I tried using the sendRedirect function as follows:
1.
String redirect = ParamUtil.getString(actionRequest , "redirect");
actionResponse.sendRedirect(redirect);
2.
actionResponse.sendRedirect ("/calendar/view") ;
3.
ThemeDisplay themeDisplay = (ThemeDisplay) actionRequest.getAttribute(WebKeys.THEME_DISPLAY);
PortletURL url = PortletURLFactoryUtil.create(PortalUtil.getHttpServletRequest(actionRequest), "8", themeDisplay.getPlid(),
PortletRequest.RENDER_PHASE);
url.setParameter("struts_action", "/calendar/view");
actionResponse.sendRedirect (url.toString()) ;
However, none of the methods cause the jsp to redirect to another one.
I am doing so in the processAction function and I am extending the BaseStrutsPortletAction.
Any suggestions how to solve it?
This is my overridden file :
public class TestAction extends BaseStrutsPortletAction {
public void processAction(StrutsPortletAction originalStrutsPortletAction, PortletConfig portletConfig,
ActionRequest actionRequest, ActionResponse actionResponse) throws Exception {
String cmdConstants = ParamUtil.getString(actionRequest, Constants.CMD);
if (cmdConstants.equals(Constants.APPROVE)) {
//some function
actionResponse.setRenderParameter("test", "test");
originalStrutsPortletAction.processAction(portletConfig, actionRequest, actionResponse);
}
}
public String render(StrutsPortletAction originalStrutsPortletAction, PortletConfig portletConfig, RenderRequest renderRequest,
RenderResponse renderResponse) throws Exception {
String cmdConstants = ParamUtil.getString(renderRequest, Constants.CMD);
if (cmdConstants.equals(Constants.APPROVE)) {
if (renderRequest.getParameter("test").equals("test")) {
return "/portlet/calendar/view.jsp";
}
}
return originalStrutsPortletAction.render(null, portletConfig, renderRequest, renderResponse);
}
}

Passing a string from web part to user control

public class ToolPartGetLists : Microsoft.SharePoint.WebPartPages.WebPart, ICommunicationInterface
{
private bool _error = false;
//.........
protected override void CreateChildControls()
{
if (!_error)
{
try
{
ViewState["prodList"] = SelectedList;
//base.CreateChildControls();
Office_Cp = (OfficeCPS)Page.LoadControl(#"/_controltemplates/OfficeCP/OfficeCP.ascx");
this.Controls.Add(Office_Cp);
// Your code here...
//this.Controls.Add(new LiteralControl(this.MyProperty));
}
catch (Exception ex)
{
HandleException(ex);
}
}
}
}
public class OfficeCPS : System.Web.UI.UserControl
{
//I want the value of Selected List here
public string prodDataList = "";
//.......
}
I tried ViewState, not working!!!
Inside the try you could use:
Office_Cp = (OfficeCPS)Page.LoadControl(#"/_controltemplates/OfficeCP/OfficeCP.ascx");
this.Controls.Add(Office_Cp);
Office_Cp.prodDataList = SelectedList;
If this doesn't work pay careful attention to how you are handling the ASP.NET lifecycle.
Also note it would be better practice to hide prodDataList behind a property or method.

Resources