001    // Copyright 2004, 2005 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry.resolver;
016    
017    import org.apache.commons.logging.Log;
018    import org.apache.hivemind.ApplicationRuntimeException;
019    import org.apache.hivemind.ClassResolver;
020    import org.apache.hivemind.Location;
021    import org.apache.hivemind.Resource;
022    import org.apache.hivemind.impl.LocationImpl;
023    import org.apache.hivemind.util.ClasspathResource;
024    import org.apache.tapestry.INamespace;
025    import org.apache.tapestry.IRequestCycle;
026    import org.apache.tapestry.services.ClassFinder;
027    import org.apache.tapestry.spec.ComponentSpecification;
028    import org.apache.tapestry.spec.IComponentSpecification;
029    
030    /**
031     * Utility class that understands the rules of component types (which may optionally have a library
032     * prefix) and can resolve the type to a {@link org.apache.tapestry.INamespace}and a
033     * {@link org.apache.tapestry.spec.IComponentSpecification}.
034     * <p>
035     * Like {@link org.apache.tapestry.resolver.PageSpecificationResolver}, if the component is not
036     * defined explicitly in the namespace, a search may occur: Performs the tricky work of resolving a
037     * page name to a page specification. The search for pages in the application namespace is the most
038     * complicated, since Tapestry searches for pages that aren't explicitly defined in the application
039     * specification. The search, based on the <i>simple-name </i> of the page, goes as follows:
040     * <ul>
041     * <li>As declared in the application specification
042     * <li><i>type</i>.jwc in the same folder as the application specification
043     * <li><i>type</i> jwc in the WEB-INF/ <i>servlet-name </i> directory of the context root
044     * <li><i>type</i>.jwc in WEB-INF
045     * <li><i>type</i>.jwc in the application root (within the context root)
046     * <li>By searching the framework namespace
047     * <li>By searching for a named class file within the org.apache.tapestry.component-class-packages
048     * property (defined within the namespace)
049     * </ul>
050     * 
051     * The search for components in library namespaces is more abbreviated:
052     * <ul>
053     * <li>As declared in the library specification
054     * <li><i>type </i>.jwc in the same folder as the library specification
055     * <li>By searching the framework namespace
056     * </ul>
057     * 
058     * @since 3.0
059     */
060    
061    public class ComponentSpecificationResolverImpl extends AbstractSpecificationResolver implements
062            ComponentSpecificationResolver
063    {
064        /** Set by container. */
065        private Log _log;
066    
067        /** Set by resolve(). */
068        private String _type;
069    
070        private ClassFinder _classFinder;
071    
072        private ClassResolver _classResolver;
073    
074        protected void reset()
075        {
076            _type = null;
077            
078            super.reset();
079        }
080        
081        /**
082         * Passed the namespace of a container (to resolve the type in) and the type to resolve,
083         * performs the processing. A "bare type" (without a library prefix) may be in the
084         * containerNamespace, or the framework namespace (a search occurs in that order).
085         * 
086         * @param cycle
087         *            current request cycle
088         * @param containerNamespace
089         *            namespace that may contain a library referenced in the type
090         * @param type
091         *            the component specification to find, either a simple name, or prefixed with a
092         *            library id (defined for the container namespace)
093         * @see #getNamespace()
094         * @see #getSpecification()
095         */
096    
097        public void resolve(IRequestCycle cycle, INamespace containerNamespace, String type, Location location)
098        {
099            int colonx = type.indexOf(':');
100    
101            if (colonx > 0)
102            {
103                String libraryId = type.substring(0, colonx);
104                String simpleType = type.substring(colonx + 1);
105    
106                resolve(cycle, containerNamespace, libraryId, simpleType, location);
107            }
108            else
109                resolve(cycle, containerNamespace, null, type, location);
110            
111            IComponentSpecification spec = getSpecification();
112            
113            if (spec.isDeprecated())
114                _log.warn(ResolverMessages.componentIsDeprecated(type, location));
115        }
116    
117        /**
118         * Like
119         * {@link #resolve(org.apache.tapestry.IRequestCycle, org.apache.tapestry.INamespace, java.lang.String, Location)},
120         * but used when the type has already been parsed into a library id and a simple type.
121         * 
122         * @param cycle
123         *            current request cycle
124         * @param containerNamespace
125         *            namespace that may contain a library referenced in the type
126         * @param libraryId
127         *            the library id within the container namespace, or null
128         * @param type
129         *            the component specification to find as a simple name (without a library prefix)
130         * @param location
131         *            of reference to be resolved
132         * @throws ApplicationRuntimeException
133         *             if the type cannot be resolved
134         */
135    
136        public void resolve(IRequestCycle cycle, INamespace containerNamespace, String libraryId,
137                String type, Location location)
138        {
139            reset();
140            _type = type;
141    
142            INamespace namespace;
143            try
144            {
145                namespace = findNamespaceForId(containerNamespace, libraryId);
146            }
147            catch (ApplicationRuntimeException e)
148            {
149                throw new ApplicationRuntimeException(e.getMessage(), location, e);
150            }
151    
152            setNamespace(namespace);
153    
154            if (namespace.containsComponentType(type))
155            {
156                setSpecification(namespace.getComponentSpecification(type));
157                return;
158            }
159    
160            IComponentSpecification spec = searchForComponent(cycle);
161    
162            // If not found after search, check to see if it's in
163            // the framework instead.
164    
165            if (spec == null)
166            {
167                throw new ApplicationRuntimeException(ResolverMessages.noSuchComponentType(
168                        type,
169                        namespace), location, null);
170    
171            }
172    
173            setSpecification(spec);
174    
175            // Install it into the namespace, to short-circuit any future search.
176    
177            install();
178        }
179    
180        // Hm. This could maybe go elsewhere, say onto ISpecificationSource
181    
182        private IComponentSpecification searchForComponent(IRequestCycle cycle)
183        {
184            IComponentSpecification result = null;
185            INamespace namespace = getNamespace();
186    
187            if (_log.isDebugEnabled())
188                _log.debug(ResolverMessages.resolvingComponent(_type, namespace));
189            
190            String expectedName = _type + ".jwc";
191            Resource namespaceLocation = namespace.getSpecificationLocation();
192            
193            // Look for appropriate file in same folder as the library (or application)
194            // specificaiton.
195            
196            result = check(namespaceLocation.getRelativeResource(expectedName));
197            
198            if (result != null)
199                return result;
200    
201            if (namespace.isApplicationNamespace()) {
202                
203                // The application namespace gets some extra searching.
204                
205                result = check(getWebInfAppLocation().getRelativeResource(expectedName));
206    
207                if (result == null)
208                    result = check(getWebInfLocation().getRelativeResource(expectedName));
209    
210                if (result == null)
211                    result = check((getContextRoot().getRelativeResource(expectedName)));
212                
213                if (result != null)
214                    return result;
215            }
216            
217            result = getDelegate().findComponentSpecification(cycle, namespace, _type);
218            if (result != null)
219                return result;
220            
221            result = searchForComponentClass(namespace, _type);
222    
223            if (result != null)
224                return result;
225    
226            // Not in the library or app spec; does it match a component
227            // provided by the Framework?
228            
229            INamespace framework = getSpecificationSource().getFrameworkNamespace();
230            
231            if (framework.containsComponentType(_type))
232                return framework.getComponentSpecification(_type);
233            
234            return null;
235        }
236    
237        IComponentSpecification searchForComponentClass(INamespace namespace, String type)
238        {
239            String packages = namespace.getPropertyValue("org.apache.tapestry.component-class-packages");
240    
241            String className = type.replace('/', '.');
242    
243            Class componentClass = _classFinder.findClass(packages, className);
244            if (componentClass == null)
245                return null;
246    
247            IComponentSpecification spec = new ComponentSpecification();
248    
249            Resource namespaceResource = namespace.getSpecificationLocation();
250            Resource componentResource = namespaceResource.getRelativeResource(type + ".jwc");
251    
252            // try classpath relative if namespace relative doesn't resolve
253    
254            if (componentResource.getResourceURL() == null) {
255                
256                componentResource = new ClasspathResource(_classResolver, componentClass.getName().replace('.', '/'));
257            }
258    
259            Location location = new LocationImpl(componentResource);
260    
261            spec.setLocation(location);
262            spec.setSpecificationLocation(componentResource);
263            spec.setComponentClassName(componentClass.getName());
264    
265            return spec;
266        }
267    
268        private IComponentSpecification check(Resource resource)
269        {
270            if (_log.isDebugEnabled())
271                _log.debug("Checking: " + resource);
272    
273            if (resource.getResourceURL() == null)
274                return null;
275    
276            return getSpecificationSource().getComponentSpecification(resource);
277        }
278        
279        private void install()
280        {
281            INamespace namespace = getNamespace();
282            IComponentSpecification specification = getSpecification();
283            
284            if (_log.isDebugEnabled())
285                _log.debug(ResolverMessages.installingComponent(_type, namespace, specification));
286            
287            namespace.installComponentSpecification(_type, specification);
288        }
289    
290        public String getType()
291        {
292            return _type;
293        }
294    
295        public void setLog(Log log)
296        {
297            _log = log;
298        }
299    
300        public void setClassFinder(ClassFinder classFinder)
301        {
302            _classFinder = classFinder;
303        }
304    
305        public void setClassResolver(ClassResolver classResolver)
306        {
307            _classResolver = classResolver;
308        }
309    }