001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    
018    package org.apache.geronimo.jasper.deployment;
019    
020    import java.io.File;
021    import java.io.IOException;
022    import java.net.JarURLConnection;
023    import java.net.MalformedURLException;
024    import java.net.URI;
025    import java.net.URISyntaxException;
026    import java.net.URL;
027    import java.util.ArrayList;
028    import java.util.Collection;
029    import java.util.Enumeration;
030    import java.util.HashMap;
031    import java.util.HashSet;
032    import java.util.List;
033    import java.util.Map;
034    import java.util.Set;
035    import java.util.jar.JarEntry;
036    import java.util.jar.JarFile;
037    
038    import javax.xml.namespace.QName;
039    
040    import org.apache.commons.logging.Log;
041    import org.apache.commons.logging.LogFactory;
042    import org.apache.geronimo.common.DeploymentException;
043    import org.apache.geronimo.deployment.ModuleIDBuilder;
044    import org.apache.geronimo.deployment.service.EnvironmentBuilder;
045    import org.apache.geronimo.deployment.xmlbeans.XmlBeansUtil;
046    import org.apache.geronimo.gbean.AbstractName;
047    import org.apache.geronimo.gbean.GBeanData;
048    import org.apache.geronimo.gbean.GBeanInfo;
049    import org.apache.geronimo.gbean.GBeanInfoBuilder;
050    import org.apache.geronimo.j2ee.annotation.Holder;
051    import org.apache.geronimo.j2ee.deployment.EARContext;
052    import org.apache.geronimo.j2ee.deployment.Module;
053    import org.apache.geronimo.j2ee.deployment.ModuleBuilderExtension;
054    import org.apache.geronimo.j2ee.deployment.NamingBuilder;
055    import org.apache.geronimo.j2ee.deployment.WebModule;
056    import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
057    import org.apache.geronimo.jasper.JasperServletContextCustomizer;
058    import org.apache.geronimo.kernel.GBeanAlreadyExistsException;
059    import org.apache.geronimo.kernel.Naming;
060    import org.apache.geronimo.kernel.config.Configuration;
061    import org.apache.geronimo.kernel.config.ConfigurationStore;
062    import org.apache.geronimo.kernel.repository.Environment;
063    import org.apache.geronimo.schema.SchemaConversionUtils;
064    import org.apache.geronimo.xbeans.javaee.FullyQualifiedClassType;
065    import org.apache.geronimo.xbeans.javaee.JspConfigType;
066    import org.apache.geronimo.xbeans.javaee.ListenerType;
067    import org.apache.geronimo.xbeans.javaee.TagType;
068    import org.apache.geronimo.xbeans.javaee.TaglibDocument;
069    import org.apache.geronimo.xbeans.javaee.TaglibType;
070    import org.apache.geronimo.xbeans.javaee.TldTaglibType;
071    import org.apache.geronimo.xbeans.javaee.WebAppType;
072    import org.apache.xbean.finder.ClassFinder;
073    import org.apache.xmlbeans.XmlCursor;
074    import org.apache.xmlbeans.XmlException;
075    import org.apache.xmlbeans.XmlObject;
076    
077    /**
078     * This JSP module builder extension is meant to find all the TLD descriptor files associated with a
079     * deployable artifact, search those TLD files for listeners, search those listeners for
080     * annotations, and ultimately create a ClassFinder using those annoated classes (for later
081     * processing by the various naming builders)
082     *
083     * @version $Rev $Date
084     */
085    public class JspModuleBuilderExtension implements ModuleBuilderExtension {
086    
087        private static final Log log = LogFactory.getLog(JspModuleBuilderExtension.class);
088    
089        private final Environment defaultEnvironment;
090        private final NamingBuilder namingBuilders;
091    
092        private static final QName TLIB_VERSION = new QName(SchemaConversionUtils.JAVAEE_NAMESPACE, "tlib-version");
093        private static final QName SHORT_NAME = new QName(SchemaConversionUtils.JAVAEE_NAMESPACE, "short-name");
094        private static final QName TAG_CLASS = new QName(SchemaConversionUtils.JAVAEE_NAMESPACE, "tag-class");
095        private static final QName TEI_CLASS = new QName(SchemaConversionUtils.JAVAEE_NAMESPACE, "tei-class");
096        private static final QName BODY_CONTENT = new QName(SchemaConversionUtils.JAVAEE_NAMESPACE, "body-content");
097    
098        private static final String SCHEMA_LOCATION_URL = "http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd";
099        private static final String VERSION = "2.1";
100    
101        public JspModuleBuilderExtension(Environment defaultEnvironment, NamingBuilder namingBuilders) {
102            this.defaultEnvironment = defaultEnvironment;
103            this.namingBuilders = namingBuilders;
104        }
105    
106        public void createModule(Module module, Object plan, JarFile moduleFile, String targetPath, URL specDDUrl, Environment environment, Object moduleContextInfo, AbstractName earName, Naming naming, ModuleIDBuilder idBuilder) throws DeploymentException {
107        }
108    
109        public void installModule(JarFile earFile, EARContext earContext, Module module, Collection configurationStores, ConfigurationStore targetConfigurationStore, Collection repository) throws DeploymentException {
110        }
111    
112        public void initContext(EARContext earContext, Module module, ClassLoader cl) throws DeploymentException {
113        }
114    
115        public void addGBeans(EARContext earContext, Module module, ClassLoader cl, Collection repository) throws DeploymentException {
116            if (!(module instanceof WebModule)) {
117                //not a web module, nothing to do
118                return;
119            }
120            //TODO Only merge if we detect jsps???
121            EnvironmentBuilder.mergeEnvironments(module.getEnvironment(), defaultEnvironment);
122    
123            WebModule webModule = (WebModule) module;
124            WebAppType webApp = (WebAppType) webModule.getSpecDD();
125    
126            EARContext moduleContext = module.getEarContext();
127            Map sharedContext = module.getSharedContext();
128    
129            GBeanData webAppData = (GBeanData) sharedContext.get(WebModule.WEB_APP_DATA);
130    
131            AbstractName moduleName = moduleContext.getModuleName();
132            Map<NamingBuilder.Key, Object> buildingContext = new HashMap<NamingBuilder.Key, Object>();
133            buildingContext.put(NamingBuilder.GBEAN_NAME_KEY, moduleName);
134    
135            //use the same jndi context as the web app
136            Map compContext = NamingBuilder.JNDI_KEY.get(sharedContext);
137            buildingContext.put(NamingBuilder.JNDI_KEY, compContext);
138    
139            //use the same holder object as the web app.
140            Holder holder = NamingBuilder.INJECTION_KEY.get(sharedContext);
141            buildingContext.put(NamingBuilder.INJECTION_KEY, holder);
142    
143            XmlObject jettyWebApp = webModule.getVendorDD();
144    
145            Configuration earConfiguration = earContext.getConfiguration();
146    
147            Set<String> listenerNames = new HashSet<String>();
148    
149            ClassFinder classFinder = createJspClassFinder(webApp, webModule, listenerNames);
150            webModule.setClassFinder(classFinder);
151    
152            namingBuilders.buildNaming(webApp, jettyWebApp, webModule, buildingContext);
153    
154            //only try to install it if reference will work.
155            //Some users (tomcat?) may have back doors into jasper that make adding this gbean unnecessary.
156            GBeanInfo webAppGBeanInfo = webAppData.getGBeanInfo();
157            if (webAppGBeanInfo.getReference("ContextCustomizer") != null) {
158                AbstractName jspLifecycleName = moduleContext.getNaming().createChildName(moduleName, "jspLifecycleProvider", NameFactory.GERONIMO_SERVICE);
159                GBeanData gbeanData = new GBeanData(jspLifecycleName, JasperServletContextCustomizer.GBEAN_INFO);
160                gbeanData.setAttribute("holder", holder);
161    
162                try {
163                    moduleContext.addGBean(gbeanData);
164                } catch (GBeanAlreadyExistsException e) {
165                    throw new DeploymentException("Duplicate jspLifecycleProvider", e);
166                }
167    
168                webAppData.setReferencePattern("ContextCustomizer", jspLifecycleName);
169            }
170            //add listeners if possible
171            //we may need to add them in another way for tomcat
172            Object value = webAppData.getAttribute("listenerClassNames");
173            if (value instanceof Collection) {
174                ((Collection<String>) value).addAll(listenerNames);
175            }
176        }
177    
178        protected ClassFinder createJspClassFinder(WebAppType webApp, WebModule webModule, Set<String> listenerNames) throws DeploymentException {
179            List<URL> urls = getTldFiles(webApp, webModule);
180            List<Class> classes = getListenerClasses(webApp, webModule, urls, listenerNames);
181            return new ClassFinder(classes);
182        }
183    
184    
185        /**
186         * getTldFiles(): Find all the TLD files in the web module being deployed
187         *
188         * <p>Locations to search for these TLD file(s) (matches the precedence search order for TLD
189         * files per the JSP specs):
190         * <ol>
191         *      <li>web.xml <taglib> entries
192         *      <li>TLD(s) in JAR files in WEB-INF/lib
193         *      <li>TLD(s) under WEB-INF
194         *      <li>All TLD files in all META-INF(s)
195         * </ol>
196         *
197         * @param webApp    spec DD for module
198         * @param webModule module being deployed
199         * @return list of the URL(s) for the TLD files
200         * @throws DeploymentException if there's a problem finding a tld file
201         */
202        private List<URL> getTldFiles(WebAppType webApp, WebModule webModule) throws DeploymentException {
203            if (log.isDebugEnabled()) {
204                log.debug("getTldFiles( " + webApp.toString() + "," + webModule.getName() + " ): Entry");
205            }
206    
207            List<URL> tldURLs = new ArrayList<URL>();
208    
209            // 1. web.xml <taglib> entries
210            JspConfigType[] jspConfigs = webApp.getJspConfigArray();
211            for (JspConfigType jspConfig : jspConfigs) {
212                TaglibType[] taglibs = jspConfig.getTaglibArray();
213                for (TaglibType taglib : taglibs) {
214                    String uri = taglib.getTaglibUri().getStringValue().trim();
215                    String location = taglib.getTaglibLocation().getStringValue().trim();
216                    if (!location.equals("")) {
217                        if (location.startsWith("/")) {
218                            location = location.substring(1);
219                        }
220                        try {
221                            File targetFile = webModule.getEarContext().getTargetFile(createURI(location));
222                            if (targetFile!=null) {
223                                tldURLs.add(targetFile.toURL());
224                            }
225                        }
226                        catch (MalformedURLException mfe) {
227                            throw new DeploymentException("Could not locate TLD file specified in <taglib>: URI: " + uri + " Location: " + location + " " + mfe.getMessage(), mfe);
228                        }
229                        catch (URISyntaxException use) {
230                            throw new DeploymentException("Could not locate TLD file specified in <taglib>: URI: " + uri + " Location: " + location + " " + use.getMessage(), use);
231                        }
232                    }
233                }
234            }
235    
236            // 2. TLD(s) in JAR files in WEB-INF/lib
237            // 3. TLD(s) under WEB-INF
238            List<URL> tempURLs = scanModule(webModule);
239            for (URL webInfURL : tempURLs) {
240                tldURLs.add(webInfURL);
241            }
242    
243            // 4. All TLD files in all META-INF(s)
244            tempURLs.clear();
245            try {
246                Enumeration<URL> enumURLs = webModule.getEarContext().getClassLoader().getResources("META-INF");
247                while (enumURLs.hasMoreElements()) {
248                    URL enumURL = enumURLs.nextElement();
249                    tempURLs = scanDirectory(enumURL);
250                    for (URL metaInfURL : tempURLs) {
251                        tldURLs.add(metaInfURL);
252                    }
253                    tempURLs.clear();
254                }
255            }
256            catch (IOException ioe) {
257                throw new DeploymentException("Could not locate TLD files located in META-INF(s) " + ioe.getMessage(), ioe);
258            }
259    
260            log.debug("getTldFiles() Exit: URL[" + tldURLs.size() + "]: " + tldURLs.toString());
261            return tldURLs;
262        }
263    
264    
265        /**
266         * scanModule(): Scan the module being deployed for JAR files or TLD files in the WEB-INF
267         * directory
268         *
269         * @param webModule module being deployed
270         * @return list of the URL(s) for the TLD files in the module
271         * @throws DeploymentException if module cannot be scanned
272         */
273        private List<URL> scanModule(WebModule webModule) throws DeploymentException {
274            log.debug("scanModule( " + webModule.getName() + " ): Entry");
275    
276            List<URL> modURLs = new ArrayList<URL>();
277            try {
278                Enumeration<JarEntry> entries = webModule.getModuleFile().entries();
279                while (entries.hasMoreElements()) {
280                    JarEntry jarEntry = entries.nextElement();
281                    if (jarEntry.getName().startsWith("WEB-INF/") && jarEntry.getName().endsWith(".tld")) {
282                        File targetFile = webModule.getEarContext().getTargetFile(createURI(jarEntry.getName()));
283                        if (targetFile!=null) {
284                            modURLs.add(targetFile.toURL());
285                        }
286                    }
287                    if (jarEntry.getName().startsWith("WEB-INF/lib/") && jarEntry.getName().endsWith(".jar")) {
288                        File targetFile = webModule.getEarContext().getTargetFile(createURI(jarEntry.getName()));
289                        List<URL> jarUrls = scanJAR(new JarFile(targetFile), null);
290                        for (URL jarURL : jarUrls) {
291                            modURLs.add(jarURL);
292                        }
293                    }
294                }
295            }
296            catch (IOException ioe) {
297                throw new DeploymentException("Could not scan module for TLD files: " + webModule.getName() + " " + ioe.getMessage(), ioe);
298            }
299            catch (Exception e) {
300                throw new DeploymentException("Could not scan module for TLD files: " + webModule.getName() + " " + e.getMessage(), e);
301            }
302    
303            log.debug("scanModule() Exit: URL[" + modURLs.size() + "]: " + modURLs.toString());
304            return modURLs;
305        }
306    
307    
308        /**
309         * scanJAR(): Scan a JAR files looking for all TLD
310         *
311         * @param jarFile jar file to scan
312         * @param prefix  Optional prefix to limit the search to a specific subdirectory in the JAR file
313         * @return list of the URL(s) for the TLD files in the JAR file
314         * @throws DeploymentException if jar file cannot be scanned
315         */
316        private List<URL> scanJAR(JarFile jarFile, String prefix) throws DeploymentException {
317            log.debug("scanJAR( " + jarFile.getName() + " ): Entry");
318    
319            List<URL> jarURLs = new ArrayList<URL>();
320            try {
321                Enumeration<JarEntry> entries = jarFile.entries();
322                while (entries.hasMoreElements()) {
323                    JarEntry jarEntry = entries.nextElement();
324                    URL tempURL = null;
325                    if (prefix != null) {
326                        if (jarEntry.getName().endsWith(".tld") && jarEntry.getName().startsWith(prefix)) {
327                            tempURL = new URL("jar:file:" + jarFile.getName() + "!/" + jarEntry.getName());
328                        }
329                    } else {
330                        if (jarEntry.getName().endsWith(".tld")) {
331                            tempURL = new URL("jar:file:" + jarFile.getName() + "!/" + jarEntry.getName());
332                        }
333                    }
334                    if (tempURL != null) {
335                        jarURLs.add(tempURL);
336                    }
337                }
338            }
339            catch (MalformedURLException mfe) {
340                throw new DeploymentException("Could not scan JAR file for TLD files: " + jarFile.getName() + " " + mfe.getMessage(), mfe);
341            }
342            catch (Exception e) {
343                throw new DeploymentException("Could not scan JAR file for TLD files: " + jarFile.getName() + " " + e.getMessage(), e);
344            }
345    
346            log.debug("scanJAR() Exit: URL[" + jarURLs.size() + "]: " + jarURLs.toString());
347            return jarURLs;
348        }
349    
350    
351        /**
352         * scanDirectory(): Scan a directory for all TLD files
353         *
354         * @param url URL for the directory to be scanned
355         * @return list of the URL(s) for the TLD files in the directory
356         * @throws DeploymentException if directory cannot be scanned
357         */
358        private List<URL> scanDirectory(URL url) throws DeploymentException {
359            log.debug("scanDirectory( " + url.toString() + " ): Entry");
360    
361            List<URL> dirURLs = new ArrayList<URL>();
362            File directory;
363            if (url != null) {
364                if (url.toString().startsWith("jar:file:")) {
365                    try {
366                        JarURLConnection jarConnection = (JarURLConnection) url.openConnection();
367                        URL urlJC = jarConnection.getJarFileURL();
368                        URI baseURI = createURI(urlJC.toString());
369                        directory = new File(baseURI);
370                        if (directory.isDirectory()) {
371                            if (directory.canRead()) {
372                                JarFile temp = new JarFile(directory);
373                                List<URL> tempURLs = scanJAR(temp, "META-INF");
374                                for (URL jarURL : tempURLs) {
375                                    dirURLs.add(jarURL);
376                                }
377                            } else {
378                                log.warn("Cannot read JAR file: " + url.toString());
379                            }
380                        }
381                    }
382                    catch (Exception e) {
383                        throw new DeploymentException("Could not scan directory for TLD files: " + url.toString() + " " + e.getMessage(), e);
384                    }
385                } else if (url.toString().startsWith("file:")) {
386                    try {
387                        URI baseURI = createURI(url.toString());
388                        directory = new File(baseURI);
389                        if (directory.isDirectory() && directory.canRead()) {
390                            File[] children = directory.listFiles();
391                            for (File child : children) {
392                                if (child.getName().endsWith(".tld")) {
393                                    dirURLs.add(child.toURL());
394                                }
395                            }
396                        } else {
397                            log.warn("Cannot read directory: " + url.toString());
398                        }
399                    }
400                    catch (Exception e) {
401                        throw new DeploymentException("Could not scan directory for TLD files: " + url.toString() + " " + e.getMessage(), e);
402                    }
403                } else if (url.toString().startsWith("jar:")) {
404                    log.warn("URL type not accounted for: " + url.toString());
405                }
406            }
407    
408            log.debug("scanDirectory() Exit: URL[" + dirURLs.size() + "]: " + dirURLs.toString());
409            return dirURLs;
410        }
411    
412    
413        private List<Class> getListenerClasses(WebAppType webApp, WebModule webModule, List<URL> urls, Set<String> listenerNames) throws DeploymentException {
414            if (log.isDebugEnabled()) {
415                log.debug("getListenerClasses( " + webApp.toString() + "," + '\n' +
416                          webModule.getName() + " ): Entry");
417            }
418    
419            // Get the classloader from the module's EARContext
420            ClassLoader classLoader = webModule.getEarContext().getClassLoader();
421            List<Class> classes = new ArrayList<Class>();
422    
423            for (URL url : urls) {
424                parseTldFile(url, classLoader, classes, listenerNames);
425            }
426    
427            if (log.isDebugEnabled()) {
428                log.debug("getListenerClasses() Exit: Classes[" + classes.size() + "]: " + classes.toString());
429            }
430            return classes;
431        }
432    
433    
434        private void parseTldFile(URL url, ClassLoader classLoader, List<Class> classes, Set<String> listenerNames) throws DeploymentException {
435            log.debug("parseTLDFile( " + url.toString() + " ): Entry");
436    
437            try {
438                XmlObject xml = XmlBeansUtil.parse(url, null);
439                TaglibDocument tld = convertToTaglibSchema(xml);
440                TldTaglibType tl = tld.getTaglib();
441    
442                // Get all the listeners from the TLD file
443                ListenerType[] listeners = tl.getListenerArray();
444                for (ListenerType listener : listeners) {
445                    FullyQualifiedClassType cls = listener.getListenerClass();
446                    String className = cls.getStringValue().trim();
447                    listenerNames.add(className);
448                    try {
449                        Class clas = classLoader.loadClass(className);
450                        classes.add(clas);
451                    }
452                    catch (ClassNotFoundException e) {
453                        log.warn("JspModuleBuilderExtension: Could not load listener class: " + className + " mentioned in TLD file at " + url.toString());
454                    }
455                }
456    
457                // Get all the tags from the TLD file
458                TagType[] tags = tl.getTagArray();
459                for (TagType tag : tags) {
460                    FullyQualifiedClassType cls = tag.getTagClass();
461                    String className = cls.getStringValue().trim();
462                    try {
463                        Class clas = classLoader.loadClass(className);
464                        classes.add(clas);
465                    }
466                    catch (ClassNotFoundException e) {
467                        log.warn("JspModuleBuilderExtension: Could not load tag class: " + className + " mentioned in TLD file at " + url.toString());
468                    }
469                }
470            }
471            catch (XmlException xmle) {
472                throw new DeploymentException("Could not parse TLD file at " + url.toString(), xmle);
473            }
474            catch (IOException ioe) {
475                throw new DeploymentException("Could not find TLD file at " + url.toString(), ioe);
476            }
477    
478            log.debug("parseTLDFile(): Exit");
479        }
480    
481    
482        /**
483         * convertToTaglibSchema(): Convert older TLD files based on the 1.1 and 1.2 DTD or the 2.0 XSD
484         * schemas
485         *
486         * <p><strong>Note(s):</strong>
487         * <ul>
488         *      <li>Those tags from the 1.1 and 1.2 DTD that are no longer valid (e.g., jsp-version) are
489         *      removed
490         *      <li>Valid  tags from the 1.1 and 1.2 DTD are converted (e.g., tlibversion to
491         *      tlib-version)
492         *      <li>The <taglib> root and the <tag> root elements are reordered as necessary (i.e.,
493         *      description, display-name)
494         *      <li>The <rtexprvalue> tag is inserted in the &lt;attribute> tag if necessary since it was
495         *      not required to preceed <type> in 2.0 schema. Default value of false is used.
496         * </ul>
497         *
498         * @param xmlObject possibly old-style tag lib document
499         * @return converted TagLibDocument in the new shiny schema
500         * @throws XmlException if something goes horribly wrong
501         */
502        protected static TaglibDocument convertToTaglibSchema(XmlObject xmlObject) throws XmlException {
503            if (log.isDebugEnabled()) {
504                log.debug("convertToTaglibSchema( " + xmlObject.toString() + " ): Entry");
505            }
506    
507            XmlCursor cursor = xmlObject.newCursor();
508            XmlCursor moveable = xmlObject.newCursor();
509            try {
510                cursor.toStartDoc();
511                cursor.toFirstChild();
512                if (SchemaConversionUtils.JAVAEE_NAMESPACE.equals(cursor.getName().getNamespaceURI())) {
513                    log.debug("Nothing to do");
514                }
515                else if (SchemaConversionUtils.J2EE_NAMESPACE.equals(cursor.getName().getNamespaceURI())) {
516                    log.debug("Converting XSD 2.0 to 2.1 schema");
517                    SchemaConversionUtils.convertSchemaVersion(cursor, SchemaConversionUtils.JAVAEE_NAMESPACE, SCHEMA_LOCATION_URL, VERSION);
518                    cursor.toStartDoc();
519                    cursor.toChild(SchemaConversionUtils.JAVAEE_NAMESPACE, "taglib");
520                    cursor.toFirstChild();
521                    do {
522                        String name = cursor.getName().getLocalPart();
523                        if ("tag".equals(name)) {
524                            cursor.push();
525                            cursor.toFirstChild();
526                            SchemaConversionUtils.convertToDescriptionGroup(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable);
527                            do {
528                                name = cursor.getName().getLocalPart();
529                                boolean rtexprvalueFound = false;
530                                boolean typeFound = false;
531                                if ("attribute".equals(name)) {
532                                    cursor.push();
533                                    cursor.toFirstChild();
534                                    do {
535                                        name = cursor.getName().getLocalPart();
536                                        if ("rtexprvalue".equals(name)) {
537                                            rtexprvalueFound = true;
538                                        }
539                                        if ("type".equals(name)) {
540                                            typeFound = true;
541                                        }
542                                    } while (cursor.toNextSibling());
543                                    cursor.pop();
544                                    if (typeFound && !rtexprvalueFound) {
545                                        //--------------------------------------------------------------
546                                        // Handle the case where the <type> tag must now be preceded by
547                                        // the <rtexprvalue> tag in the 2.1 schema. Cases are:
548                                        // 1: Only type found:
549                                        //      We are currently positioned directly after the attribute
550                                        //      tag (via the pop) so just insert the rtexprvalue tag
551                                        //      with the default value. The tags will be properly
552                                        //      ordered below.
553                                        // 2: Both type and rtexprvalue found:
554                                        //      The tags will be properly ordered below with the
555                                        //      convertToAttributeGroup() call, so nothing to do
556                                        // 3: Only rtexprvalue found:
557                                        //      Nothing to do
558                                        // 4: Neither found:
559                                        //      Nothing to do
560                                        //--------------------------------------------------------------
561                                        cursor.push();
562                                        cursor.toFirstChild();
563                                        cursor.insertElementWithText("rtexprvalue", SchemaConversionUtils.JAVAEE_NAMESPACE, "false");
564                                        cursor.pop();
565                                    }
566                                    cursor.push();
567                                    cursor.toFirstChild();
568                                    SchemaConversionUtils.convertToTldAttribute(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable);
569                                    cursor.pop();
570                                }
571                            } while (cursor.toNextSibling());
572                            cursor.pop();
573                            // Do this conversion last after the other tags have been converted
574                            SchemaConversionUtils.convertToTldTag(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable);
575                        }
576                    } while (cursor.toNextSibling());
577                }
578                else {
579                    log.debug("Converting DTD to 2.1 schema");
580                    SchemaConversionUtils.convertToSchema(cursor, SchemaConversionUtils.JAVAEE_NAMESPACE, SCHEMA_LOCATION_URL, VERSION);
581                    cursor.toStartDoc();
582                    cursor.toChild(SchemaConversionUtils.JAVAEE_NAMESPACE, "taglib");
583                    cursor.toFirstChild();
584                    SchemaConversionUtils.convertToDescriptionGroup(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable);
585                    do {
586                        String name = cursor.getName().getLocalPart();
587                        if ("jsp-version".equals(name) ||
588                            "jspversion".equals(name) ||
589                            "info".equals(name)) {
590                            cursor.removeXmlContents();
591                            cursor.removeXml();
592                        }
593                        if ("tlibversion".equals(name)) {
594                            cursor.setName(TLIB_VERSION);
595                        }
596                        if ("tlibversion".equals(name)) {
597                            cursor.setName(TLIB_VERSION);
598                        }
599                        if ("shortname".equals(name)) {
600                            cursor.setName(SHORT_NAME);
601                        }
602                        if ("tag".equals(name)) {
603                            cursor.push();
604                            cursor.toFirstChild();
605                            SchemaConversionUtils.convertToDescriptionGroup(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable);
606                            boolean bodyContentFound = false;
607                            do {
608                                name = cursor.getName().getLocalPart();
609                                if ("tagclass".equals(name)) {
610                                    cursor.setName(TAG_CLASS);
611                                }
612                                if ("teiclass".equals(name)) {
613                                    cursor.setName(TEI_CLASS);
614                                }
615                                if ("bodycontent".equals(name)) {
616                                    cursor.setName(BODY_CONTENT);
617                                    bodyContentFound = true;
618                                }
619                                if ("body-content".equals(name)) {
620                                    bodyContentFound = true;
621                                }
622                                if ("attribute".equals(name)) {
623                                    cursor.push();
624                                    cursor.toFirstChild();
625                                    SchemaConversionUtils.convertToTldAttribute(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable);
626                                    cursor.pop();
627                                }
628                                if ("variable".equals(name)) {
629                                    cursor.push();
630                                    cursor.toFirstChild();
631                                    SchemaConversionUtils.convertToTldVariable(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable);
632                                    cursor.pop();
633                                }
634                                if ("info".equals(name)) {
635                                    cursor.removeXmlContents();
636                                    cursor.removeXml();
637                                }
638                            } while (cursor.toNextSibling());
639                            cursor.pop();
640                            if (!bodyContentFound) {
641                                //--------------------------------------------------------------
642                                // Handle the case where the <body-content> tag is missing. We
643                                // are currently positioned directly after the <tag> attribute
644                                // (via the pop) so just insert the <body-content> tag with the
645                                // default value. The tags will be properly ordered below.
646                                //--------------------------------------------------------------
647                                cursor.push();
648                                cursor.toFirstChild();
649                                cursor.insertElementWithText("body-content", SchemaConversionUtils.JAVAEE_NAMESPACE, "scriptless");
650                                cursor.pop();
651                            }
652                            // Do this conversion last after the other tags have been converted
653                            cursor.push();
654                            cursor.toFirstChild();
655                            SchemaConversionUtils.convertToTldTag(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable);
656                            cursor.pop();
657                        }
658                        if ("validator".equals(name)) {
659                            cursor.push();
660                            cursor.toFirstChild();
661                            SchemaConversionUtils.convertToTldValidator(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable);
662                            cursor.pop();
663                        }
664                    } while (cursor.toNextSibling());
665                }
666            }
667            finally {
668                cursor.dispose();
669                moveable.dispose();
670            }
671            XmlObject result = xmlObject.changeType(TaglibDocument.type);
672            if (result != null) {
673                try {
674                    XmlBeansUtil.validateDD(result);
675                } catch (XmlException e) {
676                    log.warn("Invalid transformed taglib", e);
677                }
678                if (log.isDebugEnabled()) { 
679                    log.debug("convertToTaglibSchema( " + result.toString() + " ): Exit 1");
680                }
681                return (TaglibDocument) result;
682            }
683            try {
684                XmlBeansUtil.validateDD(xmlObject);
685            } catch (XmlException e) {
686                log.warn("Invalid transformed taglib", e);
687            }
688            if (log.isDebugEnabled()) { 
689                log.debug("convertToTaglibSchema( " + xmlObject.toString() + " ): Exit 2");
690            }
691            return (TaglibDocument) xmlObject;
692        }
693    
694    
695        private URI createURI(String path) throws URISyntaxException {
696            path = path.replaceAll(" ", "%20");
697            return new URI(path);
698        }
699    
700        public static final GBeanInfo GBEAN_INFO;
701    
702        static {
703            GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic(JspModuleBuilderExtension.class, NameFactory.MODULE_BUILDER);
704            infoBuilder.addAttribute("defaultEnvironment", Environment.class, true, true);
705            infoBuilder.addReference("NamingBuilders", NamingBuilder.class, NameFactory.MODULE_BUILDER);
706    
707            infoBuilder.setConstructor(new String[]{
708                    "defaultEnvironment",
709                    "NamingBuilders"});
710            GBEAN_INFO = infoBuilder.getBeanInfo();
711        }
712    
713        public static GBeanInfo getGBeanInfo() {
714            return GBEAN_INFO;
715        }
716    }