001 /*
002 * The contents of this file are subject to the terms
003 * of the Common Development and Distribution License
004 * (the "License"). You may not use this file except
005 * in compliance with the License.
006 *
007 * You can obtain a copy of the license at
008 * http://www.opensource.org/licenses/cddl1.php
009 * See the License for the specific language governing
010 * permissions and limitations under the License.
011 */
012
013 /*
014 * RuntimeDelegate.java
015 *
016 * Created on November 15, 2007, 4:00 PM
017 *
018 */
019
020 package javax.ws.rs.ext;
021
022 import java.lang.reflect.ReflectPermission;
023 import java.net.URL;
024 import javax.ws.rs.core.Application;
025 import javax.ws.rs.core.Response.ResponseBuilder;
026 import javax.ws.rs.core.Variant.VariantListBuilder;
027 import javax.ws.rs.core.UriBuilder;
028
029 /**
030 * Implementations of JAX-RS provide a concrete subclass of RuntimeDelegate and
031 * various JAX-RS API methods defer to methods of RuntimeDelegate for their
032 * functionality. Regular users of JAX-RS are not expected to use this class
033 * directly and overriding an implementation of this class with a user supplied
034 * subclass may cause unexpected behavior.
035 *
036 */
037 public abstract class RuntimeDelegate {
038
039 public static final String JAXRS_RUNTIME_DELEGATE_PROPERTY
040 = "javax.ws.rs.ext.RuntimeDelegate";
041 private static final String JAXRS_DEFAULT_RUNTIME_DELEGATE
042 = "com.sun.ws.rs.ext.RuntimeDelegateImpl";
043
044 private static ReflectPermission rp = new ReflectPermission("suppressAccessChecks");
045
046 protected RuntimeDelegate() {
047 }
048
049 private static volatile RuntimeDelegate rd;
050
051 /**
052 * Obtain a RuntimeDelegate instance. If an instance had not already been
053 * created and set via {@link #setInstance}, the first invocation will
054 * create an instance which will then be cached for future use.
055 *
056 * <p>
057 * The algorithm used to locate the RuntimeDelegate subclass to use consists
058 * of the following steps:
059 * <p>
060 * <ul>
061 * <li>
062 * If a resource with the name of
063 * <code>META-INF/services/javax.ws.rs.ext.RuntimeDelegate</code>
064 * exists, then its first line, if present, is used as the UTF-8 encoded
065 * name of the implementation class.
066 * </li>
067 * <li>
068 * If the $java.home/lib/jaxrs.properties file exists and it is readable by
069 * the <code>java.util.Properties.load(InputStream)</code> method and it contains
070 * an entry whose key is <code>javax.ws.rs.ext.RuntimeDelegate</code>, then the value of
071 * that entry is used as the name of the implementation class.
072 * </li>
073 * <li>
074 * If a system property with the name <code>javax.ws.rs.ext.RuntimeDelegate</code>
075 * is defined, then its value is used as the name of the implementation class.
076 * </li>
077 * <li>
078 * Finally, a default implementation class name is used.
079 * </li>
080 * </ul>
081 *
082 * @return an instance of RuntimeDelegate
083 */
084 public static RuntimeDelegate getInstance() {
085 // Double-check idiom for lazy initialization of fields.
086 RuntimeDelegate result = rd;
087 if (result == null) { // First check (no locking)
088 synchronized(RuntimeDelegate.class) {
089 result = rd;
090 if (result == null) { // Second check (with locking)
091 rd = result = findDelegate();
092 }
093 }
094 }
095 return result;
096 }
097
098 /**
099 * Obtain a RuntimeDelegate instance using the method described in
100 * {@link #getInstance}.
101 * @return an instance of RuntimeDelegate
102 */
103 private static RuntimeDelegate findDelegate() {
104 try {
105 Object delegate =
106 FactoryFinder.find(JAXRS_RUNTIME_DELEGATE_PROPERTY,
107 JAXRS_DEFAULT_RUNTIME_DELEGATE);
108 if (!(delegate instanceof RuntimeDelegate)) {
109 Class pClass = RuntimeDelegate.class;
110 String classnameAsResource = pClass.getName().replace('.', '/') + ".class";
111 ClassLoader loader = pClass.getClassLoader();
112 if(loader == null) {
113 loader = ClassLoader.getSystemClassLoader();
114 }
115 URL targetTypeURL = loader.getResource(classnameAsResource);
116 throw new LinkageError("ClassCastException: attempting to cast" +
117 delegate.getClass().getClassLoader().getResource(classnameAsResource) +
118 "to" + targetTypeURL.toString() );
119 }
120 return (RuntimeDelegate) delegate;
121 } catch (Exception ex) {
122 throw new RuntimeException(ex);
123 }
124 }
125
126 /**
127 * Set the runtime delegate that will be used by JAX-RS classes. If this method
128 * is not called prior to {@link #getInstance} then an implementation will
129 * be sought as described in {@link #getInstance}.
130 * @param rd the runtime delegate instance
131 * @throws SecurityException if there is a security manager and the permission
132 * ReflectPermission("suppressAccessChecks") has not been granted.
133 */
134 public static void setInstance(RuntimeDelegate rd) throws SecurityException {
135 SecurityManager security = System.getSecurityManager();
136 if (security != null) {
137 security.checkPermission(rp);
138 }
139 synchronized(RuntimeDelegate.class) {
140 RuntimeDelegate.rd = rd;
141 }
142 }
143
144 /**
145 * Create a new instance of a {@link javax.ws.rs.core.UriBuilder}.
146 * @return new UriBuilder instance
147 * @see javax.ws.rs.core.UriBuilder
148 */
149 public abstract UriBuilder createUriBuilder();
150
151 /**
152 * Create a new instance of a {@link javax.ws.rs.core.Response.ResponseBuilder}.
153 * @return new ResponseBuilder instance
154 * @see javax.ws.rs.core.Response.ResponseBuilder
155 */
156 public abstract ResponseBuilder createResponseBuilder();
157
158 /**
159 * Create a new instance of a {@link javax.ws.rs.core.Variant.VariantListBuilder}.
160 *
161 * @return new VariantListBuilder instance
162 * @see javax.ws.rs.core.Variant.VariantListBuilder
163 */
164 public abstract VariantListBuilder createVariantListBuilder();
165
166 /**
167 * Create a configured instance of the supplied endpoint type. How the
168 * returned endpoint instance is published is dependent on the type of
169 * endpoint.
170 * @param application the application configuration
171 * @param endpointType the type of endpoint instance to be created.
172 * @return a configured instance of the requested type.
173 * @throws java.lang.IllegalArgumentException if application is null or the
174 * requested endpoint type is not supported.
175 * @throws java.lang.UnsupportedOperationException if the implementation
176 * supports no endpoint types.
177 */
178 public abstract <T> T createEndpoint(Application application,
179 Class<T> endpointType) throws IllegalArgumentException, UnsupportedOperationException;
180
181 /**
182 * Obtain an instance of a HeaderDelegate for the supplied class. An
183 * implementation is required to support the following values for type:
184 * {@link javax.ws.rs.core.Cookie}, {@link javax.ws.rs.core.CacheControl},
185 * {@link javax.ws.rs.core.EntityTag}, {@link javax.ws.rs.core.NewCookie},
186 * {@link javax.ws.rs.core.MediaType} and {@code java.util.Date}.
187 * @param type the class of the header
188 * @return an instance of HeaderDelegate for the supplied type
189 * @throws java.lang.IllegalArgumentException if type is null
190 */
191 public abstract <T> HeaderDelegate<T> createHeaderDelegate(Class<T> type);
192
193 /**
194 * Defines the contract for a delegate that is responsible for
195 * converting between the String form of a HTTP header and
196 * the corresponding JAX-RS type <code>T</code>.
197 * @param T a JAX-RS type that corresponds to the value of a HTTP header
198 */
199 public static interface HeaderDelegate<T> {
200 /**
201 * Parse the supplied value and create an instance of <code>T</code>.
202 * @param value the string value
203 * @return the newly created instance of <code>T</code>
204 * @throws IllegalArgumentException if the supplied string cannot be
205 * parsed or is null
206 */
207 public T fromString(String value) throws IllegalArgumentException;
208
209 /**
210 * Convert the supplied value to a String.
211 * @param value the value of type <code>T</code>
212 * @return a String representation of the value
213 * @throws IllegalArgumentException if the supplied object cannot be
214 * serialized or is null
215 */
216 public String toString(T value);
217 }
218 }