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.services.impl;
016    
017    import java.util.HashMap;
018    import java.util.Map;
019    
020    import org.apache.hivemind.ApplicationRuntimeException;
021    import org.apache.hivemind.ClassResolver;
022    import org.apache.hivemind.Resource;
023    import org.apache.hivemind.util.ClasspathResource;
024    import org.apache.tapestry.INamespace;
025    import org.apache.tapestry.asset.AssetSource;
026    import org.apache.tapestry.engine.ISpecificationSource;
027    import org.apache.tapestry.engine.Namespace;
028    import org.apache.tapestry.event.ReportStatusEvent;
029    import org.apache.tapestry.event.ReportStatusListener;
030    import org.apache.tapestry.event.ResetEventListener;
031    import org.apache.tapestry.parse.ISpecificationParser;
032    import org.apache.tapestry.services.NamespaceResources;
033    import org.apache.tapestry.spec.IApplicationSpecification;
034    import org.apache.tapestry.spec.IComponentSpecification;
035    import org.apache.tapestry.spec.ILibrarySpecification;
036    import org.apache.tapestry.spec.LibrarySpecification;
037    import org.apache.tapestry.util.xml.DocumentParseException;
038    
039    /**
040     * Default implementation of {@link ISpecificationSource} that expects to use the normal class
041     * loader to locate component specifications from within the classpath.
042     * <p>
043     * Caches specifications in memory forever, or until {@link #resetEventDidOccur()} is invoked.
044     * 
045     * @author Howard Lewis Ship
046     */
047    public class SpecificationSourceImpl implements ISpecificationSource, ResetEventListener,
048            ReportStatusListener
049    {
050        private ClassResolver _classResolver;
051    
052        private IApplicationSpecification _specification;
053    
054        private ISpecificationParser _parser;
055    
056        private NamespaceResources _namespaceResources;
057    
058        private INamespace _applicationNamespace;
059    
060        private INamespace _frameworkNamespace;
061    
062        private AssetSource _assetSource;
063    
064        private String _serviceId;
065    
066        /**
067         * Contains previously parsed component specifications.
068         */
069    
070        private Map _componentCache = new HashMap();
071    
072        /**
073         * Contains previously parsed page specifications.
074         * 
075         * @since 2.2
076         */
077    
078        private Map _pageCache = new HashMap();
079    
080        /**
081         * Contains previously parsed library specifications, keyed on specification resource path.
082         * 
083         * @since 2.2
084         */
085    
086        private Map _libraryCache = new HashMap();
087    
088        /**
089         * Contains {@link INamespace} instances, keyed on id (which will be null for the application
090         * specification).
091         */
092    
093        private Map _namespaceCache = new HashMap();
094    
095        public void reportStatus(ReportStatusEvent event)
096        {
097            event.title(_serviceId);
098    
099            event.property("page specification count", _pageCache.size());
100            event.collection("page specifications", _pageCache.keySet());
101            event.property("component specification count", _componentCache.size());
102            event.collection("component specifications", _componentCache.keySet());
103        }
104    
105        public void initializeService()
106        {
107            _namespaceResources = new NamespaceResourcesImpl(this, _assetSource);
108        }
109    
110        /**
111         * Clears the specification cache. This is used during debugging.
112         */
113    
114        public synchronized void resetEventDidOccur()
115        {
116            _componentCache.clear();
117            _pageCache.clear();
118            _libraryCache.clear();
119            _namespaceCache.clear();
120    
121            _applicationNamespace = null;
122            _frameworkNamespace = null;
123        }
124    
125        protected IComponentSpecification parseSpecification(Resource resource, boolean asPage)
126        {
127            IComponentSpecification result = null;
128    
129            try
130            {
131                if (asPage)
132                    result = _parser.parsePageSpecification(resource);
133                else
134                    result = _parser.parseComponentSpecification(resource);
135            }
136            catch (DocumentParseException ex)
137            {
138                throw new ApplicationRuntimeException(
139                        ImplMessages.unableToParseSpecification(resource), ex);
140            }
141    
142            return result;
143        }
144    
145        protected ILibrarySpecification parseLibrarySpecification(Resource resource)
146        {
147            try
148            {
149                return _parser.parseLibrarySpecification(resource);
150            }
151            catch (DocumentParseException ex)
152            {
153                throw new ApplicationRuntimeException(
154                        ImplMessages.unableToParseSpecification(resource), ex);
155            }
156    
157        }
158    
159        /**
160         * Gets a component specification.
161         * 
162         * @param resourceLocation
163         *            the complete resource path to the specification.
164         * @throws ApplicationRuntimeException
165         *             if the specification cannot be obtained.
166         */
167    
168        public synchronized IComponentSpecification getComponentSpecification(Resource resourceLocation)
169        {
170            IComponentSpecification result = (IComponentSpecification) _componentCache
171                    .get(resourceLocation);
172    
173            if (result == null)
174            {
175                result = parseSpecification(resourceLocation, false);
176    
177                _componentCache.put(resourceLocation, result);
178            }
179    
180            return result;
181        }
182    
183        public synchronized IComponentSpecification getPageSpecification(Resource resourceLocation)
184        {
185            IComponentSpecification result = (IComponentSpecification) _pageCache.get(resourceLocation);
186    
187            if (result == null)
188            {
189                result = parseSpecification(resourceLocation, true);
190    
191                _pageCache.put(resourceLocation, result);
192            }
193    
194            return result;
195        }
196    
197        public synchronized ILibrarySpecification getLibrarySpecification(Resource resourceLocation)
198        {
199            ILibrarySpecification result = (LibrarySpecification) _libraryCache.get(resourceLocation);
200    
201            if (result == null)
202            {
203                result = parseLibrarySpecification(resourceLocation);
204                _libraryCache.put(resourceLocation, result);
205            }
206    
207            return result;
208        }
209    
210        public synchronized INamespace getApplicationNamespace()
211        {
212            if (_applicationNamespace == null)
213                _applicationNamespace = new Namespace(null, null, _specification, _namespaceResources);
214    
215            return _applicationNamespace;
216        }
217    
218        public synchronized INamespace getFrameworkNamespace()
219        {
220            if (_frameworkNamespace == null)
221            {
222                Resource resource = new ClasspathResource(_classResolver,
223                        "/org/apache/tapestry/Framework.library");
224    
225                ILibrarySpecification ls = getLibrarySpecification(resource);
226    
227                _frameworkNamespace = new Namespace(INamespace.FRAMEWORK_NAMESPACE, null, ls,
228                        _namespaceResources);
229            }
230    
231            return _frameworkNamespace;
232        }
233    
234        public void setParser(ISpecificationParser parser)
235        {
236            _parser = parser;
237        }
238    
239        public void setClassResolver(ClassResolver resolver)
240        {
241            _classResolver = resolver;
242        }
243    
244        public void setSpecification(IApplicationSpecification specification)
245        {
246            _specification = specification;
247        }
248    
249        public void setAssetSource(AssetSource assetSource)
250        {
251            _assetSource = assetSource;
252        }
253    
254        public void setServiceId(String serviceId)
255        {
256            _serviceId = serviceId;
257        }
258    
259    }