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
015package org.apache.tapestry5.internal.services;
016
017import org.apache.tapestry5.ioc.Resource;
018import org.apache.tapestry5.ioc.internal.util.AbstractResource;
019import org.apache.tapestry5.services.Context;
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            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}