001// Copyright 2006-2013 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
015package org.apache.tapestry5.internal.services;
016
017import org.apache.tapestry5.commons.Resource;
018import org.apache.tapestry5.http.services.Context;
019import org.apache.tapestry5.ioc.internal.util.AbstractResource;
020
021import java.io.File;
022import java.net.MalformedURLException;
023import java.net.URL;
024
025/**
026 * A resource stored with in the web application context.
027 */
028public 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
067            if (!urlResolved)
068            {
069                resolveURL();
070            }
071
072            return url;
073
074        } finally
075        {
076            releaseReadLock();
077        }
078    }
079
080    private void resolveURL()
081    {
082        try
083        {
084            upgradeReadLockToWriteLock();
085
086            // Race condition on the write lock:
087            if (urlResolved)
088            {
089                return;
090            }
091
092            // This is so easy to screw up; ClassLoader.getResource() doesn't want a leading slash,
093            // and HttpServletContext.getResource() does. This is what I mean when I say that
094            // a framework is an accumulation of the combined experience of many users and developers.
095
096            String contextPath = "/" + getPath();
097
098            // Always prefer the actual file to the URL.  This is critical for templates to
099            // reload inside Tomcat.
100
101            File file = context.getRealFile(contextPath);
102
103            if (file != null && file.exists())
104            {
105                try
106                {
107                    url = file.toURI().toURL();
108
109                    validateURL(url);
110
111                    urlResolved = true;
112
113                    return;
114                } catch (MalformedURLException ex)
115                {
116                    throw new RuntimeException(ex);
117                }
118            }
119
120            // But, when packaged inside a WAR or JAR, the File will not be available, so use whatever
121            // URL we get ... but reloading won't work.
122
123            url = context.getResource(contextPath);
124
125            validateURL(url);
126
127            urlResolved = true;
128
129
130        } finally
131        {
132            downgradeWriteLockToReadLock();
133        }
134    }
135
136    @Override
137    public int hashCode()
138    {
139        return PRIME * context.hashCode() + getPath().hashCode();
140    }
141
142    @Override
143    public boolean equals(Object obj)
144    {
145        if (this == obj) return true;
146        if (obj == null) return false;
147        if (getClass() != obj.getClass()) return false;
148
149        final ContextResource other = (ContextResource) obj;
150
151        return context == other.context && getPath().equals(other.getPath());
152    }
153
154}