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}