/*
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the "License"). You may not use this file except
* in compliance with the License.
*
* You can obtain a copy of the license at
* http://www.opensource.org/licenses/cddl1.php
* See the License for the specific language governing
* permissions and limitations under the License.
*/
/*
* RuntimeDelegate.java
*
* Created on November 15, 2007, 4:00 PM
*
*/
package javax.ws.rs.ext;
import java.lang.reflect.ReflectPermission;
import java.net.URL;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Variant.VariantListBuilder;
import javax.ws.rs.core.UriBuilder;
/**
* Implementations of JAX-RS provide a concrete subclass of RuntimeDelegate and
* various JAX-RS API methods defer to methods of RuntimeDelegate for their
* functionality. Regular users of JAX-RS are not expected to use this class
* directly and overriding an implementation of this class with a user supplied
* subclass may cause unexpected behavior.
*
*/
public abstract class RuntimeDelegate {
public static final String JAXRS_RUNTIME_DELEGATE_PROPERTY
= "javax.ws.rs.ext.RuntimeDelegate";
private static final String JAXRS_DEFAULT_RUNTIME_DELEGATE
= "com.sun.ws.rs.ext.RuntimeDelegateImpl";
private static ReflectPermission rp = new ReflectPermission("suppressAccessChecks");
protected RuntimeDelegate() {
}
private static volatile RuntimeDelegate rd;
/**
* Obtain a RuntimeDelegate instance. If an instance had not already been
* created and set via {@link #setInstance}, the first invocation will
* create an instance which will then be cached for future use.
*
* <p>
* The algorithm used to locate the RuntimeDelegate subclass to use consists
* of the following steps:
* <p>
* <ul>
* <li>
* If a resource with the name of
* <code>META-INF/services/javax.ws.rs.ext.RuntimeDelegate</code>
* exists, then its first line, if present, is used as the UTF-8 encoded
* name of the implementation class.
* </li>
* <li>
* If the $java.home/lib/jaxrs.properties file exists and it is readable by
* the <code>java.util.Properties.load(InputStream)</code> method and it contains
* an entry whose key is <code>javax.ws.rs.ext.RuntimeDelegate</code>, then the value of
* that entry is used as the name of the implementation class.
* </li>
* <li>
* If a system property with the name <code>javax.ws.rs.ext.RuntimeDelegate</code>
* is defined, then its value is used as the name of the implementation class.
* </li>
* <li>
* Finally, a default implementation class name is used.
* </li>
* </ul>
*
* @return an instance of RuntimeDelegate
*/
public static RuntimeDelegate getInstance() {
// Double-check idiom for lazy initialization of fields.
RuntimeDelegate result = rd;
if (result == null) { // First check (no locking)
synchronized(RuntimeDelegate.class) {
result = rd;
if (result == null) { // Second check (with locking)
rd = result = findDelegate();
}
}
}
return result;
}
/**
* Obtain a RuntimeDelegate instance using the method described in
* {@link #getInstance}.
* @return an instance of RuntimeDelegate
*/
private static RuntimeDelegate findDelegate() {
try {
Object delegate =
FactoryFinder.find(JAXRS_RUNTIME_DELEGATE_PROPERTY,
JAXRS_DEFAULT_RUNTIME_DELEGATE);
if (!(delegate instanceof RuntimeDelegate)) {
Class pClass = RuntimeDelegate.class;
String classnameAsResource = pClass.getName().replace('.', '/') + ".class";
ClassLoader loader = pClass.getClassLoader();
if(loader == null) {
loader = ClassLoader.getSystemClassLoader();
}
URL targetTypeURL = loader.getResource(classnameAsResource);
throw new LinkageError("ClassCastException: attempting to cast" +
delegate.getClass().getClassLoader().getResource(classnameAsResource) +
"to" + targetTypeURL.toString() );
}
return (RuntimeDelegate) delegate;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
/**
* Set the runtime delegate that will be used by JAX-RS classes. If this method
* is not called prior to {@link #getInstance} then an implementation will
* be sought as described in {@link #getInstance}.
* @param rd the runtime delegate instance
* @throws SecurityException if there is a security manager and the permission
* ReflectPermission("suppressAccessChecks") has not been granted.
*/
public static void setInstance(RuntimeDelegate rd) throws SecurityException {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPermission(rp);
}
synchronized(RuntimeDelegate.class) {
RuntimeDelegate.rd = rd;
}
}
/**
* Create a new instance of a {@link javax.ws.rs.core.UriBuilder}.
* @return new UriBuilder instance
* @see javax.ws.rs.core.UriBuilder
*/
public abstract UriBuilder createUriBuilder();
/**
* Create a new instance of a {@link javax.ws.rs.core.Response.ResponseBuilder}.
* @return new ResponseBuilder instance
* @see javax.ws.rs.core.Response.ResponseBuilder
*/
public abstract ResponseBuilder createResponseBuilder();
/**
* Create a new instance of a {@link javax.ws.rs.core.Variant.VariantListBuilder}.
*
* @return new VariantListBuilder instance
* @see javax.ws.rs.core.Variant.VariantListBuilder
*/
public abstract VariantListBuilder createVariantListBuilder();
/**
* Create a configured instance of the supplied endpoint type. How the
* returned endpoint instance is published is dependent on the type of
* endpoint.
* @param application the application configuration
* @param endpointType the type of endpoint instance to be created.
* @return a configured instance of the requested type.
* @throws java.lang.IllegalArgumentException if application is null or the
* requested endpoint type is not supported.
* @throws java.lang.UnsupportedOperationException if the implementation
* supports no endpoint types.
*/
public abstract <T> T createEndpoint(Application application,
Class<T> endpointType) throws IllegalArgumentException, UnsupportedOperationException;
/**
* Obtain an instance of a HeaderDelegate for the supplied class. An
* implementation is required to support the following values for type:
* {@link javax.ws.rs.core.Cookie}, {@link javax.ws.rs.core.CacheControl},
* {@link javax.ws.rs.core.EntityTag}, {@link javax.ws.rs.core.NewCookie},
* {@link javax.ws.rs.core.MediaType} and {@code java.util.Date}.
* @param type the class of the header
* @return an instance of HeaderDelegate for the supplied type
* @throws java.lang.IllegalArgumentException if type is null
*/
public abstract <T> HeaderDelegate<T> createHeaderDelegate(Class<T> type);
/**
* Defines the contract for a delegate that is responsible for
* converting between the String form of a HTTP header and
* the corresponding JAX-RS type <code>T</code>.
* @param T a JAX-RS type that corresponds to the value of a HTTP header
*/
public static interface HeaderDelegate<T> {
/**
* Parse the supplied value and create an instance of <code>T</code>.
* @param value the string value
* @return the newly created instance of <code>T</code>
* @throws IllegalArgumentException if the supplied string cannot be
* parsed or is null
*/
public T fromString(String value) throws IllegalArgumentException;
/**
* Convert the supplied value to a String.
* @param value the value of type <code>T</code>
* @return a String representation of the value
* @throws IllegalArgumentException if the supplied object cannot be
* serialized or is null
*/
public String toString(T value);
}
}
|