I have an application with a well defined interface. It uses CDI for resolution of the modules, (Specifically it uses Instance<> injection points on API interfaces to resolve modules) and passes various data back and fourth via the interfaces without issue. I've intentionally kept the API and implementation separate, and the modules only inherit from the API to avoid tight coupling, and the application only knows of the modules through runtime dependancies, and data passing accomplished via the APIs. The application runs fine without the modules, which can be added simply by dropping the jar into the WEB-INF/lib folder and restarting the app server.
Where I'm running into issues is that I want the modules to create a portion of the view, and I therefor want to invoke, in a portable way, either a JSF component, or do an include from the module in order to have it render its view. I already have resolved what module I want to invoke, and have references to the module's interface ready. The way I initially thought to do this was to do a ui:include that asks the module to supply where it's view template is, but I have no idea how to answer that query in a meaningful way, as view resolution is done from the application root, not the library root.
The executive summary is that I have no idea how to jump the gap from Application to Library using JSF for .xhtml (template/component) files.
Using a CC would be nice, but how do I specify that I want a particular CC instance at runtime, instead of having that hard coded into the page?
I can of course invoke the application code directly and ask it for markup, but this seems really brute force, and once I have the markup, I'm not sure exactly how to tell JSF to evaluate it. That said, I can imagine a component that would take the resource path, grab the markup and evaluate it, returning the completed markup, I just don't know how to implement that.
I'd rather avoid forcing module developers to go the heavy duty UIComponent approach if possible, which means either a dynamic way of doing ui:include (or some equivalent) or a dynamic way of invoking CCs. (I don't mind coding the UIComponent approach ONCE in the application if that's what it takes to make module developers' lives easier)
Any suggestions on where I should look to figure this out? (I'll post the answer here if I find it first)
I understand that your question basically boils down to How can I include Facelets views in a JAR?
You can do this by placing a custom ResourceResolver in the JAR.
public class FaceletsResourceResolver extends ResourceResolver {
private ResourceResolver parent;
private String basePath;
public FaceletsResourceResolver(ResourceResolver parent) {
this.parent = parent;
this.basePath = "/META-INF/resources"; // TODO: Make configureable?
}
#Override
public URL resolveUrl(String path) {
URL url = parent.resolveUrl(path); // Resolves from WAR.
if (url == null) {
url = getClass().getResource(basePath + path); // Resolves from JAR.
}
return url;
}
}
Configure this in webapp's web.xml as follows:
<context-param>
<param-name>javax.faces.FACELETS_RESOURCE_RESOLVER</param-name>
<param-value>com.example.FaceletsResourceResolver</param-value>
</context-param>
Imagine that you've a /META-INF/resources/foo/bar.xhtml in random.jar, then you can just include it the usual way
<ui:include src="/foo/bar.xhtml" />
or even dynamically
<ui:include src="#{bean.path}" />
Note: since Servlet 3.0 and newer JBoss/JSF 2.0 versions, the whole ResourceResolver approach is not necessary if you keep the files in /META-INF/resources folder. The above ResourceResolver is only mandatory in Servlet 2.5 or older JBoss/JSF versions because they've bugs in META-INF resource resolving.
See also:
Packaging Facelets files (templates, includes, composites) in a JAR
JSF facelets template packaging
I was looking for information on the same topic and came across this link: How-to: Modular Java EE Applications with CDI and PrettyFaces which worked really well for me.
By the way.. you can avoid implementing your own resource resolver when you're using seam solder (currently being integrated into apache deltaspike) which is a really useful library complementing CDI (your typical Java EE 6 component model)
I too experimented with modularity in jsf applications. Basically I built a template interface with a toolbar which gets filled with buttons provided by each module. Typically you will do this by providing a List of Strings as a Named Object:
#Produces
#SomethingScoped
#Named("topMenuItems")
public List<String> getTopMenuItems(){
return Arrays.asList("/button1.xhtml", "/button2.xhtml", "/button3.xhtml");
}
Notice how each of the buttons may come from a different module of the jsf application.
The template interface contains a panel where
you can use it in your markup the following way (at your own risk ;) ) :
....
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
....
<xy:toolbar>
<xy:toolbarGroup>
<c:forEach items="#{topMenuItems}" var="link">
<ui:include src="#{link}" />
</c:forEach>
</xy:toolbarGroup>
</xy:toolbar>
<xy:panel>
<ui:include src="#{contentPath}"/>
</xy:panel>
This was the toolbar and the content panel.
a simple button or view definition may look like this:
<ui:composition ...>
<xy:commandButton actionListener="#{topMenuController.switchContent()}"
value="Test" id="testbutton" />
</ui:composition>
lets name this artifact view1.xhtml
When this button is pushed (which doesn't trigger a postback using the actionListener, we want to reload the content using ajax) the switchContentMethod in your controller may change the string returned by getContentPath :
public void switchContent(){
contentPath = "/view1.xhtml";
}
#Produces
#SomethingScoped
#Named("contentPath")
public String getContentPath(){
return contentPath;
}
now you can change the view displayed in the panel using the button in the menubar which sort of gives you navigation without page reloads.
Some advice ( or 'what I've learned' ) :
You may wanna choose a large scope for the getTopMenuItems method
Don't nest the ui:include tag. Unfortunately this is not possible (e.g. your view1.xhtml cannot include another composition). I really
would like something like this to be possible as you can build really
modular jsf views with this, kinda like portlets only without the
portlets.. =D
doing ui:include in container components like tabviews also proves problematic.
generally it's not advisable to mix JSTL (c:forEach) and JSF. Still I found this to be the only way working as ui:repeat gets
evaluated too late e.g. your included content does not appear.
Related
I have a webserver with Struts 2.3 (with the conventions plugin)
I'm encountering some problems with the handling of MessageResources for localization.
This is my directory layout (simplified):
global.properties
it.group.projectName
actions
LoginAction.java
LoginAction.properties
users
SampleAction.java
SampleAction.properties
package-info.java (neesed for the conventions plugin to define namespace)
package.properties
Package-info.java:
#org.apache.struts2.convention.annotation.ParentPackage(value = "users")
package it.group.projectName.actions.users;
Now, if I ask for a string contained in global.properties or LoginAction.properties (using <s:text name="navbar.myPage"/> ) everything works fine.
But when I ask for any string contained in package.properties or SampleAction.properties from the page corresponding to SampleAction (sample.jsp) it seems it can't find it and just prints out the key. Any ideas?
According to this article (and others) Struts2 should load those files, but it seems to have some problems with convention plugin and the namespace redefinition.
According to that article you should read it careful. Another approach to get message resources is to use getText. Make sure the current locale is default and an action extends ActionSupport.
<s:property value="getText('navbar.myPage')" />
I have a customized JSP tag library with a Java class (extending TagSupport) that generates the output for my web application. It has some parameters that are formed into HTML code using a StringBuilder.
Now the generated HTML is becoming more complex and hard to handle with calls of StringBuilder.append, so I'd like to replace the code generation with a Freemarker template.
I already found out that I could use a generic Struts component tag instead, because the Struts tags already use Freemarker template files, so I could write a tag like:
<s:component template="/components/myStruct.ftl">
<s:param name="myParam" value="%{'myParam'}" />
</s:component>
Then writing the specified template file myStruct.ftl would probably solve my problem. I actually did not try if Struts really finds and uses that file correctly, but I optimistically expect it to work.
My question is, if it's also possible to retain the current code with the customized tag
<my:struct param="myParam" />
and only change the Java class linked to that tag.
I've found code that reads a Freemarker template:
Configuration config = FreemarkerManager.getInstance().getConfiguration(pageContext.getServletContext());
config.setServletContextForTemplateLoading(pageContext.getServletContext(), "/components");
Template templ = config.getTemplate("myStruct.ftl");
templ.process(params, pageContext.getOut());
but it seems very circuitously to me and I wondered what would be the "standard" way to do it. Additionally it seemed that you cannot use tags from the Struts tag library in a template used like this. (I ran into an ArrayIndexOutOfBoundException, caused by Sitemesh... I did not analyze it yet.)
My intention was to keep the Java class as some kind of wrapper around the Struts component tag. Maybe somthing like:
OgnlValueStack stack = TagUtils.getStack(pageContext);
Component c = new Component(stack);
c.addParameter("param", param);
But I don't know how to continue this code stub. It may be crap anyway.
Is there an easy/"standard" way to do this or do I simply have to get rid of the customized tag?
Thanks in advance.
A friend of mine sent me this link:
http://cppoon.wordpress.com/2013/02/27/how-to-create-a-struts-2-component-with-freemarker/
This is what I was looking for. The gist is to change the customized tag to not extend TagSupportbut AbstractUITag which makes it a Struts tag instead of a JSP tag, roughly speaking.
This enables the automatic linkage (by name and path conventions) to my Freemarker template. I basically followed the instructions on that page. I only added the methods that are abstract in the super class, so they had to be implemented.
IMO the site lacks of a description of how the UI bean class is linked to the tag class. But as the IDE forces you to implement the getBean method inside the tag class, you quickly get to this code (using the classes described on that site):
#Override
public Component getBean(OgnlValueStack stack, HttpServletRequest request, HttpServletResponse response)
{
Pagination pagination = new Pagination(stack, request, response);
pagination.setList(list);
return pagination;
}
This might not be completely correct for the recent Struts, but it worked for the ancient version I've got to use.
Thanks again to the guy who sent me the link :)
I have a JSP 2.0 file containing calls to a custom tag that needs to know what bundle is currently in use on the page, so it can look up some resources. The custom tag is written in Java. The bundle can change depending what page the custom tag is used on, but the resource key will always be the same, so I wanted to use the existing fmt:bundle tag to specify this, e.g.:
<fmt:bundle basename="myBundle">
<custom:tag title="text.title"/>
</fmt:bundle>
I've been assuming that the fmt:bundle tag can be read from (or otherwise provides its environment to) the inner tags, where the custom tag is in the above example (in the same way that it interacts with the fmt:message tag), but I haven't been able to figure out how to access the LocalizationContext that fmt:bundle supposedly defines from within the Java that defines the custom tag.
I've tried
LocalizationContext lc = (LocalizationContext)Config.get(
pageContext.getRequest(),Config.FMT_LOCALIZATION_CONTEXT);
ResourceBundle rb = lc.getResourceBundle();
String s =rb.getKey(title);
but I just get
java.util.MissingResourceException: Can't find resource for bundle
java.util.PropertyResourceBundle, key text.title
which seems to indicate that is not the right place to look (I guess that has fallen through to the default bundle?).
One workaround might be to pass the bundle name into the custom tag, but I'm sure what I want to achieve should be possible, if only my poor JSP know-how didn't let me down, so I hope someone can help me get a better understanding!
What should I be doing?
According to the JSTL specification there is a class named LocaleSupport that implements the bundle lookup and may be used by any tag handler implementation that needs to produce localized messages.
Does anybody have a quick method to generate slugs and permalinks in Grails 1.3.7/2.0.0.RC1?
The main restriction: this method should work with non-latin characters.
Russian/bulgarian cirillic, deutsch umlauts etc...
Any suggestions ?
Grails 2.0.0.RC1
From the 2.0.0.RC1 docs:
Link Generation API
A general purpose LinkGenerator class is now available that is usable
anywhere within a Grails application and not just within the context
of a controller. For example if you need to generate links in a
service or an asynchronous background job outside the scope of a
request:
LinkGenerator grailsLinkGenerator
def generateLink() { grailsLinkGenerator.link(controller:"book", action:"list") }
Although it's not stated explicitly, I assume the reference to grailsLinkGenerator is obtained via dependency injection
Grails 1.3.7
You can use either the createLink or resource tags to generate links. If you're generating permalinks, I assume you'll want these to be absolute URLs. If so, you'll need to use either the absolute or base attribute when using these tags.
If you use the absolute attribute, be sure to set the value of grails.serverURL in Config.groovy
Link Permanence
The text above describes how to generate links to resources in a Grails application, but doesn't say anything about how to make these links permanent. AFAIK, the link to a resource will always remain the same as long as you don't change anything that is used in the URL mapping scheme (as defined in UrlMappings.groovy)
By default the URL mapping scheme uses
the resource's ID
the controller name
the action name
So if you never change these for the links of interest, you should be good.
As easy as:
title.replaceAll("[\\W]+", "-")
That makes it.
I'm trying to figure this out, I was asking this question in Liferay's forum here - the last entry.
And only thing I'm able to come up with is creating an Expando for FileEntry, which seems very complicated. Who knows if it makes sense. I don't like the expando feature since I couldn't query them via hibernate properly.
Does anybody know the answer for my question in that Liferay forum ?
The problem is that,
DLAppLocalServiceUtil.addFileEntry(...);
DLLocalServiceUtil.addFile(....);
doesn't let you store any additional information / properties about the file. As a result, one would have to use JackRabbit directly, instead of using Liferay's JCRHook. But you loose all the advantages that Document Library brings.
Yep, the only option is to use Expando AKA custom properties/fields. In case of fileEntry you don't need to create table and columns programmatically but you can set it up in Control Panel > custom fields.
After that you have a few options how to populate the expando values.
fileEntry.getExpandoBridge().setAttribute("propName", "propValue")
or if you get the properties from view layer
<liferay-ui:custom-attributes-available className="<%= DLFileEntry.class.getName() %>">
<liferay-ui:custom-attribute-list
className="<%= DLFileEntry.class.getName() %>"
classPK="<%= (fileVersion != null) ? fileVersion.getFileVersionId() : 0 %>"
editable="<%= true %>"
label="<%= true %>"
/>
</liferay-ui:custom-attributes-available>
and then
ServiceContext serviceContext = ServiceContextFactory.getInstance(
DLFileEntry.class.getName(), actionRequest);
serviceContext gets populated by parameters in actionRequest and you then just call
fileEntry.getExpandoBridge().setAttributes(serviceContext)
Finally, you may need to query for fileEntries with particular properties
public Hits search() {
Map<String, Serializable> attributes = new HashMap<String, Serializable>();
attributes.put("propertyName", "propertyValue");
SearchContext searchContext = new SearchContext();
searchContext.setAttributes(attributes);
Indexer indexer = IndexerRegistryUtil.getIndexer(FileEntry.class);
return indexer.search(searchContext);
}
Of course, this solution may seem a little complicated, because Liferay Document Library is not a JCR content repository, but it's literally a document library that provides abstraction layer for concrete repo implementations via Hooks, such as JCRHook ( where files are stored into jackrabbit repository ), CMIS support, migration support etc. It also handles permissionChecking, fileVersioning, document workflow and asset management.
So if you intend to do something more complicated and you will have to query the properties/metadata, change them and expand them. You should consider using JCR repository directly...