My task is to translate all the messages of an application into another language, different than English. The tricky part is with the messages generated by the JSF framework itself. I came across various articles showing how to customize some particular conversion or validation error message, but i am interested in customizing every possible error message that the application might generate (including, for example, authentication and navigation error messages).
Is there a file that contains all the possible error messages?
So far, i came across a file named Messages.properties, located in the jsf-api jar , which contains Validation, Converter and Component error messages. But that is not enough, there is plenty other errors apart from these.
This jar is actually named jboss-jsf-api_2.1_spec-2.1.28.Final-redhat-1.jar and is located under jboss-eap\modules\system\layers\base\javax\faces\api\main folder.
Inside this jar, the Messages.properties file is located under the javax\faces package.
I currently use Red Hat JBoss Enterprise Application Platform - Version 6.4.0.GA, JSF Implementation-Version: 2.1.28.Final-redhat-1
The problem can be split into two parts:
How do i override a framework error message?
What is the list of all possible error messages that can be generated by the JSF framework?
Part 1: OVERRIDING FRAMEWORK'S ERROR MESSAGES
In my project, under WebContent\WEB-INF, there is faces-config.xml, which contains <message-bundle>resources</message-bundle>. 'resources' points to src\resources.properties. In this .properties file, i just have to add the corresponding entries, like
javax.faces.converter.DateTimeConverter.DATE={2}: ''{0}'' non poteva essere inteso come una data.
Should such error be generated by the application, the translated message will be displayed.
However, as i understand from Cannot override validation error message, the use of a resources.properties file under src is maven specific. Different packaging technologies may require different solutions.
Part 2: LIST OF ALL ERROR MESSAGES
Taking a look at the files contained in javax.faces.jar http://www.java2s.com/Code/Jar/j/Downloadjavaxfacesjar.htm , i see the following packages:
javax.faces.application
javax.faces.bean
javax.faces.component
javax.faces.context
javax.faces.convert
javax.faces.el
javax.faces.event
javax.faces.lifecycle
javax.faces.model
javax.faces.render
javax.faces.validator
javax.faces.view
javax.faces.webapp
For every class in each of these packages, i have to add entries in the resources.properties for every error that this class might raise.
But what are the errors that a class might raise? And what should the entry in the resource.properties look like?
I let myself guided by the entries that I've found in the Messages.properties file inside the jboss-jsf-api_2.1_spec-2.1.28.Final-redhat-1.jar (which I've mentioned in the question). It has the following entries, among many others:
# ==============================================================================
# Component Errors
# ==============================================================================
javax.faces.component.UIInput.CONVERSION={0}: Conversion error occurred.
javax.faces.component.UIInput.REQUIRED={0}: Validation Error: Value is required.
javax.faces.component.UIInput.UPDATE={0}: An error occurred when processing your submitted information.
In my IDE (eclipse), i click on an import statement import javax.faces.component.UIInput; to navigate to UIInput.class . In the Class File Editor , i have the following lines :
// Field descriptor #193 Ljava/lang/String;
public static final java.lang.String CONVERSION_MESSAGE_ID = "javax.faces.component.UIInput.CONVERSION";
// Field descriptor #193 Ljava/lang/String;
public static final java.lang.String REQUIRED_MESSAGE_ID = "javax.faces.component.UIInput.REQUIRED";
// Field descriptor #193 Ljava/lang/String;
public static final java.lang.String UPDATE_MESSAGE_ID = "javax.faces.component.UIInput.UPDATE";
I remark that the value of the String field is exactly the key that must be added in the resources.properties, like, for example, javax.faces.component.UIInput.CONVERSION.
For a new class, like, for example, javax.faces.validator.RegexValidator class, I will do the following: open it in class file editor, pick those fields whose name ends in _ID, like :
// Field descriptor #30 Ljava/lang/String;
public static final java.lang.String VALIDATOR_ID = "javax.faces.RegularExpression";
// Field descriptor #30 Ljava/lang/String;
public static final java.lang.String PATTERN_NOT_SET_MESSAGE_ID = "javax.faces.validator.RegexValidator.PATTERN_NOT_SET";
// Field descriptor #30 Ljava/lang/String;
public static final java.lang.String NOT_MATCHED_MESSAGE_ID = "javax.faces.validator.RegexValidator.NOT_MATCHED";
// Field descriptor #30 Ljava/lang/String;
public static final java.lang.String MATCH_EXCEPTION_MESSAGE_ID = "javax.faces.validator.RegexValidator.MATCH_EXCEPTION";
and add the following entries to the resources.properties file:
javax.faces.RegularExpression=custom message
javax.faces.validator.RegexValidator.PATTERN_NOT_SET=custom message
javax.faces.validator.RegexValidator.NOT_MATCHED=custom message
javax.faces.validator.RegexValidator.MATCH_EXCEPTION=custom message
Unfortunately, given the huge number of classes for which error messages must be provided, i don't find this as a feasible solution.
UPDATE
I just realized why only Conversion and Validation error messages should be provided: because they are related to user input, which the programmer cannot control. All the other errors like Navigation related issues must be properly handled by the programmer. Such errors should not appear in the first place, so it does not make sense to translate those messages anyway.
Related
I recently changed my domain objects from LocalDate to ZonedDateTime. I also created a brand new play JHipster application and one play entity choosing ZonedDateTime for two class members. The test application (new) works while my existing application does not, even after going through all the code twice. I loaded CSV data using Liquibase and my listing code shows the dates properly. Here's what the data looks like in my Maven output, e.g. entrydate='2017-02-23T19:53:18-05:00[America/New_York]', transaction='Initial Balance',
When I choose to update the date-time value with the "datetime-picker" in the dialog.html, a string date time is shown in the text box but when I push "Save" I get an "Internal Server Error" and the Maven output shows:
.HttpMessageNotReadableException: Could not read document: Text '2017-02- 26T00:53:18.000Z' could not be parsed at index 23 (through reference chain: org.ciwise.blackhole.domain.GenLedger["entrydate"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Text '2017-02-26T00:53:18.000Z' could not be parsed at index 23 (through reference chain: org.ciwise.blackhole.domain.GenLedger["entrydate"])]
Does anyone have an idea why the picker would produce String text in the text box that isn't acceptable when the HTTP PUT occurs (edit)?
My application does use Service classes but they handle the same Java domain objects as the JPA Repository classes do.
One more thing, the schema for the API shows e.g. "entrydate": "2017-02-23T21:44:04.859Z", but the actual JSON return is "entrydate": "2017-02-23T19:53:18.000-0500",
I'm hoping someone else has encountered this before.
Thanks
David
The answer here was to re-introduce application.yml. Inside this file it defines some Spring profiles but of most importance, it provides an option for Jackson serialization into JSON e.g.
jackson:
serialization.write_dates_as_timestamps: false
This resolved my issue.
I've written a number of Java annotation processors that write some arbitrary data to text files that will be included in my class directory / jar file. I typically use code that looks like this:
final OutputStream out = processingEnv
.getFiler()
.createResource(StandardLocation.CLASS_OUTPUT, "", "myFile")
.openOutputStream();
I'm trying to do something similar in a groovy ASTTransformation. I've tried adding a new source file but that (expectedly) must be valid groovy. How do I write arbitrary resources from an ASTTransformation? Is it even possible?
As part of implementing your ASTTransformation, you need to implement the void visit(ASTNode[] nodes, SourceUnit source) method. In it you can call source.getConfiguration().getTargetDirectory() and it will return your build output directory, e.g. /Users/skissane/my-groovy-project/build/classes/groovy/main). You can then write your resources into there, and whatever is packaging them into the JAR (such as Gradle) should pull them from that.
In my case, I wanted to delay writing the resources until OUTPUT phase – since I was creating META-INF/services files, and I wanted to wait until I'd seen all the annotated classes before writing them, or else I'd be repeatedly adding to them for each annotated class – so I also implemented CompilationUnitAware, and then in my setCompilationUnit method I call unit.addPhaseOperation() and pass it a method reference to run during OUTPUT. Note, if you are using a local ASTTransformation, setCompilationUnit will be called multiple times (each time on a new instance of your transformation class); to avoid adding the phase operation repeatedly, I used a map in a static field to track if I'd seen this CompilationUnit before or not. My addPhaseOperation method is called once per an output class, so I used a boolean field to make sure I only wrote the resource files out once.
Doing this caused a warning to be printed:
> Task :compileGroovy
warning: Implicitly compiled files were not subject to annotation processing.
Use -implicit to specify a policy for implicit compilation.
1 warning
Adding this to build.gradle made the warning go away:
compileGroovy {
options.compilerArgs += ['-implicit:none']
}
The requirements of the application that I'm building demands that user roles are to be dynamic, they will be stored in the database, and they will also be mapped to functionalities (forms) of the application, also stored in the database.
Restricting a role from accessing a specific page won't be difficult, but the requirements also states that form inputs must be customized based on roles, which means, an input can be mandatory or not, visible or not, read-only or not based on the role.
My approach to control these restrictions is based on creating a property file for each role, which will store all the inputs of all the forms in the application, as keys, and a long string as value in which we define the state of the input, like the following:
user-inputs.properties
# form.input=mandatory:visibility
searchBooks.bookName=true:true
searchBooks.bookCategory=false:true
searchBooks.authorName=false:false
admin-inputs.properties
searchBooks.bookName=true:true
searchBooks.bookCategory=false:true
searchBooks.authorName=false:true
And then do some magic Java code, whenever a form is accessed, read its inputs properties from the file of the specific user role, and parse the values so I could provide the right value for the rendered="" and required="" attribute of an <h:inputText/>.
This could be a solution, but the inputs of the application are much more than a book name and category, means I will be putting lots of required and rendered attributes which will make JSF pages look ugly with huge amount of variables in the managed bean.
Is there a better approach/framework/solution to my issue?
I think that you are in the right way, and i will continue using your approach which consists of creating multiple property files, one for each user, except that we will not use a any "huge amount of variables
in the managed bean".
So, the first step consists on managing multiple resource properties using a single resource bundle prefix ( the <var></var> in <resource-bundle>), in the second step we will see how to switch between those files, and in the last step we will read from property file using JSTL.
Managing multiple property files:
We start by defining our ResourceBundle in the faces-config file:
<application>
<resource-bundle>
<base-name>UserMessages</base-name>
<var>msgs</var>
</resource-bundle>
</application>
UserMessages is a ResourceBundle where we will implement the logic that allow us to switch between our property files (assuming that yourpackage.user-inputs is the fully qualified name of your user-inputs.properties):
import java.util.Enumeration;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import javax.faces.context.FacesContext;
public class UserMessages extends ResourceBundle {
public UserMessages() {
// we are loading user-inputs.properties as the default properties file
setParent(getBundle("yourpackage.user-inputs", FacesContext.getCurrentInstance()
.getViewRoot().getLocale()));
}
#Override
protected Object handleGetObject(String key) {
// we could just return parent.getObject(key) but we want to respect JSF recommandations
try {
return parent.getObject(key);
} catch (MissingResourceException e) {
return "???" + key + "???";
}
}
#Override
public Enumeration<String> getKeys() {
return parent.getKeys();
}
// this is the method that will allow us to switch between our .properties
public void setResourceBundle(String basename) {
setParent(getBundle(basename, FacesContext.getCurrentInstance()
.getViewRoot().getLocale()));
}
}
Switching between property files:
In order to switch from a property file to another we will need to use the method setResourceBundle(String basename) that we just declared in our class above, So in the managed bean where you are declaring your business logic and where you are intending to switch files depending on the user's role, you need to inject the bundle, like:
//don't forget adding getters and setters or you end with NullPointerException
#ManagedProperty("#{msgs}")
private UserMessages userMesssages;
Then, to switch to another file (admin-inputs.properties), just use it like this:
//yourpackage.admin-inputs is the fully qualified name
userMesssages.setResourceBundle("yourpackage.admin-inputs");
NB: You can inject the bundle in that way (above) only in request scoped beans, to use it in broader scopes please see: Read i18n variables from properties file in a Bean
Now, as we can switch easily from the user-inputs to the admin-inputs, the last step is the easiest one.
Parsing the property file:
The bad news, is that when using this approach you will need to add rendered="" and required="" attribute to every input you are willing to manage (but don't forget that the good ones was that you will not need to manage variables in managed beans ;) ).
First, you need to add JSTL namespaces declaration on the top of your xhtml file:
xmlns:fn="http://java.sun.com/jsp/jstl/functions"
you can find more about JSTL functions in the javadocs, regarding the function substringAfter:
Returns a subset of a string following a specific substring.
Example:
P.O. Box: ${fn:substringAfter(zip, "-")}
The function substringBefore:
Returns a subset of a string before a specific substring.
Example:
Zip (without P.O. Box): ${fn:substringBefore(zip, "-")}
Second, as the first part of your String represents the required attribute:
//Returns the substring of msgs['searchBooks.authorName'] before the first occurrence of the separator ':'
required="${fn:substringBefore(msgs['searchBooks.authorName'], ':')}"
and the second part:
//Returns the substring of msgs['searchBooks.authorName'] after the first occurrence of the separator ':'.
rendered="${fn:substringAfter(msgs['searchBooks.authorName'], ':')}"
See also:
JSF Internationalization f:loadbundle or through faces-config:
Performance point
Difference between by Application#getResourceBundle() and ResourceBundle#getBundle() in JSF 2.0
How to remove the surrounding ??? when message is not found in
bundle
Context Sensitive Resource Bundle entries in JavaServer Faces
applications – going beyond plain language, region & variant
locales
I've got a legacy logging class. Its got a static Logger reference (named logger) and a bunch of static methods.
Each method takes a String input and writes it to System.out.println and to logger if logger is not null.
Its got a constructor that initializes logger. But this constructor only has package scope and I'm pretty sure its not being called anywhere. Therefore logger is always null and the class essentially only ever executes System.out.println
I want to change this so it can be used in a multi threaded application where each thread writes to its own unique FileAppender.
And that's where I'm stuck.
Basically, what I want to do is have this static class associated with a bunch of different log4j FileAppenders. Each FileAppender can be created by the Thread, and the file name can be derived from unique information known to the Thread.
What I can't figure out how to do is magically use Log4j to communicate that Thread's unique FileAppender to this legacy logging class.
Ideas? Hints? Suggestions?
Mark
It is possible to change the target log file name dynamically using a RoutingAppender and the ThreadContext map.
This can all be done with configuration (no need for custom code where threads create FileAppenders). In the RoutingAppender configuration you can specify a ThreadContext key you want to use to switch the target FileAppender. For example, you can use the string "ROUTINGKEY". Each thread puts a unique value in the ThreadContext map for key "ROUTINGKEY", and this value is used to select the Appender that the log event is routed to. You can even set it up to dynamically create log files that have the ROUTINGKEY value in the file name, so not all target log files need to be known in advance.
The FAQ page has a good example: http://logging.apache.org/log4j/2.x/faq.html#separate_log_files
I have just started with Eclipse RCP.
I created Eclipse RCP View with TableViewer and WritableList to get data from other thread.
But I cannot see any changes. I need only to show content of List that other thread is managing.
public class View extends ViewPart {
private TableViewer viewer;
private WritableList input;
I also can get error,
org.eclipse.core.runtime.AssertionFailedException: assertion failed: Getter called outside realm of observable org.eclipse.core.databinding.observable.list.WritableList
I know what is UI Thread. I just don't know how to write. Please help with example.
UPDATE. Was not solved, because of lack of time, and missing good and focused tutorial.
I received this error message also with my code.
Databinding observables (WritableList, WritableValue...) inherit from ChangeManager, which provides ChangeManager#getRealm and the realm has Realm#exec. Within the runnable provided to exec, the operation runs in the correct thread.
This line caused the error (Getter called outside realm of observable):
WritableValue value = getEditor().getWritableValue();
System.out.println(((RcpEditorModel) value.getValue()).getNumber());
And this prevented the exception:
WritableValue value = getEditor().getWritableValue();
value.getRealm().exec(() -> System.out.println(((RcpEditorModel) value.getValue()).getNumber()));
The same will work with WritableList since it also inherits from ChangeManager.