    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.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
    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}