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    }