    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    }