{"id":1604,"date":"2011-01-11T09:18:04","date_gmt":"2011-01-11T14:18:04","guid":{"rendered":"http:\/\/www.andygibson.net\/blog\/?p=1604"},"modified":"2011-01-11T11:24:28","modified_gmt":"2011-01-11T16:24:28","slug":"handling-missing-resources-with-cdi-events","status":"publish","type":"post","link":"https:\/\/www.andygibson.net\/blog\/article\/handling-missing-resources-with-cdi-events\/","title":{"rendered":"Handling missing resources with CDI Events"},"content":{"rendered":"<p>In <a href=\"http:\/\/www.andygibson.net\/blog\/article\/resource-bundles-in-jsf-2-0-applications\/\">part 1<\/a>, we created a simple application that made use of string resource bundles in JSF and in <a href=\"http:\/\/www.andygibson.net\/blog\/article\/injecting-string-resource-services-with-cdi\/\">part 2<\/a>  we extended it by using CDI to inject the resource provider into beans so we can re-use our code for accessing locale specific string based resources.<br \/>\n<!--more--><br \/>\nOne benefit of using CDI to inject your beans instead of just creating them manually is the ability to utilize the event mechanism within those beans. In this example, we are going to fire an event when we discover a resource is missing. <\/p>\n<ol>\n<li>Start by creating a <code>MissingResourceInfo<\/code> class that stores the info regarding the missing resource (key and locale). This will be passed along with the event so the observer has all the info when it receives the event.\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic class MissingResourceInfo {\r\n\r\n\tprivate final String key;\r\n\tprivate final String locale;\r\n\r\n\tpublic MissingResourceInfo(String key, Locale locale) {\r\n\t\tthis(key, locale.getDisplayName());\r\n\t}\r\n\r\n\tpublic MissingResourceInfo(String key, String locale) {\r\n\t\tsuper();\r\n\t\tthis.key = key;\r\n\t\tthis.locale = locale;\r\n\t}\r\n\r\n\tpublic String getKey() {\r\n\t\treturn key;\r\n\t}\r\n\r\n\tpublic String getLocale() {\r\n\t\treturn locale;\r\n\t}\r\n}\r\n<\/pre>\n<\/li>\n<li>We fire an event when we discover a resource key is missing from the locale specific properties file being used. In the <code>MessageProvider<\/code> class from part 2, we inject the <code>Event<\/code> instance in to a field and modify the <code>getValue<\/code> method to fire off the event when a key is missing.\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n@RequestScoped\r\npublic class MessageProvider {\r\n\r\n\t@Inject\r\n\tprivate Event&lt;MissingResourceInfo&gt; event;\r\n\t....\r\n\t....\r\n\tpublic String getValue(String key) {\r\n\t\tString result = null;\r\n\t\ttry {\r\n\t\t\tresult = getBundle().getString(key);\r\n\t\t} catch (MissingResourceException e) {\t\t\t\r\n\t\t\tresult = &quot;???&quot; + key + &quot;??? not found&quot;;\r\n\t\t\tevent.fire(new MissingResourceInfo(key, getBundle().getLocale()));\r\n\t\t}\r\n\t\treturn result;\r\n\t}\r\n}\r\n<\/pre>\n<\/li>\n<li>Finally, we want to add an observer for that event that handles it when it is fired. To do this, we will add a new class for this purpose.\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic class MissingResourceObserver {\r\n\r\n\tpublic void observeMissingResources(@Observes MissingResourceInfo info) {\r\n\t\tString template = &quot;Oh noes! %s key is missing in locale %s&quot;;\r\n\t\tString msg = String.format(template, info.getKey(), info.getLocale());\r\n\t\tSystem.out.println(msg);\r\n\t}\r\n\r\n}\r\n<\/pre>\n<\/li>\n<li>Finally, to see it in action, we need to add bean getter to fetch a non-existing value from the bundle.\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n@Named\r\n@RequestScoped\r\npublic class AnotherBean {\r\n\r\n\tpublic String getMissingResource() {\r\n\t\treturn provider.getValue(&quot;ImNotHere&quot;);\r\n\t}\r\n}\r\n<\/pre>\n<\/li>\n<li>We can see this in action by adding to our JSF page a call to this property\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n#{anotherBean.missingResource}\r\n<\/pre>\n<p>Results in the following being displayed in the console :<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nOh noes! 'ImNotHere' key is missing from locale English (United States)\r\n<\/pre>\n<\/li>\n<\/ol>\n<p>Remember, you can have as many event observers as you want so you can have one that just logs it to the console or one that emails if missing text strings are urgent (i.e. production). You could have it write the missing values to the database so during development cycles, the missing strings can be exported and put into the properties files. This is especially useful if you have third parties handling the translation where you can send them a list of missing items to translate in one big batch.<\/p>\n<h1>What about EL?<\/h1>\n<p>This works great when we are accessing resource bundles from Java, but what about when we are using EL expressions to access the values. JSF channels those expressions straight to the resource bundle without the ability to intercept them. One way to fix this would be to write our own EL expression resolver and see if the root of the expression maps to a resource bundle and if so try to obtain the value from the bundle and fire the event if the item is not found. This would work, but it seems overkill since every EL expression  would end up going through the resolver.<br \/>\nRemember that the syntax for accessing resource strings in EL is <code>#{bundlename.key}<\/code>, and Java <code>Map<\/code> classes can also be accessed using the same syntax. What we can do is create a named bean that (barely) implements the map interface and in the <code>get<\/code> method, it gets the value from the injected resource bundle. This means EL expressions will use our Map bean and we can delegate the call to the Message provider which will handle missing expressions.<\/p>\n<ol>\n<li>Start by creating a new bean that implements the <code>Map<\/code> interface. Most of the methods will throw exceptions that they are not implemented, we only really care about the method to get values.\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n@Named(&quot;messages&quot;)\r\n@RequestScoped\r\npublic class ResourceMap implements Map&lt;String, String&gt; {\r\n\r\n\t@Inject\r\n\tprivate MessageProvider provider;\r\n\r\n\t@Override\r\n\tpublic int size() {\r\n\t\treturn 0;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean isEmpty() {\r\n\t\treturn false;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean containsKey(Object key) {\r\n\t\treturn provider.getBundle().containsKey((String) key);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean containsValue(Object value) {\r\n\t\tthrow new RuntimeException(&quot;Not implemented&quot;);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String get(Object key) {\r\n\t\treturn provider.getValue((String) key);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String put(String key, String value) {\r\n\t\tthrow new RuntimeException(&quot;Not implemented&quot;);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String remove(Object key) {\r\n\t\tthrow new RuntimeException(&quot;Not implemented&quot;);\r\n\t}\r\n\r\n       ....\r\n       ....\r\n       ....\r\n}\r\n<\/pre>\n<p>If the interface method isn&#8217;t listed, assume it throws a &#8220;not implemented&#8221; exception. We inject the <code>MessageProvider<\/code> instance so we can re-use our message provider bean to fetch messages and fire the events. <\/li>\n<li>\nWe gave the bean the name <code>messages<\/code>  so if we go to our web page and add the following to our page :<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\nFirst name from map = #{messages.firstName}&lt;br\/&gt;\r\nMissing name from map = #{messages.missingValue}&lt;br\/&gt;\r\n<\/pre>\n<p>When we run our application and refresh the page, we get the value displayed for <code>messages.firstName<\/code>, and a missing value for <code>messages.missingValue<\/code>. In our server log we get <\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nOh noes! missingValue key is missing in locale English (United States)\r\n<\/pre>\n<p>Since we re-used the <code>MessageProvider<\/code> bean, it fires the event when it cannot find a key value so even though we called it from the JSF page, it ended up being routed through to our message provider and the event being fired.\n<\/li>\n<\/ol>\n<p>How you use this is up to you, but since the syntax is the same whether you use the Map or the JSF resource variable, you can switch between the two depending on whether it is a development or production environment. You may also use different event handlers depending on the deployment environment. Alternatively, you may be worried about performance in production, or the fact that you lose autocompletion if you use the map in development.<br \/>\nTo switch implementations, just give either the JSF resource variable, in <code>faces-config<\/code> or the <code>MessageProvider<\/code> bean the name used for your resource string component. You can switch components without having to change either your code or your JSF pages.<\/p>\n<p><a href='http:\/\/www.andygibson.net\/blog\/wp-content\/uploads\/2010\/10\/resourceEventDemo.zip'>Download the Maven source code<\/a> for this project and run it locally by typing unzipping it and running <code>mvn clean jetty:run<\/code> in the command line, and going to <a href=\"http:\/\/localhost:8080\/resourcedemo\/\">http:\/\/localhost:8080\/resourcedemo\/<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In part 1, we created a simple application that made use of string resource bundles in JSF and in part 2 we extended it by using CDI to inject the resource provider into beans so we can re-use our code for accessing locale specific string based resources.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0},"categories":[63],"tags":[49,72,32,68],"_links":{"self":[{"href":"https:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/posts\/1604"}],"collection":[{"href":"https:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/comments?post=1604"}],"version-history":[{"count":7,"href":"https:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/posts\/1604\/revisions"}],"predecessor-version":[{"id":1684,"href":"https:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/posts\/1604\/revisions\/1684"}],"wp:attachment":[{"href":"https:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/media?parent=1604"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/categories?post=1604"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/tags?post=1604"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}