I want to define the date pattern in my application on a global level. Therefor I would like to use a properties file. Since I am already using one e.g. to set the default number of rows for a repeat control I added a ne wkey-value pair:
date_format_date_only=yyyy-MM-dd
In my application I have set the date format via convertors e.g.:
<xp:this.converter>
<xp:convertDateTime pattern="yyyy-MM-dd" type="date" />
</xp:this.converter>
But when I set it to
<xp:this.converter>
<xp:convertDateTime pattern="${application.date_format_date_only}" />
</xp:this.converter>
It is not working. The date is displayed as Feb 26, 2018. Application is the variable I have set via a Theme design element:
<resources>
<bundle src="/application.properties" var="application" />
</resources>
Am I overlooking something?
I notice that when I add the bundle directly to the XPage the pattern works but what is the use of a Theme design element then?
I do certain things through theme definition but not all. There are some restrictions that apply - read timing on theme properties evaluations.
I think your need would be the one of avoiding declaring the resource bundle in every page you use. Am I right?
In that case my advice is to tap into JSF mechanics more.
I personally set up a request scoped bean that I call MessageBean:
public class MessageBean implements Serializable {
private static final long serialVersionUID = 1L;
private ApplicationEx application;
private Locale locale;
private ResourceBundle app;
private ResourceBundle error;
private ResourceBundle log;
public void setApplication(Application application) {
this.application = (ApplicationEx) application;
}
public void setLanguage(String language) {
locale = new Locale(language);
}
public ResourceBundle getApp() {
if (app == null) {
app = getResourceBundle("app");
}
return app;
}
public ResourceBundle getError() {
if (error == null) {
error = getResourceBundle("error");
}
return error;
}
public ResourceBundle getLog() {
if (log == null) {
log = getResourceBundle("log");
}
return log;
}
private ResourceBundle getResourceBundle(String name) {
try {
return application.getResourceBundle("/WEB-INF/i18n/" + name + ".properties", locale);
} catch (IOException e) {
throw new FacesException(e);
}
}
}
In the faces-config.xml:
<managed-bean>
<managed-bean-name>msg</managed-bean-name>
<managed-bean-class>mypackage.MessageBean
</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>application</property-name>
<value>#{facesContext.application}</value>
</managed-property>
<managed-property>
<property-name>language</property-name>
<value>#{facesContext.viewRoot.locale.language}</value>
</managed-property>
</managed-bean>
What this bean does for me is to wire up different property files - these are located under /WEB-INF/i18n/ - (which I split according to their domain of competence 'app' = general app messages, 'error' = error messages etc), and arrange them nicely under one single root: msg.
In other words I can declare anywhere in the page ${msg.app.hello} for the app resource bundle or ${msg.error.sorry} for the error resource bundle.
I don't have to declare any resource on the page, that's the magic of the beans. You don't use them, they are not created. You want to use them, the framework creates them and gives them to you automatically.
I notice that when I add the bundle directly to the XPage the pattern works but what is the use of a Theme design element then?
I believe the intention behind themes was to "define the look and feel of an application" (source) and I think this is reflected in the fact that resource bundles defined on a theme file only become available to an XPage during the beforeRenderResponse phase. So, as you've discovered, any properties that you want to access using a bundle declared on a theme can only be accessed via run time bindings and not on Page Load - and as Converters can't use run time bindings...
A couple of quick and simple SSJS workarounds to avoid adding the bundle directly to each XPage you build are:
Put the property in xsp.properties and access it using context.getProperty('foo') or, if you fall on the side of the debate that prefers not to add your own key-value pairs to xsp.properties
use context.bundle("application.properties").getString("foo") instead.
#shillem's answer about writing it all in Java and using a bean is probably more elegant and better practice - especially if you're using a controller framework in your applications - but the above will work.
Related
I have a web application that uses optional modules. The modules are implemented as Web Fragment projects, their jars may or may not be deployed with the war depending on the build profile.
A module can contain it's own module.taglib.xml with a http://company.com/module namespace and some tags.
The war xhtml templates use module tags like this:
<ui:composition ... xmlns:mod="http://company.com/module">
<c:if test="#{moduleDeployed}">
<mod:someTag />
</c:if>
Problems.
When the module is not deployed, the war pages work fine, but in ProjectStage.Development I get FacesMessage warnings:
Warning: This page calls for XML namespace
http://company.com/module declared with prefix mod but no
taglibrary exists for that namespace.
As far as I can see, JSF specification doesn't define what happens, when a template uses a nonexistent tag library. So with my current approach war pages can stop working after an upgrade or a switch to a different JSF implementation.
Questions.
Is there a (not very ugly) way to disable this specific warning?
Is there a better approach to using optional facelet tag libraries?
As of now I plan to disable the warning anyway I can: e.g. override Messages renderer and check message string if I have to. If the problem 2 manifests, make the build supply placeholder taglib.xml files for not deployed modules.
Even though placeholder taglibs seemed like a pretty good solution, they also seemed harder to implement and maintain.
So in the end I went with filtering the messages. This is likely Mojarra specific: the message text, the fact that the iterator allows removal (this isn't forbidden by the spec, but it's not required either). It's known to work with Mojarra 2.2.8 to 2.2.13.
public class SuppressNoTaglibraryExistsFacesMessage implements SystemEventListener {
private static final Pattern PTTRN_NO_TAGLIBRARY_EXISTS_FOR_NAMESPACE =
Pattern.compile("Warning: This page calls for XML namespace \\S+ declared with "
+ "prefix \\S+ but no taglibrary exists for that namespace.");
#Override
public void processEvent(SystemEvent event) {
Iterator<FacesMessage> messages = FacesContext.getCurrentInstance().getMessages();
while (messages.hasNext()) {
String messageSummary = messages.next().getSummary();
if (PTTRN_NO_TAGLIBRARY_EXISTS_FOR_NAMESPACE.matcher(messageSummary).matches()) {
messages.remove();
}
}
}
#Override
public boolean isListenerForSource(Object source) {
return true;
}
}
Bind the listener only in Development project stage.
public class SubscribeListenersAfterApplicationPostConstructListener
implements SystemEventListener {
#Override
public void processEvent(SystemEvent event) throws AbortProcessingException {
Application application = (Application) event.getSource();
if (ProjectStage.Development.equals(application.getProjectStage())) {
application.subscribeToEvent(PostAddToViewEvent.class, UIViewRoot.class,
new SuppressNoTaglibraryExistsFacesMessage());
}
}
#Override
public boolean isListenerForSource(Object source) {
return source instanceof Application;
}
}
And in faces-config.xml:
<system-event-listener>
<system-event-listener-class><packages>.SubscribeListenersAfterApplicationPostConstructListener</system-event-listener-class>
<system-event-class>javax.faces.event.PostConstructApplicationEvent</system-event-class>
</system-event-listener>
I have a navigation managed bean for each user.
and I need it to initialize first before any other bean because a value is required from the bean.
May I know how do I perform that?
I have tried eager="true" but it doesn't work.
any quick and easy solution via faceconfig would be greatly appreciated.
Just perform the desired initialization job in bean's #PostConstruct.
#PostConstruct
public void init() {
// Here.
}
It'll be invoked when the bean is injected/referenced from another bean for the first time.
The eager=true works only on application scoped beans.
From what I see you should reference the other bean. Let's assume a have a utility class that can pull a bean from the context.
Basically ->
//Bean A
public void doSomething()
{
String required = Utility.getBeanB().getRequiredValue();
use(required);
}
...
//Bean B
public String getRequiredValue()
{
return "Hi, I'm a required value";
}
I have several large web apps that have a "Session Bean" that stores stuff like user preferences, shared objects etc... and this method works perfectly. By using a reference to the bean you eliminate the need to chain the initialization. That method will always DEPEND on the method in the other bean, thus guaranteeing the order of initialization.
There's a variety of ways to access the bean but I usually go through the EL route ->
Get JSF managed bean by name in any Servlet related class
Best of luck, I try to stay "functionally pure" when I can--and I hope that get's a laugh considering the language!
Here's some cool hax for ya, in case other solutions aren't working for you due to various circumstances...
Let's say I have a class Alpha that I want initialized first:
public class Alpha {
#PostConstruct
public void init() {
}
}
I can put the following method in Alpha:
#ManagedBean(name = "Alpha", eager = true)
public class Alpha {
public static void requireAlpha() {
FacesContext context = FacesContext.getCurrentInstance();
Object alpha = context.getApplication().evaluateExpressionGet(context, "#{Alpha}", Object.class);
System.out.println("Alpha required: " + alpha.toString());
}
#PostConstruct
public void init() {
}
}
Then, in any classes that are initializing too early, simply call:
Alpha.requireAlpha();
// ...
// (Code that requires Alpha to be initialized first.)
And if there's a ChildAlpha class that extends Alpha that you really want to be initialized (rather than the parent), make sure to use "ChildAlpha" instead, in both the name="" and the EL Expression ("#{}").
See here for more infos: Get JSF managed bean by name in any Servlet related class
In my app, user should be able to switch the locale (the language used to render text on pages). Tons of tutorials are using FacesContext.getCurrentInstance().getViewRoot().setLocale(). For example: http://www.mkyong.com/jsf2/jsf-2-internationalization-example/. But, that simply doesn't work in JSF 2.0 (it did work in 1.2). The language never switches. No errors or anything. The same code worked fine in JSF 1.2.
What is the correct and definitive approach? I have cobbled together a solution, but not sure if this is the correct one. This works fine. The language switches after user clicks on English or French. Here is code snippet to give you some idea.
#ManagedBean(name = "switcher")
#SessionScoped
public class LanguageSwitcher {
Locale locale = FacesContext.getCurrentInstance().getViewRoot().getLocale();
public String switchLocale(String lang) {
locale = new Locale(lang);
return FacesContext.getCurrentInstance().getViewRoot().getViewId() +
"?faces-redirect=true";
}
//getLocale() etc. omitted for brevity
}
The XHTML:
<f:view locale="#{switcher.locale}">
<h:outputText value="#{msg.greeting}" />
<h:commandLink value="English" action="#{switcher.switchLocale('en')}" />
<h:commandLink value="French" action="#{switcher.switchLocale('fr')}" />
</f:view>
Just to give you more info, here is the config file.
<application>
<locale-config>
<supported-locale>en</supported-locale>
<supported-locale>fr</supported-locale>
</locale-config>
<resource-bundle>
<base-name>com.resources.Messages</base-name>
<var>msg</var>
</resource-bundle>
</application>
Once again, this works. But, I haven't changed the locale of JSF itself by calling any API in any way. This gives me somewhat of a creepy feeling. Is this the correct way to change user's locale?
OK, at the risk of answering my own question, I will like to summarize all the different approaches that I have found.
The basic approach is what I am already doing. That is, have a managed bean in session scope that returns the Locale of the user. This locale needs to be used in every XHTML using <f:view locale="...">. I learned this technique from a post by BalusC, so thanks are due there.
Now, the concern is the use of the f:view element. This needs to be repeated in every page, a potential source of defect if omitted by mistake. I have found a couple of ways of solving this problem.
Approach #1: Create a Facelet template and add the f:view element there. Individual template user pages don't have to worry about adding this element.
Approach #2 uses a phase listener. #meriton has posted the solution here. Thank you for that.
Approach #3 uses a custom view handler that extends MultiViewHandler and returns user's locale from the calculateLocale() method. This is described in the book Beginning JSF 2 APIs and JBoss Seam By: Kent Ka Iok Tong. Here is a slightly altered example from the book:
public class MyViewHandler extends MultiViewHandler {
public Locale calculateLocale(FacesContext context) {
HttpSession session = (HttpSession) context.getExternalContext()
.getSession(false);
if (session != null) {
//Return the locale saved by the managed bean earlier
Locale locale = (Locale) session.getAttribute("locale");
if (locale != null) {
return locale;
}
}
return super.calculateLocale(context);
}
}
Then register it in faces config.
<application>
<view-handler>com.package.MyViewHandler</view-handler>
<!-- Other stuff ... -->
</application>
This is somewhat more elegant than the phase listener. Unfortunately, MultiViewHandler is an internal non-API class from the com.sun.faces.application.view package. That incurs some risk going forward.
With either approach #2 or #3, there is no need for the f:view element in the pages.
One can use custom view handler that extends javax.faces.application.ViewHandlerWrapper and returns user's locale from the calculateLocale() method.
This is definitely better than extending MultiViewHandler from the proprietary SUN package com.sun.faces.application.view, no matter what is described in the book Beginning JSF 2 APIs mentioned in your suggestion. Apart from that, your original approach is absolutely OK:
public class MyViewHandler extends ViewHandlerWrapper {
public Locale calculateLocale(FacesContext context) {
HttpSession session = (HttpSession) context.getExternalContext()
.getSession(false);
if (session != null) {
//Return the locale saved by the managed bean earlier
Locale locale = (Locale) session.getAttribute("locale");
if (locale != null) {
return locale;
}
}
return super.calculateLocale(context);
}
}
Then register it in faces config.
<application>
<view-handler>com.package.MyViewHandler</view-handler>
<!-- Other stuff ... -->
</application>
I had a related problem recently. In my case, the JSF implementation forgot the view locale set by UIViewRoot.setLocale() after navigating to a different view. I rather consider this a bug in the JSF impl, but I didn't have time to make sure.
I didn't particularly like the <f:view> approach, as that tag has been obsoleted by facelets - except for keeping the locale, it seems. This made my leary of including it in a Facelets template. I therefore wrote the following PhaseListener:
/**
* PhaseListener that keeps the current view locale in the session while no request is being processed, to work around
* bugs where JSF forgets the changed locale.
*/
public class SaveViewLocaleToSessionPhaseListener implements PhaseListener {
private static final String key = "locale";
#Override
public PhaseId getPhaseId() {
return PhaseId.ANY_PHASE;
}
#Override
public void beforePhase(PhaseEvent event) {
// do nothing
}
#Override
public void afterPhase(PhaseEvent event) {
PhaseId currentPhase = event.getPhaseId();
if (currentPhase == PhaseId.RESTORE_VIEW) {
viewRoot().setLocale((Locale) sessionMap().get(key));
} else if (currentPhase == PhaseId.RENDER_RESPONSE) {
sessionMap().put(key, viewRoot().getLocale());
}
}
private Map<String, Object> sessionMap() {
return FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
}
private UIViewRoot viewRoot() {
return FacesContext.getCurrentInstance().getViewRoot();
}
}
However, I can not offer any solid evidence that this is really better than simply using <f:view>.
But, I haven't changed the locale of JSF itself in any way.
Sure you did: The <f:view> tag reads the locale from the value expression, and passes it to UIViewRoot.setLocale().
I know of two ways of creating custom JSF components:
1. Native JSF way: creating JSF component class, tag, etc.
2. Facelets way: defining component in a xhtml file and then creating appropriate decrption in facelets taglib.
Currently I work on a project in which introducing facelets is unfortunately out of the question. On the other hand, creating custom components the standard JSF way seems like a pain in the ass.
Is there maybe a third party library that allows creating custom components in the way similar to facelets but doesn't entail the need of using non-standard renderer?
You can do a limited amount of templating using (for example) jsp:include and f:subview.
Alternatively, you can extend a UIComponent overriding selected methods and then provide it via an existing tag and a managed bean using the binding attribute. This still requires a reasonably detailed understanding of component development (and the consequences of this choice), but could cut down the number of files/volume of code significantly.
This approach is a bit of a hack, but might be OK for short-term stuff. You wouldn't do it for component libraries you want to distribute or components requiring long term maintenance.
The new component:
public class QuickComponent extends HtmlOutputText {
#Override public void encodeAll(FacesContext context) throws IOException {
ResponseWriter writer = context.getResponseWriter();
writer.writeText("I'm not really a HtmlOutputText", null);
for (UIComponent kid : getChildren()) {
if (kid instanceof UIParameter) {
UIParameter param = (UIParameter) kid;
writer.startElement("br", this);
writer.endElement("br");
writer.writeText(param.getName() + "=" + param.getValue(), null);
}
}
}
}
The bean providing an instance:
/**Request-scope managed bean defined in faces-config.xml*/
public class QuickComponentProviderBean {
private QuickComponent quick;
public void setQuick(QuickComponent quick) {
this.quick = quick;
}
public QuickComponent getQuick() {
if (quick == null) {
quick = new QuickComponent();
}
return quick;
}
}
Note: don't reuse a single bean property for multiple tags in your views, or they'll reference the same object instance.
Adding the new component to the view:
<h:outputText binding="#{quickComponentProviderBean.quick}">
<f:param name="Hello" value="World" />
</h:outputText>
Note: the attributes that can be defined have not changed. They're fixed by the TLD.
From the jsf 1.2 revB mrel2 spec: bottom of page 65
■ It must be possible for the application to programmatically modify the component tree at any time during the request processing lifecycle (except during the rendering of the view) and have the system behave as expected. For example, the following must be permitted. Modification of the view during rendering may lead to undefined results. It must be possible to allow components added by the templating system (such as JSP) to be removed from the tree before rendering. It must be possible to programmatically add components to the tree and have them render in the proper place in the hierarchy. It must be possible to re-order components in the tree before rendering. These manipulations do require that any components added to the tree have ids that are unique within the scope of the closest parent NamingContainer component. The value of the rendersChildren property is handled as expected, and may be either true or false.
So how does one do this in adf 11g? I'm trying to implement an application wide authorization system where components are visible/editable based on a user's roles. However, I cannot find a way to hook into adf to modify components (eg RichInputText.setDisabled(true)) before the response is written out. I've tried with both PhaseListeners and ViewHandlers. Neither of these seem to allow me to perform the above mentioned functionality. So what gives? Am I out of luck? Am I missing something?
Thanks,
Ben
public class AuthorizationPhaseListener implements PhaseListener {
...
public PhaseId getPhaseId() {
return PhaseId.RENDER_RESPONSE; // I've also tried in the other phases including ALL_PHASES
}
public void beforePhase(PhaseEvent p1) {
// relevant ui components don't yet exist
...
}
public void afterPhase(PhaseEvent p1) {
// relevant ui components exist, but have already been written to the stream, thus it's too late to modify them
...
}
...
}
public class MyCustomViewHandler extends ViewHandlerWrapper {
...
#Override
public void renderView(FacesContext context, UIViewRoot viewToRender) throws IOException {
AuthorizationService as = (AuthorizationService)RiscsContext.getCurrentInstance().getBean("AuthorizationService");
// relevant ui components don't yet exist
as.applyAuthorization();
super.renderView(context, viewToRender);
// relevant ui components exist, but have already been written to the stream, thus it's too late to modify them
as.applyAuthorization();
}
...
}
You really need to do this at the presentation side. Do not do it using a phaselistener, there it is not for. Make use of the rendered attribute wisely. Here are some basic examples how you can make use of it:
<h:someComponent rendered="#{bean.booleanValue}" />
<h:someComponent rendered="#{bean.intValue > 10}" />
<h:someComponent rendered="#{bean.objectValue == null}" />
<h:someComponent rendered="#{bean.stringValue != 'someValue'}" />
<h:someComponent rendered="#{!empty bean.collectionValue}" />
<h:someComponent rendered="#{!bean.booleanValue && bean.intValue != 0}" />
<h:someComponent rendered="#{bean.stringValue == 'oneValue' || bean.stringValue == 'anotherValue'}" />
According to my (limited) searching through the docs, the UIViewRoot object is the root node of the view tree. You can use it's getChildren methods to find the appropriate UIComponents and make your modifications. However, I would suggest a different way.
If you expose your Authorization service as a bean, you can directly add the methods to the markup. For example...
public class User {
...
Map<String, Boolean> allowedRoles;
...
public Map<String, Boolean> getAllowedRoles { return allowedRoles; }
}
<h:inputText value="#{SomethingImportant}" disabled="!#{User.allowedRoles['importantRole']}/>
Even better would be using a security framework which would simplify this even more.
It does allow to access/modify the UI Components in tree if it is locatable. You need to provide Client Component Id while using findComponent(). The only problem remains that it does not give you access/constrol for Initial Page load( restore_view ). :-(
For that as of now only 1 way i could find that is specifying EL in jspx/jsp/jsff.