JSF 1.2 NavigationHandler#handleNavigation called twice - jsf

I'm trying to implement a Custom Naviguation, that do the usual job and update my breadcrumb.
public class CustomNaviguationHandler extends NavigationHandlerImpl{
public void handleNavigation(FacesContext context, String fromAction, String outcome) {
//do the breadcrumb update
super.handleNavigation(context, fromAction, outcome);
}
}
But when I debug it, the method is executed 2 times once,
The 1st time, fromAction and outcome are null
The 2nd time, these parameters contains the right values.
The handler has been registered in the faces-config.xml
<navigation-handler>my.package.CustomNaviguationHandler</navigation-handler>
I'm not sure to understant why? Any Idea?

You should not extend the JSF impl class NavigationHandlerImpl at all. You should instead extend the JSF API class NavigationHandler.
Here's a kickoff example. Do your job in the handleNavigation() method. If you don't want to take responsibility on navigation, delegate to the parent.
public class CustomNavigationHandler extends NavigationHandler {
private NavigationHandler parent;
public CustomNavigationHandler(NavigationHandler parent) {
this.parent = parent;
}
#Override
public void handleNavigation(FacesContext context, String from, String outcome) {
parent.handleNavigation(context, from, outcome);
}
}

Related

Disable components in a large project

with a lot of developers and plenty of juniors I want to disable certain components such as <p:spacer> to prohibit using components for html/css issues. I want to limit the available components for libraries like omnifaces / primefaces / richfaces to a whitelist / blacklist thing basically.
Would this be a reasonable feature request for a library like omnifaces or is it to hard to build / to localized?
Basically, you can achieve this by providing a custom Application implementation (based on ApplicationWrapper) wherein you override the desired createComponent() method and throw e.g. IllegalArgumentException when a blacklisted component type and/or renderer type is passed.
Here's a kickoff example:
public class YourApplication extends ApplicationWrapper {
private static final Set<String> BLACKLISTED_COMPONENT_TYPES = unmodifiableSet(new HashSet<>(asList(
"org.primefaces.component.Spacer",
"com.example.SomeComponentType",
"com.example.OtherComponentType"
// Etc..
)));
private final Application wrapped;
public YourApplication(Application wrapped) {
this.wrapped = wrapped;
}
#Override
public UIComponent createComponent(FacesContext context, String componentType, String rendererType) {
if (BLACKLISTED_COMPONENT_TYPES.contains(componentType)) {
throw new IllegalArgumentException("You are not allowed to use this component.");
}
return super.createComponent(context, componentType, rendererType);
}
#Override
public Application getWrapped() {
return wrapped;
}
}
You can get it to run with this factory:
public class YourApplicationFactory extends ApplicationFactory {
private final ApplicationFactory wrapped;
public YourApplicationFactory(ApplicationFactory wrapped) {
this.wrapped = wrapped;
}
#Override
public Application getApplication() {
return new YourApplication(wrapped.getApplication());
}
#Override
public void setApplication(Application application) {
wrapped.setApplication(application);
}
}
Which is registered in faces-config.xml as below:
<factory>
<application-factory>com.example.YourApplicationFactory</application-factory>
</factory>
You can use tag file feature of jsf. You will declare tag file for each component that you want to use. After that, your team will only use these tag file in your project.

jsf custom converter does not work with view scope

Here is my code
Pojo
public class Deal implements Serializable {
private int id;
private String name;
private String description;
private Customer customer;
//getter setter omitted
}
public class Customer implements Serializable {
private int id;
private String name;
private String email;
private String phone;
//getter setter and equal hashcode omitted
}
Managed Bean
#ManagedBean(name="dealBean")
#ViewScoped
public class DealBean implements Serializable {
private List<Customer> customerList;
private List<Deal> dealList;
private Deal deal;
#PostConstruct
public void init() {
deal = new Deal();
dealList = new ArrayList<Deal>();
customerList = new ArrayList<Customer>();
customerList.add(new Customer(1, "MPRL", "mprl#mail.com", "1234455"));
customerList.add(new Customer(2, "Total", "total#mail.com", "3434323"));
customerList.add(new Customer(3, "Petronas", "petronas#mail.com", "8989876"));
}
//getter setter omitted
}
Customer Converter
#FacesConverter("customerConverter")
public class CustomerConverter implements Converter {
#Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String customerID) {
DealBean dealBean = (DealBean) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("dealBean");
if (dealBean != null) {
List<Customer> customerList = dealBean.getCustomerList();
for (Customer customer : customerList) {
if (customerID.equals(String.valueOf(customer.getId()))) {
return customer;
}
}
}
return null;
}
#Override
public String getAsString(FacesContext arg0, UIComponent arg1, Object obj) {
if (obj != null) {
return String.valueOf(((Customer)obj).getId());
}
return null;
}
}
XHTML
Customer : <h:selectOneMenu id="customer" value="#{dealBean.deal.customer}">
<f:converter converterId="customerConverter" />
<f:selectItems value="#{dealBean.customerList}" var="cus"
itemLabel="#{cus.name}" itemValue="#{cus}" />
</h:selectOneMenu>
When the managed bean is in request or session scope, the Customer pojo is set correctly to Deal pojo. The problem is when the managed bean is in View scope, the Customer pojo is set to Deal pojo as NULL.
I am using JSF 2.2.0
Thanks much for the help in advance.
It's not the converter, is the view scoped the one broken:
Since you're using JSF tags, you cannot use #ViewScoped annotation, because it was removed from specification and recovered only for CDI usage. You could use omnifaces view scoped or the components of apache myFaces (I personally recommend omnifaces).
You can confirm this creating a
System.out.print("Creating");
in the constructor and checking how is called each Ajax request, so the bean is not recovered and since is marked as view and is a partial request, the values are not setted again (unless you send all the form, which is not a nice solution), other workaround could be making the bean request and recover all the data each request, making it Session (but will be alive for the session), or the #ConvesationScoped, in which you'll have to destroy and start the conversation manually.
Again, my first recommendation could be change to a Java ee server compliant and use the CDI annotations since JSF are being depreciated and not updated anymore

JSF 2 Custom components having Expression Language for attribute value don't trigger the attribute setter

I have build a custom component in JSF 2.0
The tag looks like this:
<x:myTag id="1" name="AAA" />
The corresponding java class:
#FacesComponent("a.b.c.MyTag")
public class UIMyTag extends UIInput {
private String name;
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public void encodeBegin(FacesContext context) throws IOException {
ResponseWriter writer = context.getResponseWriter();
logger.debug(getName()); //prints null for name="#{dummyBean.name}"
// and AAA for name="AAA"
logger.debug(getAttributes().get("name")); // always correct value
...
}
....
}
If I use
<x:myTag id="1" name="AAA" />
everything works as expected, but when I use EL for myTag attributes the setName() method never gets called. So for,
<x:myTag id="#{dummyBean.id}" name="#{dummyBean.name}" />
I always get null for the name property inside my encodeBegin method. After debugging I've noticed that the setName method never gets called. I thought that maybe something regarding EL messes things up (and I still believe that the reason is related to that), but what's really weird is that the id property works good: the setter gets called, and the value is as expected when the econding begins.
I have to mention that if I call getAttributes().get("name") from the encodeBegin method I get the correct name value, but I'm intrigued why it doesn't work with getter and setter.
Any ideas what's missing to my component?
This behavior is expected and by specification. Attribute values which are value expressions are set by UIComponent#setValueExpression(). They are namely supposed to be evaluated only when they are really been used, usually during view render time.
The id (and binding) attribute has special treatment: it's evaluated during view build time before it's been set, so the "regular" setter would be called instead of the setValueExpression() (because rendering of the view would otherwise crash when the id (or binding) attribute dynamically evaluates to a different value than it was during the view build time for some reason).
Better is to delegate the getters/setters to UIComponent#getStateHelper() instead of to local properties. The setValueExpression() ultimately also end up in the StateHelper (note that it doesn't call the setter at all; just call the getter if you need the data) and the getAttributes() also resolves the values from the StateHelper.
public String getName() {
return (String) getStateHelper().eval("name");
}
public void setName(String name) {
getStateHelper().put("name", name);
}
Note that you can safely remove the getId() and setId() methods, because they're already definied in the UIComponentBase superclass which you're extending from.

XspDateTimeHelper and XspInputText

I am trying to build an JSF library control for XPages based on the examples by Keith Strickland.
http://xprentice.gbs.com/A55BAC/keithstric.nsf/default.xsp?documentId=82770C11FA7B9B21852579C100581766
I am trying to create a function which creates an InputBox control with an attached DatePicker control.
here is code that I have tried :
XspInputText inputText = new XspInputText();
inputText.setId("inputText1");
DateTimeConverter converter = new DateTimeConverter();
converter.setType("date");
inputText.setConverter(converter);
XspDateTimeHelper dateTimeHelper = new XspDateTimeHelper();
dateTimeHelper.setId("dateTimeHelper1");
inputText.getChildren().add(dateTimeHelper);
inputText.setRendered(true);
this.getChildren().add(inputText);
The InputText control is rendered but the Converter, Validator and DatePicker button are not rendered.
Hi the answer from Keith solved the problem but now I had an other. I have tryed to add the inputtext to an fromtable from the exlib. Without the formtable arround the "DateTimePicker" is rendered correctly, but with the Formtable it is only rendered as a textfield.
here is the complete code:
public class Libcontrol extends UIComponentBase implements FacesComponent {
private static final String RENDERER_TYPE = "de.chris.Libcontrol ";
private static final String COMPONENT_FAMILY = "de.chris";
public void CommonContactInfo() {
setRendererType(RENDERER_TYPE);
}
#Override
public String getFamily() {
return COMPONENT_FAMILY;
}
#SuppressWarnings("unchecked")
public void initBeforeContents(FacesContext arg0) throws FacesException {
UIFormTable table = new UIFormTable();
table.setId("mytable");
table.setDisableRowError(false);
table.setDisableErrorSummary(true);
table.setLabelPosition("left");
UIFormLayoutRow row = new UIFormLayoutRow();
row.setId("myrow");
row.setLabel("DATEROW");
XspInputText inputText = new XspInputText();
inputText.setId("inputText1");
DateTimeConverter converter = new DateTimeConverter();
converter.setType("date");
inputText.setConverter(converter);
XspDateTimeHelper dateTimeHelper = new XspDateTimeHelper();
dateTimeHelper.setId("dateTimeHelper1");
inputText.getChildren().add(dateTimeHelper);
inputText.setRendered(true);
this.getChildren().add(inputText);
row.getChildren().add(inputText);
table.getChildren().add(row);
this.getChildren().add(table);
}
public void buildContents(FacesContext arg0, FacesComponentBuilder arg1) throws FacesException {
// Do Nothing
}
/**
* This method must be present because we're implementing FacesComponent
*/
public void initAfterContents(FacesContext arg0) throws FacesException {
// Do nothing
}
}
I'm not sure where the problem is, any ideas?
OK, think I may have found your issue. On the XPage/Custom Control itself, set the properties for:
dojoParseOnLoad=true
dojoTheme=true
or, if you don't want to set those properties in your XPage/Custom Control you can do it in the renderer encodeBegin with the following lines somewhere before the call to super.encodeBegin():
//Here context = FacesContext or FacesContext.getCurrentInstance();
UIViewRootEx rootEx = (UIViewRootEx) context.getViewRoot();
rootEx.setDojoParseOnLoad(true);
rootEx.setDojoTheme(true);

How to intercept methods of EntityManager with Seam 3?

I'm trying to intercept the method persist and update of javax.persistence.EntityManager in a Seam 3 project.
In a previous version (Seam 2) of the micro-framework I'm trying to make, I did this using an implementation of org.hibernate.Interceptor and declaring it in the persistence.xml.
But I want something more "CDI-like" now we are in a JEE6 environment.
I want that just before entering in a EntityManager.persist call, an event #BeforeTrackablePersist is thrown. The same way, I want an event #BeforeTrackableUpdate to be thrown before entering in a EntityManager.merge call. Trackable is an interface which some of my Entitys could implement in order to be intercepted before persist or merge.
I'm using Seam 3 (3.1.0.Beta3) Extended Persistence Manager :
public class EntityManagerHandler {
#SuppressWarnings("unused")
#ExtensionManaged
#Produces
#PersistenceUnit
private EntityManagerFactory entityManagerFactory;
}
So I've made a javax.enterprise.inject.spi.Extension, and tryied many ways to do that :
public class TrackableExtension implements Extension {
#Inject #BeforeTrackablePersisted
private Event<Trackable> beforeTrackablePersistedEvent;
#Inject #BeforeTrackableMerged
private Event<Trackable> beforeTrackableMergedEvent;
#SuppressWarnings("unchecked")
public void processEntityManagerTarget(#Observes final ProcessInjectionTarget<EntityManager> event) {
final InjectionTarget<EntityManager> injectionTarget = event.getInjectionTarget();
final InjectionTarget<EntityManager> injectionTargetProxy = (InjectionTarget<EntityManager>) Proxy.newProxyInstance(event.getClass().getClassLoader(), new Class[] {InjectionTarget.class}, new InvocationHandler() {
#Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
if ("produce".equals(method.getName())) {
final CreationalContext<EntityManager> ctx = (CreationalContext<EntityManager>) args[0];
final EntityManager entityManager = decorateEntityManager(injectionTarget, ctx);
return entityManager;
} else {
return method.invoke(injectionTarget, args);
}
}
});
event.setInjectionTarget(injectionTargetProxy);
}
public void processEntityManagerType(#Observes final ProcessAnnotatedType<EntityManager> event) {
final AnnotatedType<EntityManager> type = event.getAnnotatedType();
final AnnotatedTypeBuilder<EntityManager> builder = new AnnotatedTypeBuilder<EntityManager>().readFromType(type);
for (final AnnotatedMethod<? super EntityManager> method : type.getMethods()) {
final String name = method.getJavaMember().getName();
if (StringUtils.equals(name, "persist") || StringUtils.equals(name, "merge")) {
builder.addToMethod(method, TrackableInterceptorBindingLiteral.INSTANCE);
}
}
event.setAnnotatedType(builder.create());
}
public void processEntityManagerBean(#Observes final ProcessBean<EntityManager> event) {
final AnnotatedType<EntityManager> annotatedType = (AnnotatedType<EntityManager>)event.getAnnotated();
// not even called
}
public void processEntityManager(#Observes final ProcessProducer<?, EntityManager> processProducer) {
processProducer.setProducer(decorate(processProducer.getProducer()));
}
private Producer<EntityManager> decorate(final Producer<EntityManager> producer) {
return new Producer<EntityManager>() {
#Override
public EntityManager produce(final CreationalContext<EntityManager> ctx) {
return decorateEntityManager(producer, ctx);
}
#Override
public Set<InjectionPoint> getInjectionPoints() {
return producer.getInjectionPoints();
}
#Override
public void dispose(final EntityManager instance) {
producer.dispose(instance);
}
};
}
private EntityManager decorateEntityManager(final Producer<EntityManager> producer, final CreationalContext<EntityManager> ctx) {
final EntityManager entityManager = producer.produce(ctx);
return (EntityManager) Proxy.newProxyInstance(entityManager.getClass().getClassLoader(), new Class[] {EntityManager.class}, new InvocationHandler() {
#Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
final String methodName = method.getName();
if (StringUtils.equals(methodName, "persist")) {
fireEventIfTrackable(beforeTrackablePersistedEvent, args[0]);
} else if (StringUtils.equals(methodName, "merge")) {
fireEventIfTrackable(beforeTrackableMergedEvent, args[0]);
}
return method.invoke(entityManager, args);
}
private void fireEventIfTrackable(final Event<Trackable> event, final Object entity) {
if (entity instanceof Trackable) {
event.fire(Reflections.<Trackable>cast(entity));
}
}
});
}
}
In all those observer methods, only the second one (processEntityManagerType(#Observes ProcessAnnotatedType<EntityManager>)) is called ! And even with that binding addition to methods persist and merge, my Interceptor is never called (I've of course enabled it with the correct lines in beans.xml, and enabled my extension with the services/javax.enterprise.inject.spi.Extension file).
Something I've thought simple with CDI seems to be actually really hard at last... or perhaps Seam 3 does something which prevent this code from executing correctly...
Does someone know how to handle that ?
I think you're making this a little harder than what it needs to be. Firstly though, JPA and CDI integration isn't very good in Java EE 6, we're very much hoping that changes in Java EE 7 and JPA 2.1.
What you'll want to do is create your own producer for the EntityManager that will delegate to an actual instance of an EntityManager, but also fire your own events when you call the methods you're interested in. Take a look at the Seam Persistence source to see one way this can be done.
As finally my little patch for Seam Persistence was applied in SEAMPERSIST-75, it will be possible in theory to do that by extending org.jboss.seam.persistence.HibernatePersistenceProvider and override the method proxyEntityManager(EntityManager).

Resources