001    // Copyright 2006, 2008, 2010, 2012 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.tapestry5.internal.services;
016    
017    import org.apache.tapestry5.ioc.Resource;
018    import org.apache.tapestry5.ioc.internal.util.AbstractResource;
019    import org.apache.tapestry5.services.Context;
020    
021    import java.io.File;
022    import java.net.MalformedURLException;
023    import java.net.URL;
024    
025    /**
026     * A resource stored with in the web application context.
027     */
028    public class ContextResource extends AbstractResource
029    {
030        private static final int PRIME = 37;
031    
032        private final Context context;
033    
034        // Guarded by lock
035        private URL url;
036    
037        // Guarded by lock
038        private boolean urlResolved;
039    
040        public ContextResource(Context context, String path)
041        {
042            super(path);
043    
044            assert context != null;
045    
046            this.context = context;
047        }
048    
049        @Override
050        public String toString()
051        {
052            return String.format("context:%s", getPath());
053        }
054    
055        @Override
056        protected Resource newResource(String path)
057        {
058            return new ContextResource(context, path);
059        }
060    
061        public URL toURL()
062        {
063            try
064            {
065                acquireReadLock();
066                if (!urlResolved)
067                {
068                    resolveURL();
069                }
070    
071                return url;
072    
073            } finally
074            {
075                releaseReadLock();
076            }
077        }
078    
079        private void resolveURL()
080        {
081            try
082            {
083                upgradeReadLockToWriteLock();
084    
085                // Race condition on the write lock:
086                if (urlResolved)
087                {
088                    return;
089                }
090    
091                // This is so easy to screw up; ClassLoader.getResource() doesn't want a leading slash,
092                // and HttpServletContext.getResource() does. This is what I mean when I say that
093                // a framework is an accumulation of the combined experience of many users and developers.
094    
095                String contextPath = "/" + getPath();
096    
097                // Always prefer the actual file to the URL.  This is critical for templates to
098                // reload inside Tomcat.
099    
100                File file = context.getRealFile(contextPath);
101    
102                if (file != null && file.exists())
103                {
104                    try
105                    {
106                        url = file.toURI().toURL();
107                        urlResolved = true;
108                        return;
109                    } catch (MalformedURLException ex)
110                    {
111                        throw new RuntimeException(ex);
112                    }
113                }
114    
115                // But, when packaged inside a WAR or JAR, the File will not be available, so use whatever
116                // URL we get ... but reloading won't work.
117    
118                url = context.getResource(contextPath);
119                urlResolved = true;
120    
121            } finally
122            {
123                downgradeWriteLockToReadLock();
124            }
125        }
126    
127        @Override
128        public int hashCode()
129        {
130            return PRIME * context.hashCode() + getPath().hashCode();
131        }
132    
133        @Override
134        public boolean equals(Object obj)
135        {
136            if (this == obj) return true;
137            if (obj == null) return false;
138            if (getClass() != obj.getClass()) return false;
139    
140            final ContextResource other = (ContextResource) obj;
141    
142            return context == other.context && getPath().equals(other.getPath());
143        }
144    
145    }