7 Replies Latest reply on Feb 19, 2009 1:49 AM by demetrio812

    Overriding xhtml and paga.xml files in a transparent way

    demetrio812

      Hi,
      I want to implement a transparent override mechanism like this:



      /root/
        /page1.page.xml
        /page1.xhtml
        /page2.page.xml
        /page2.xhtml
        /override/
            /page1.xml
            /page1.xhtml
      



      I would like to implement a filter that check if there root file is also into the override folder and in that case read the contents of that one instead of the one into the /root/. Also it is very important that I can override also the .page.xml file and that the rewrite filter works also in the opposite way.


      I've made a filter that manage the url encoding but it will show the override folder into the url path and I would like to have it transparent, also it gives me problem using url rewrite.


      Where can I start? Can I put a filter for Facelets so it would also work with facelets templating? Has someone some experience on that topic?


      I hope I explained well.


      Thanks!


      Demetrio

        • 1. Re: Overriding xhtml and paga.xml files in a transparent way
          swd847

          Create a custom facelets ResourceResolver, if you override the resolveUrl method you can make it look in your override directory first.


          As for the pages.xml you will probably have to override the seam component org.jboss.seam.navigation.pages, have a look at the code and you can probably figure out with methods need overriding.


          Stuart

          • 2. Re: Overriding xhtml and paga.xml files in a transparent way
            demetrio812

            I will try!


            Thank you very much!


            Demetrio

            • 3. Re: Overriding xhtml and paga.xml files in a transparent way
              swd847

              The method you probably want to override on the Pages class is initialize, it will probably look something like this:



              public void initialize(Set<String> fileNames)
              {
                Set<String newFileNames = new HashSet<String>();
                for(String i: fileNames)
                {
                   //lookup override here and add new (or old) path to the set
                }
                super.initialize(newFileNames);
              }
              



              i am not sure how this will go with hot deploy, but it should point you in the right direction. There are plenty of tutorials on using the facelets ResourceResolver if you google for them.


              Stuart

              • 4. Re: Overriding xhtml and paga.xml files in a transparent way
                demetrio812

                Hi,
                the ResourceResolver works perfectly, but I don't know if there is a way to override the Page component, I will look at the Seam source code tonight, in the mean time I wrote here and wait for other suggestions while looking also by myself... :)


                Thanks again!


                Demetrio

                • 5. Re: Overriding xhtml and paga.xml files in a transparent way
                  barakka

                  Ciao Demetrio,


                  this is my attempt (working so far) at doing what you are asking for:


                  
                  /**
                   * This class adds pages.xml defined in jar files to the same page handler, as
                   * this files don't get scanned by default by seam.
                   * 
                   * @see ClasspathPagesDeploymentHandler
                   * @see org.jboss.seam.navigation.Pages
                   * @author rserafin
                   * @version $Rev: 1027 $ $Date: 2009-01-28 23:50:23 +0100 (Wed, 28 Jan 2009) $
                   */
                  @Name("es.tsbsoluciones.platform.utils.web.classpathPagesAdder")
                  @Scope(ScopeType.APPLICATION)
                  @Startup(depends = "org.jboss.seam.navigation.pages")
                  @Install(classDependencies = "org.jboss.seam.navigation.Pages", dependencies = { "org.jboss.seam.navigation.pages" })
                  public class ClasspathPagesAdder {
                  
                      /** Handler that has collected all pages.xml files. */
                      protected ClasspathPagesDeploymentHandler    pagesHandler;
                      /** Handler that has collected all .page.xml files. */
                      protected ClasspathDotPagesDeploymentHandler dotPagesHandler;
                  
                      /** Logger. */
                      @Logger
                      protected Log                                log;
                  
                  
                      /**
                       * Startup method that will add all collected files.
                       */
                      @Create
                      public void create() {
                          log.trace("Created.");
                  
                          // Retrieve deployment handlers
                          final DeploymentStrategy strategy = (DeploymentStrategy) Contexts
                              .lookupInStatefulContexts("deploymentStrategy");
                          if (strategy == null) {
                              throw new IllegalStateException("Unable to locate seam deployment strategy.");
                          }
                  
                          log.trace("Current deployment strategy: " + strategy);
                  
                          pagesHandler = (ClasspathPagesDeploymentHandler) strategy.getDeploymentHandlers().get(
                              ClasspathPagesDeploymentHandler.class.getName());
                          if (pagesHandler == null) {
                              throw new IllegalStateException("Unable to locate ClasspathPagesDeploymentHandler.");
                          }
                  
                          dotPagesHandler = (ClasspathDotPagesDeploymentHandler) strategy.getDeploymentHandlers().get(
                              ClasspathDotPagesDeploymentHandler.class.getName());
                          if (dotPagesHandler == null) {
                              throw new IllegalStateException("Unable to locate ClasspathDotPagesDeploymentHandler.");
                          }
                  
                          // First get a ref to the Pages component
                          final Pages pages = (Pages) Component.getInstance(Pages.class, false);
                  
                          if (pages == null) {
                              throw new IllegalStateException("Unable to locate the pages component.");
                          }
                  
                          // The get the parse method
                          final Method parse = getParseMethod(pages);
                  
                          if (parse == null) {
                              throw new IllegalStateException("Unable to access the parse method.");
                          }
                  
                          // The get the parse method
                          final Method parsePages = getParsePagesMethod(pages);
                  
                          if (parsePages == null) {
                              throw new IllegalStateException("Unable to access the parsePages method.");
                          }
                  
                          // now add all pages.xml
                          for (final FileDescriptor file : pagesHandler.getCollectedFileDescriptors()) {
                              addPagesFile(file, pages, parse);
                          }
                  
                          // finally add all .page.xml
                          addPageFiles(dotPagesHandler.getCollectedFileDescriptors(), pages, parsePages);
                  
                      }
                  
                  
                      /**
                       * Adds a files to pages by reflection.
                       * 
                       * @param files list of filenames to add
                       * @param pages pages object to add it
                       * @param parsePages method to be called
                       */
                      protected void addPageFiles(final Set<FileDescriptor> files, final Pages pages, final Method parsePages) {
                          // Try to call the pages object to add it
                          try {
                              for (final FileDescriptor fileDescriptor : files) {
                                  log.trace("Adding .pages.xml file: #0", fileDescriptor.getUrl());
                              }
                              parsePages.invoke(pages, (Object) files);
                          } catch (final IllegalArgumentException e) {
                              log.warn("IllegalArgumentException while trying to invoke the parsePages method.", e);
                          } catch (final IllegalAccessException e) {
                              log.warn("IllegalAccessException while trying to invoke the parsePages method.");
                          } catch (final InvocationTargetException e) {
                              log.warn("IllegalAccessException while trying to invoke the parsePages method.");
                          }
                      }
                  
                  
                      /**
                       * Adds a files to pages by reflection.
                       * 
                       * @param file file descriptor to parse
                       * @param pages pages object to add it
                       * @param parse method to be called
                       */
                      protected void addPagesFile(final FileDescriptor file, final Pages pages, final Method parse) {
                  
                          // Try to call the pages object to add it
                          try {
                              log.trace("Adding pages.xml file: #0", file.getUrl());
                  
                              // Try to get an input stream for the file
                              final InputStream fileStream = file.getUrl().openStream();
                  
                              parse.invoke(pages, fileStream);
                          } catch (final IllegalArgumentException e) {
                              log.warn("IllegalArgumentException while trying to invoke the parse method.", e);
                          } catch (final IllegalAccessException e) {
                              log.warn("IllegalAccessException while trying to invoke the parse method.", e);
                          } catch (final InvocationTargetException e) {
                              log.warn("IllegalAccessException while trying to invoke the parse method.", e);
                          } catch (final IOException e) {
                              log.warn("Unable to access pages.xml file: " + file.getUrl(), e);
                          }
                      }
                  
                  
                      /**
                       * Return the parse method of the pages object obtaining it by reflection
                       * (as it private, sigh).
                       * 
                       * @param pages the pages object.
                       * @return the parse method, if possible, null otherwise.
                       */
                      protected Method getParseMethod(final Pages pages) {
                          try {
                              final Method parse = pages.getClass().getDeclaredMethod("parse", InputStream.class);
                              parse.setAccessible(true);
                              return parse;
                          } catch (final SecurityException e) {
                              log.warn("SecurityException while trying to get the parse method.");
                          } catch (final NoSuchMethodException e) {
                              log.warn("NoSuchMethodException while trying to get the parse method.");
                          }
                  
                          return null;
                      }
                  
                  
                      /**
                       * Return the parsePages method of the pages object obtaining it by
                       * reflection (as it private, sigh).
                       * 
                       * @param pages the pages object.
                       * @return the parsePages method, if possible, null otherwise.
                       */
                      protected Method getParsePagesMethod(final Pages pages) {
                          try {
                              for (final Method m : pages.getClass().getDeclaredMethods()) {
                                  if (m.getName().equals("parsePages")) {
                                      m.setAccessible(true);
                                      return m;
                                  }
                              }
                          } catch (final SecurityException e) {
                              log.warn("SecurityException while trying to get the parse method.");
                          }
                  
                          return null;
                      }
                  }
                  
                  



                  where ClasspathPagesDeploymentHandler and ClasspathDotPagesDeploymentHandler are custom seam deployers handlers for pages.xml and .page.xml. The class uses reflection to manipulate the pages component, as the methods I needed where private. I definitely don't like it very much (I know, i was the one making it... but in retrospective), and if some seam developer would make those methods public or at least protected it would really help.


                  You might also want to have a look at the seam wiki example, as I believe to remember that they do something similar to manage the plugins.


                  Hope this can help,
                  Riccardo.

                  • 6. Re: Overriding xhtml and paga.xml files in a transparent way
                    demetrio812

                    Ciao Riccardo!
                    Thank you very much for your code!


                    My problem is different but I will solve it using your way ;)


                    Rewriting (without mistakes) the example I've done in the first post:


                    /root/
                      /page1.page.xml
                      /page1.xhtml
                      /page2.page.xml
                      /page2.xhtml
                      /override/
                          /page1.page.xml
                          /page1.xhtml
                    



                    I have to make seam to use /override/page1.page.xml as the page file for the viewId /file1.xhtml


                    Initially Seam adds also the .page.xml files that are inside the override directory so I have to remove all the file with an override (both the normal viewId and the override one) and then add another page with the normal view but reading the content of the .page.xml file inside the override.


                    I hope I explained well, another problem I'll have to solve will be the hot deploy (it adds all the pages again).


                    I hope that next version of Seam will have more support for this kind of personalizations. :)


                    I'll write here how I am going :)


                    Thanks again!


                    Demetrio

                    • 7. Re: Overriding xhtml and paga.xml files in a transparent way
                      demetrio812

                      Hi,
                      I didn't have a lot of time today but it seems it is working and I will post the final version very soon...I have also to check it against the url rewrite of the page and the overridden ones but I don't think there will be problems...


                      thanks again!!


                      Demetrio