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