My application has a save and retrieve function. I have the save/retrieve working in that the objects are saved to a database and retrieved correctly. However, in my retrieve landing page, depending on the state of the saved application, I either want to validate some details with the user, or silently navigate to the last accessed view. The latter is where I'm having trouble.
We're using spring beans and in my SaveAndRetrieve page bean I have:
#PostConstruct
public void initialise() {
caseNotFound = false;
caseReference = saveAndRetrieveActionHandler.getRequestedCaseReference();
LOGGER.debug("Retrieve initialise. Case ref is {}", caseReference);
if (caseReference != null) {
try {
saveAndRetrieveActionHandler.retrieveApplicationByCaseRef();
LOGGER.debug("Retrieve initialise - case found");
final NavigationOutcome outcome = saveAndRetrieveActionHandler.getLastAccessedView();
if (outcome.getApplicationState() == ApplicationState.QUOTE) {
LOGGER.info("Quote retrieved, navigating to view");
// HERE IS WHERE THE TROUBLE LIES! THIS DOESNT WORK
FacesUtils.setNextViewNavigation(outcome.getViewId());
}
} catch (final FrameworkException fe) {
LOGGER.debug("Exception caught {}", fe);
caseNotFound = true;
}
}
}
outcome is an enumeration containing amongst other things the view I need to navigate to, and the application state (another enumeration). If applicationState is quote, I want to silently navigate. For all other applicationStates I want to challenge the user to verify them.
My facesUtils method is:
public static void setNextViewNavigation(final String p_lastAccessedViewId) {
if (p_lastAccessedViewId != null) {
getCurrentViewRoot().setViewId(p_lastAccessedViewId);
}
}
I've also tried calling this method
public static void navigateToOutcome(final String p_outcome) {
final FacesContext context = getFacesContext();
final NavigationHandler navigationHandler = context.getApplication().getNavigationHandler();
navigationHandler.handleNavigation(context, null, p_outcome);
}
Despite my efforts, I'm seeing the landing page wheras I want to silently navigate to the saved page
Basically I want to abort the current lifecycle and reset the viewroot to the saved view. (note I am not saving the component tree itself, just my business objects)
One more piece of information, this is jsf1.2, but with facelets. I cannot use any jsf2 specific functionality, nor can I use any third party JSF extenstions.
Help please!
We solved this by using a ui:include tag with the src attribute being a jsf method that determines the name of the page to navigate to.
Related
I have an Xpage application that uses the extension library where the xsp.extlib.convstate is 'null' for one of three users until they manually refresh page. All three users access application via RDP using Citrix and internet options are the same for all three. Trying to figure out why this would be happening. The application is only on one 9.0.1 server.
From the looks of the source code, if there hasn't been a conversationState initialised yet, the conversationState would not be initialised until either:
after the Render Response phase (in the phase listener: com.ibm.xsp.extlib.component.layout.impl.ApplicationPhaseListener)
#SuppressWarnings("unchecked") // $NON-NLS-1$
public void afterPhase(PhaseEvent event) {
if(event.getPhaseId()==PhaseId.RENDER_RESPONSE) {
// After the render phase, we save the conversion state
ConversationState.saveInSession(event.getFacesContext());
}
}
in the setParent method of the UIApplicationLayout, and this seems to be guarded by a 'isRestoringState' condition, which means I don't think this would run on the first view of a page as there wouldn't be any state to restore.
#Override
public void setParent(UIComponent parent) {
super.setParent(parent);
if( null == parent ){ // removing parent
return;
}
// TODO should move this initialization to initBeforeContents instead
FacesContextEx context = (FacesContextEx) getFacesContext();
if(null != context && !context.isRestoringState()) {
ConversationState cs = ConversationState.get(context, FacesUtil.getViewRoot(this), true);
// Initialize the conversation state
// Set the current navigation path to the UserBean
ApplicationConfiguration conf = findConfiguration();
if(conf!=null) {
String navPath = conf.getNavigationPath();
if(StringUtil.isEmpty(navPath)) {
// If there isn't a navigation path that is defined, the use the default one
if(StringUtil.isEmpty(cs.getNavigationPath())) {
navPath = conf.getDefaultNavigationPath();
}
}
if(StringUtil.isNotEmpty(navPath)) {
cs.setNavigationPath(navPath);
}
}
}
}
So this might explain why it wouldn't be initialised until the 2nd page view.
You could try forcing an initialisation of the ConversationState before you try to use it, maybe in beforePageLoad, by calling one of the com.ibm.xsp.extlib.component.layout.ConversationState's get() methods.
Note the boolean parameter tells the method to create the ConversationState if it does not exist.
I don't do much ServerSide Javascript but I guess this works? The sentiment is correct.
#{javascript: com.ibm.xsp.extlib.component.layout.ConversationState.get(facesContext, true); }
If you are doing it in java then:
ConversationState.get(FacesContext.getInstance(), true);
Does this sound like an explanation of why you are seeing your behaviour?
We have a JSF application on WildFly 8 which uses the traditionally mechanism with internationalizing text by having message bundles for German and English in the WEB-INF\classes folder of the WAR and a configuration in faces-config.xml mapping a name to it and listing the locales. The application does not have a database connection, but uses REST services to communicate with a 2nd application.
Now we need to be able to change text more easily, meaning not having to build a new WAR file and do a deployment when changing a text. So I need a mechanism to have the message bundles outside of the WAR while being able to use it as before within the XHTML pages.
Two optional requirements would be to change the text and refresh the messages in the application without having to restart the application (priority 2), and to have a default bundle within the WAR, which is overwritten by the external bundle (priority 3).
My thought was to use something like Apache commons configuration to read a property file within an Application scoped bean and expose a getter under the EL name used before. But somehow it feels like having to re-implement an existing mechanism and that this should somehow be easier, maybe even with Java EE core only.
Has someone used this mechanism in such a way and can point me to some example/description on the details or has a better idea to implement the listed requirement(s)?
How to put JSF message bundle outside of WAR?
Two ways:
Add its path to the runtime classpath of the server.
Create a custom ResourceBundle implementation with a Control.
change the text and refresh the messages in the application without having to restart the application
Changing the text will be trivial. However, refreshing is not trivial. Mojarra internally caches it agressively. This has to be taken into account in case you want to go for way 1. Arjan Tijms has posted a Mojarra specific trick to clear its internal resource bundle cache in this related question: How to reload resource bundle in web application?
If changing the text happens in the webapp itself, then you could simply perform the cache cleanup in the save method. If changing the text however can happen externally, then you'd need to register a file system watch service to listen on changes (tutorial here) and then either for way 1 clear the bundle cache, or for way 2 reload internally in handleGetObject().
have a default bundle within the WAR, which is overwritten by the external bundle
When loading them from classpath, the default behavior is the other way round (resources in WAR have higher classloading precedence), so this definitely scratches way 1 and leaves us with way 2.
Below is a kickoff example of way 2. This assumes that you're using property resource bundles with a base name of text (i.e. no package) and that the external path is located in /var/webapp/i18n.
public class YourBundle extends ResourceBundle {
protected static final Path EXTERNAL_PATH = Paths.get("/var/webapp/i18n");
protected static final String BASE_NAME = "text";
protected static final Control CONTROL = new YourControl();
private static final WatchKey watcher;
static {
try {
watcher = EXTERNAL_PATH.register(FileSystems.getDefault().newWatchService(), StandardWatchEventKinds.ENTRY_MODIFY);
} catch (IOException e) {
throw new ExceptionInInitializerError(e);
}
}
private Path externalResource;
private Properties properties;
public YourBundle() {
Locale locale = FacesContext.getCurrentInstance().getViewRoot().getLocale();
setParent(ResourceBundle.getBundle(BASE_NAME, locale, CONTROL));
}
private YourBundle(Path externalResource, Properties properties) {
this.externalResource = externalResource;
this.properties = properties;
}
#Override
protected Object handleGetObject(String key) {
if (properties != null) {
if (!watcher.pollEvents().isEmpty()) { // TODO: this is naive, you'd better check resource name if you've multiple files in the folder and keep track of others.
synchronized(properties) {
try (InputStream input = new FileInputStream(externalResource.toFile())) {
properties.load(input);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
}
return properties.get(key);
}
return parent.getObject(key);
}
#Override
#SuppressWarnings({ "rawtypes", "unchecked" })
public Enumeration<String> getKeys() {
if (properties != null) {
Set keys = properties.keySet();
return Collections.enumeration(keys);
}
return parent.getKeys();
}
protected static class YourControl extends Control {
#Override
public ResourceBundle newBundle
(String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
throws IllegalAccessException, InstantiationException, IOException
{
String resourceName = toResourceName(toBundleName(baseName, locale), "properties");
Path externalResource = EXTERNAL_PATH.resolve(resourceName);
Properties properties = new Properties();
try (InputStream input = loader.getResourceAsStream(resourceName)) {
properties.load(input); // Default (internal) bundle.
}
try (InputStream input = new FileInputStream(externalResource.toFile())) {
properties.load(input); // External bundle (will overwrite same keys).
}
return new YourBundle(externalResource, properties);
}
}
}
In order to get it to run, register as below in faces-config.xml.
<application>
<resource-bundle>
<base-name>com.example.YourBundle</base-name>
<var>i18n</var>
</resource-bundle>
</application>
I am trying to create a custom UIData component and I am having troubles with Ajax. The first call works fine, but subsequent calls cannot resolve my UIData 'var' attribute. When trying to debug, I can see that the first ajax call restores my custom UIData and puts the 'var' into the RequestMap. Subsequent calls though do not call again restoreState resulting in an empty 'var' variable.
PS. Apologies for this post not being very SSCCE but it would be very large.
The problem was that I was not using
UIComponentBase.restoreAttachedState(context, values[1]);
UIComponentBase.saveAttachedState(context, getValue());
in Save and restore state
public Object saveState(FacesContext context)
public void restoreState(FacesContext context, Object state)
Another issue was that I didn't reset the rowIndex of the UIData
setRowIndex(-1);
in the
public boolean visitTree(VisitContext context, VisitCallback callback)
This causes the id of the saved state to be adjusted with the index resulting to a key miss in the next restore phase.
Although my answer might be interesting for some, it didn't answer how UIData 'var' is resolved. The answer is that in each iteration/phase of UIData processing the setRowIndex(int) method is called which sets the 'var' attribute with the data from the dataModel in the request map (See extract below). This is called by the UIData method invokeOnComponent or UIData.visitTree() which is called by FaceletViewHandlingStrategy.locateComponentByClientId() in JSF 1.2 or by various places in JSF2 including state Management Strategies, ViewContextImpl and many others:
See change in this link under -Tree visiting
http://andyschwartz.wordpress.com/2009/07/31/whats-new-in-jsf-2/
Documentation on visitTree:
http://docs.oracle.com/cd/E17802_01/j2ee/javaee/javaserverfaces/2.0/docs/api/javax/faces/component/UIComponent.html#visitTree(javax.faces.component.visit.VisitContext, javax.faces.component.visit.VisitCallback)
This is the extract from UIData:
String var = (String) getStateHelper().get(PropertyKeys.var);
if (var != null) {
Map<String, Object> requestMap =
getFacesContext().getExternalContext().getRequestMap();
if (rowIndex == -1) {
oldVar = requestMap.remove(var);
} else if (isRowAvailable()) {
requestMap.put(var, getRowData());
} else {
requestMap.remove(var);
if (null != oldVar) {
requestMap.put(var, oldVar);
oldVar = null;
}
}
I am new to JSF and want to create the login part of an app. I have a login page
where I validate logins against a database. That is fine, but I can not figure out the logic
in the following part. A legal user should be redirected to her own profile page and non legal users to a common error page. How do I "transport" the identity from the login to the profile page. All the info I need for a profile page i can get from the database so I kind of want to transport a bean from the login to create a user dependent view. I have looked at tutorials online but could not find examples except for the even simpler example where there is no use of user identity and eg.password and username is simply matched against hard coded values. I think there is some underlying "idea" I don't get because this should ne simple, right ?
Put it in a session scoped managed bean. Here's a basic kickoff example:
#ManagedBean
#SessionScoped
public class ActiveUser {
private User user = new User();
#EJB
private UserService userService;
public String login() {
User found = userService.find(user);
if (found == null) {
setGlobalMessage("Invalid login, try again");
return null;
} else {
user = found;
return "userprofile";
}
}
public void isLoggedIn() {
return user.getId() != null;
}
// ...
}
You can intercept on its presence in a filter.
See also
Prevent accessing restricted page without login in Jsf2
Is there any easy way to preprocess and redirect GET requests?
I have a client who want's a single page design for his site where the content for each "page" is shown/hidden using javascript as the user navigates the site.
I'm not sure on the best way to approach this using Orchard. One option would be to have the content all on a single page content item but then you lose the ability to use the navigation features of Orchard and can't let the client think about administration in terms of pages.
Does anyone have ideas or experiences on how best to set this up in Orchard CMS?
Here's the solution I used based on Bertrand's advice:
public ActionResult Display(int id)
{
var contentItem = _contentManager.Get(id, VersionOptions.Published);
dynamic model = _contentManager.BuildDisplay(contentItem);
var ctx = _workContextAccessor.GetContext();
ctx.Layout.Metadata.Alternates.Add("Layout_Null");
return new ShapeResult(this, model);
}
I created a new module with a controller containing the action method above. The action method takes a parameter for the content part id. The _contentManager and _workContextAccessor objects are being injected into the controller. The Layout.Null.cshtml view was created exactly like Bertrand suggested.
Here's what I would do to achieve that sort of very polished experience without sacrificing SEO, client performance and maintainability: still create the site "classically" as a set of pages, blog posts, etc., with their own URLs. It's the home page layout that should then be different and bring the contents of those other pages using Ajax calls.
One method that I've been using to display the same contents as a regular content item, but from an Ajax call (so without the chrome around the content, without bringing the stylesheet in, as it's already there, etc.) is to have a separate controller action that returns the contents in a "null layout":
var ctx = _workContextAccessor.GetContext();
ctx.Layout.Metadata.Alternates.Add("Layout_Null");
return new ShapeResult(this, shape);
Then, I have a Layout.Null.cshtml file in my views that looks like this:
#{
Model.Metadata.Wrappers.Clear();
}
#Display(Model.Content)
Clearing the wrappers removes the rendering from document.cshtml, and the template itself is only rendering one zone, Content. So what gets rendered is just the contents and nothing else. Ideal to inject from an ajax call.
Does this help?
Following along the lines of Bertrand's solution, would it make more sense to implement this as a FilterProvider/IResultFilter? This way we don't have to handle the content retrieval logic. The example that Bertrand provided doesn't seem to work for List content items.
I've got something like this in my module that seems to work:
public class LayoutFilter : FilterProvider, IResultFilter {
private readonly IWorkContextAccessor _wca;
public LayoutFilter(IWorkContextAccessor wca) {
_wca = wca;
}
public void OnResultExecuting(ResultExecutingContext filterContext) {
var workContext = _wca.GetContext();
var routeValues = filterContext.RouteData.Values;
if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest()) {
workContext.Layout.Metadata.Alternates.Add("Layout_Null");
}
}
public void OnResultExecuted(ResultExecutedContext filterContext) {
}
}
Reusing Rahul's answer with added code to answer #tuanvt's question. I'm honestly not sure what your question is but if seems like you want to access the data sent with the ajax request. If it's JSON you're sending set contentType: "application/json" on the request, JSON.stringify() it , then access it in Rahul's proposed ActionFilter by extracting it from the request stream. Hope it helps in any way.
public class LayoutFilter : FilterProvider, IResultFilter {
private readonly IWorkContextAccessor _wca;
public LayoutFilter(IWorkContextAccessor wca) {
_wca = wca;
}
public void OnResultExecuting(ResultExecutingContext filterContext) {
var workContext = _wca.GetContext();
var routeValues = filterContext.RouteData.Values;
if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest()) {
workContext.Layout.Metadata.Alternates.Add("Layout_Null");
if (filterContext.HttpContext.Request.ContentType.ToLower().Contains("application/json"))
{
var bytes = new byte[filterContext.HttpContext.Request.InputStream.Length];
filterContext.HttpContext.Request.InputStream.Read(bytes, 0, bytes.Length);
filterContext.HttpContext.Request.InputStream.Position = 0;
var json = Encoding.UTF8.GetString(bytes);
var jsonObject = JObject.Parse(json);
// access jsonObject data from ajax request
}
}
}
public void OnResultExecuted(ResultExecutedContext filterContext) {
}
}