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

    Overriding xhtml and paga.xml files in a transparent way

    demetrio812 Apprentice

      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
          Stuart Douglas Master

          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

          • 3. Re: Overriding xhtml and paga.xml files in a transparent way
            Stuart Douglas Master

            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 Apprentice

              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
                Riccardo Serafin Newbie

                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 Apprentice

                  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 Apprentice

                    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