3 Replies Latest reply on Sep 11, 2006 11:56 AM by gus888

    MyFaces Tomahawk schedule component does not work.

    sidragon

      I am having trouble using the Tomahawk schedule (see http://myfaces.apache.org/tomahawk/schedule.html ) component in a simple Seam application. The component renders the day view regardless of the mode or the addition of any entries to the ScheduleModel. Based on the lack of debugging output, it does not appear to get a reference to my ScheduleModel implementation. (I am subclassing SimpleScheduleModule provided by the Tomahawk library such that its getter and setter methods produce log messages, but again, I get no output.) Others have reported varying degrees of success (see http://mail-archives.apache.org/mod_mbox/myfaces-users/200607.mbox/%3C44BF5C7A.2090401@psiserv.com%3Eapache.org/mod_mbox/myfaces-users/200607.mbox/%3C44BF5C7A.2090401@psiserv.com%3E ) with this component when used with Seam, but I basically get nothing, not even any exceptions.

      I have attempted to use both the 1.1.4 and 1.1.5 Tomahawk snapshot releases. I am using Facelets with Seam and JBoss AS 1.0.1.GA. (Other Seam applications work flawlessly, by the way.) Other Tomahawk components are working just fine after following the instructions found at http://wiki.apache.org/myfaces/Use_Facelets_with_Tomahawk . I have added the following tag to my Facelets tag library definition.

      <?xml version="1.0"?>
      <!DOCTYPE facelet-taglib PUBLIC "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN" "http://java.sun.com/dtd/facelet-taglib_1_0.dtd">
      <facelet-taglib>
       <namespace>http://myfaces.apache.org/tomahawk</namespace>
       <!-- ... -->
       <tag>
       <tag-name>schedule</tag-name>
       <component>
       <component-type>org.apache.myfaces.Schedule</component-type>
       </component>
       </tag>
       <!-- ... -->
      </facelet-taglib>


      The web application deployment descriptor follows.

      <?xml version="1.0" encoding="UTF-8"?>
      <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
       <listener>
       <listener-class>org.jboss.seam.servlet.SeamListener</listener-class>
       </listener>
      
       <listener>
       <listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
       </listener>
      
       <filter>
       <filter-name>Seam Exception Filter</filter-name>
       <filter-class>org.jboss.seam.servlet.SeamExceptionFilter</filter-class>
       </filter>
      
       <filter>
       <filter-name>Seam Redirect Filter</filter-name>
       <filter-class>org.jboss.seam.servlet.SeamRedirectFilter</filter-class>
       </filter>
      
       <filter-mapping>
       <filter-name>Seam Exception Filter</filter-name>
       <url-pattern>/*</url-pattern>
       </filter-mapping>
      
       <filter-mapping>
       <filter-name>Seam Redirect Filter</filter-name>
       <url-pattern>*.seam</url-pattern>
       </filter-mapping>
      
       <context-param>
       <param-name>javax.faces.AUTO_SCROLL</param-name>
       <param-value>true</param-value>
       </context-param>
      
       <context-param>
       <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
       <param-value>client</param-value>
       </context-param>
      
       <context-param>
       <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
       <param-value>.xhtml</param-value>
       </context-param>
      
       <context-param>
       <param-name>facelets.DEVELOPMENT</param-name>
       <param-value>true</param-value>
       </context-param>
      
       <context-param>
       <param-name>facelets.LIBRARIES</param-name>
       <param-value>/WEB-INF/tomahawk.facelets.xml</param-value>
       </context-param>
      
       <servlet>
       <servlet-name>Faces Servlet</servlet-name>
       <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
       <load-on-startup>1</load-on-startup>
       </servlet>
      
       <servlet-mapping>
       <servlet-name>Faces Servlet</servlet-name>
       <url-pattern>*.seam</url-pattern>
       </servlet-mapping>
      
       <servlet-mapping>
       <servlet-name>Faces Servlet</servlet-name>
       <url-pattern>*.jsf</url-pattern>
       </servlet-mapping>
      
       <!--
      
       MyFaces extensions filter. See http://myfaces.apache.org/tomahawk/extensionsFilter.html
       for details.
      
       -->
       <filter>
       <filter-name>MyFaces Extensions Filter</filter-name>
       <filter-class>org.apache.myfaces.webapp.filter.ExtensionsFilter</filter-class>
       <init-param>
       <param-name>maxFileSize</param-name>
       <param-value>20m</param-value>
       </init-param>
       </filter>
      
       <filter-mapping>
       <filter-name>MyFaces Extensions Filter</filter-name>
       <servlet-name>Faces Servlet</servlet-name>
       </filter-mapping>
      
       <filter-mapping>
       <filter-name>MyFaces Extensions Filter</filter-name>
       <url-pattern>/faces/*</url-pattern>
       </filter-mapping>
      </web-app>


      JavaServer Faces configuration follows.

      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN" "http://java.sun.com/dtd/web-facesconfig_1_0.dtd">
      <faces-config>
       <application>
       <view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
       </application>
      
       <lifecycle>
       <phase-listener>org.jboss.seam.jsf.SeamExtendedManagedPersistencePhaseListener</phase-listener>
       </lifecycle>
      
       <managed-bean>
       <managed-bean-name>today</managed-bean-name>
       <managed-bean-class>java.util.Date</managed-bean-class>
       <managed-bean-scope>application</managed-bean-scope>
       </managed-bean>
      
       <managed-bean>
       <managed-bean-name>foo</managed-bean-name>
       <managed-bean-class>com.mycompany.scheduler.DummyScheduleModel</managed-bean-class>
       <managed-bean-scope>session</managed-bean-scope>
       <managed-property>
       <property-name>mode</property-name>
       <value>3</value>
       </managed-property>
       <managed-property>
       <property-name>selectedDate</property-name>
       <value>#{today}</value>
       </managed-property>
       </managed-bean>
      
       <managed-bean>
       <managed-bean-name>bar</managed-bean-name>
       <managed-bean-class>com.mycompany.scheduler.TriggerScheduleController</managed-bean-class>
       <managed-bean-scope>session</managed-bean-scope>
       <managed-property>
       <property-name>model</property-name>
       <value>#{foo}</value>
       </managed-property>
       </managed-bean>
      </faces-config>


      Sample implementation of ScheduleModel.

      package com.mycompany.scheduler;
      
      import java.util.Calendar;
      import java.util.Date;
      
      import org.apache.myfaces.custom.schedule.model.DefaultScheduleEntry;
      import org.apache.myfaces.custom.schedule.model.ScheduleEntry;
      import org.apache.myfaces.custom.schedule.model.ScheduleModel;
      import org.apache.myfaces.custom.schedule.model.SimpleScheduleModel;
      
      public class DummyScheduleModel extends SimpleScheduleModel
      {
       public DummyScheduleModel()
       {
       super();
       final Calendar calendar = Calendar.getInstance();
       calendar.setTime(new Date());
       final DefaultScheduleEntry entry = new DefaultScheduleEntry();
       entry.setId("dummyEntry");
       entry.setStartTime(calendar.getTime());
       calendar.add(Calendar.MINUTE, 45);
       entry.setEndTime(calendar.getTime());
       entry.setTitle("Test MyFaces schedule component");
       entry.setSubtitle("my office");
       entry.setDescription("We need to get this thing out of the sandbox ASAP.");
       setMode(ScheduleModel.WEEK);
       addEntry(entry);
       refresh();
       System.err.println("Created DummyScheduleModel instance with an event on "
       + entry.getStartTime() + ".");
       }
      
       public boolean isEmpty()
       {
       System.err.println("Called isEmpty (" + super.isEmpty() + ")");
       return super.isEmpty();
       }
      
       public Date getSelectedDate()
       {
       System.err.println("Called getSelectedDate (" + super.getSelectedDate() + ")");
       return super.getSelectedDate();
       }
      
       public ScheduleEntry getSelectedEntry()
       {
       System.err.println("Called getSelectedEntry (" + super.getSelectedEntry() + ")");
       return super.getSelectedEntry();
       }
      
       public boolean isEntrySelected()
       {
       System.err.println("Called isEntrySelected (" + super.isEntrySelected() + ")");
       return super.isEntrySelected();
       }
      
       public int getMode()
       {
       System.err.println("Called getMode (" + super.getMode() + ")");
       return super.getMode();
       }
      
       public Object get(int i)
       {
       System.err.println("Called get (" + super.get(i) + ")");
       return super.get(i);
       }
      
       public boolean containsDate(Date date)
       {
       System.err.println("Called containsDate (" + super.containsDate(date) + ")");
       return super.containsDate(date);
       }
      
       public int size()
       {
       System.err.println("Called size (" + super.size() + ")");
       return super.size();
       }
      }


      My simple "controller" class.

      package com.mycompany.scheduler;
      
      import java.text.DateFormat;
      import java.text.SimpleDateFormat;
      
      import org.apache.myfaces.custom.schedule.model.ScheduleModel;
      
      public class TriggerScheduleController
      {
       private ScheduleModel model;
      
       public ScheduleModel getModel()
       {
       System.err.println("Giving model " + model);
       return model;
       }
      
       public void setModel(final ScheduleModel model)
       {
       System.err.println("Got model " + model);
       this.model = model;
       }
      }


      Note that I started with TriggerScheduleController as a stateful session bean. The class had the following annotations.

      @Stateful
      @Name("triggerSchedule")
      @Scope(SESSION)


      And these methods.

      @Create
      public void create()
      {
       setModel(new DummyScheduleModel());
       getModel().setMode(ScheduleModel.MONTH);
      }
      
      @Destroy
      @Remove
      public void destroy()
      {
      }


      The view, browse.xhtml, contains the following (note it fills in a template).

      <?xml version="1.0" encoding="utf-8"?>
      <ui:composition xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"
       xmlns:s="http://jboss.com/products/seam/taglib" xmlns:t="http://myfaces.apache.org/tomahawk"
       xmlns:ui="http://java.sun.com/jsf/facelets" template="/template.xhtml">
       <ui:define name="title-text">Schedule</ui:define>
      
       <ui:define name="content">
       <div class="Section">
       <h2>
       <span>Browse</span>
       </h2>
      
       <h:form>
       <t:schedule id="triggerSchedule" value="#{bar.model}" rendered="true" theme="evolution" tooltip="true"
       visibleEndHour="18" visibleStartHour="8" workingEndHour="17" workingStartHour="9" readonly="false"/>
       </h:form>
       </div>
       </ui:define>
      </ui:composition>


      When exploded, my deployment in JBoss appears as follows.

      /
       scheduler.jar!/
       META-INF/
       ejb-jar.xml
       persistence.xml
       seam.properties
       (project classes)
       scheduler.war/
       WEB-INF/
       lib/
       tomahawk-1.1.[45].jar
       facelets-1.1.11.jar
       commons-lang-2.1.jar
       el-ri.jar
       jboss-seam-ui-1.0.1.GA.Jar
       el-api.jar
       web.xml
       components.xml
       pages.xml
       faces-config.xml
       tomahawk.facelets.xml
       browse.xhtml
       template.xhtml
       META-INF/
       application.xml
       jboss-app.xml
       jboss-seam-1.0.1.GA.jar
       tomahawk-1.1.[45].jar
      


      And for reference, application.xml contains this.

      <?xml version="1.0" encoding="UTF-8"?>
      <application xmlns="http://java.sun.com/xml/ns/j2ee" version="1.4" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://java.sun.com /xml/ns/j2ee
       http://java.sun.com/xml/ns/j2ee/application_1_4.xsd">
       <display-name>Scheduler</display-name>
       <module>
       <web>
       <web-uri>scheduler.war</web-uri>
       <context-root>/scheduler</context-root>
       </web>
       </module>
       <module>
       <ejb>scheduler.jar</ejb>
       </module>
       <module>
       <java>jboss-seam-1.0.1.GA.jar</java>
       </module>
       <module>
       <java>tomahawk-1.1.[45].jar</java>
       </module>
      </application>
      


      Of course, when I was using a stateful bean, I was passing that through the value attribute of t:schedule. In that case, I have be supplying the @Out annotation on the model field. I have even tried making the bean extend SimpleScheduleModel itself and pass it directly. The configuration you see here is to mimick the known-working example provided by the MyFaces project (their examples work when deployed in JBoss).

      If I upgrade the MyFaces library in JBoss's Tomcat deployment (tried both 1.1.4 and 1.1.5 snapshots), things become even more broken. And again, as mentioned, no messages with the exception of the println statement in the constructor are produced at runtime.

      So, does anyone have any idea what I am doing wrong?

        • 1. Re: MyFaces Tomahawk schedule component does not work.
          sidragon

          It just occurred to me why this is broken. Two copies of the Tomahawk library in both the WAR and the EAR because they both have a dependency. When the schedule component in the web application checks the class type for the parameter, the classes fail to match because they come from two different libraries. (Or at least something close to this.)

          • 2. Re: MyFaces Tomahawk schedule component does not work.
            sidragon

            Alright, this is completely misfiled. It has nothing to do with Facelets and certainly nothing to do with Seam. The problem was a classloader issue. The schedule component checks that the input is an instance of ScheduleModel. Of course, if that interface is being loaded by two different contexts, they will not match. If that happens, well, the following code speaks for itself.

            public ScheduleModel getModel()
            {
             if (getValue() instanceof ScheduleModel)
             {
             return (ScheduleModel) getValue();
             }
             else
             {
             return new SimpleScheduleModel();
             }
            }


            On a side note, I positively hate this silent failure behavior. My apologies for dumping this nonsense in the wrong place.

            • 3. Re: MyFaces Tomahawk schedule component does not work.
              gus888

              You need to add something according to following codes, or refer to http://www.nabble.com/Schedule-component%2C-mouseListener-is-not-triggered-At-all-tf2213732.html

              public class ScheduleComponentHandler extends HtmlComponentHandler {
              // private static final Log logger = LogFactory.getLog(ScheduleComponentHandler.class);
              
               private static final String RENDER_ZERO_LENGTH_ENTRIES_ATTRIBUTE_NAME = "renderZeroLengthEntries";
               private static final String MOUSE_LISTENER_METHOD_ATTRIBUTE_NAME = "mouseListener";
               private static final String EXPAND_TO_FIT_ENTRIES_ATTRIBUTE_NAME = "expandToFitEntries";
              
               public ScheduleComponentHandler(ComponentConfig tagConfig) {
               super(tagConfig);
               }
              
               protected MetaRuleset createMetaRuleset(Class type){
               MetaRuleset m = super.createMetaRuleset(type);
               m.addRule(new MethodRule(MOUSE_LISTENER_METHOD_ATTRIBUTE_NAME, null, new Class[] {ScheduleMouseEvent.class }));
               return m;
               }
              
               @Override
               protected void onComponentCreated(FaceletContext context, UIComponent component, UIComponent parent) {
               if (getAttribute(RENDER_ZERO_LENGTH_ENTRIES_ATTRIBUTE_NAME) != null) {
               component.getAttributes().put(RENDER_ZERO_LENGTH_ENTRIES_ATTRIBUTE_NAME, getAttribute(RENDER_ZERO_LENGTH_ENTRIES_ATTRIBUTE_NAME).getObject(context));
               }
              
               if (getAttribute(EXPAND_TO_FIT_ENTRIES_ATTRIBUTE_NAME) != null) {
               component.getAttributes().put(EXPAND_TO_FIT_ENTRIES_ATTRIBUTE_NAME, getAttribute(EXPAND_TO_FIT_ENTRIES_ATTRIBUTE_NAME).getObject(context));
               }
               }
              }
              --tomahawk.taglib.xml
              ...
              <tag>
               <tag-name>schedule</tag-name>
               <component>
               <component-type>org.apache.myfaces.Schedule</component-type>
               <renderer-type> org.apache.myfaces.Schedule</renderer-type>
               <handler-class>com.genchik.componenthandlers.ScheduleComponentHandler</handler-class>
               </component>
              </tag>