/*******************************************************************************
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *  
 *   http://www.apache.org/licenses/LICENSE-2.0
 *  
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *  
 *******************************************************************************/

package org.apache.wink.common.internal.registry;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Providers;

import org.apache.wink.common.RuntimeContext;
import org.apache.wink.common.internal.runtime.RuntimeContextTLS;

public class ContextAccessor {

    public <T> T getContext(Class<T> contextClass, RuntimeContext runtimeContext) {

        // try to get a context from a ContextResolver
        T context = getContextFromResolver(contextClass, runtimeContext);
        if (context != null) {
            return context;
        }

        // get a context from one of the message context accessors
        return getContextFromAccessor(contextClass, runtimeContext);
    }

    /**
     * Obtain a context from one of the contexts available through the
     * RuntimeContext. This can be done in two ways: if runtimeContext is
     * provided, the context is obtained directly. If runtimeContext is null,
     * then a proxy is created which obtains the RuntimeContext using the TLS.
     * 
     * @param <T> - interface of the context
     * @param contextClass - interface of the context
     * @param runtimeContext - RuntimeContext
     * @return instance of T if context was found or null otherwise.
     */
    @SuppressWarnings("unchecked")
    public <T> T getContextFromAccessor(final Class<T> contextClass, RuntimeContext runtimeContext) {

        // return context directly
        if (runtimeContext != null) {
            try {
                T instance = runtimeContext.getAttribute(contextClass);
                if (instance != null) {
                    return instance;
                }
                /*
                 * if the instance isn't directly available from the
                 * runtimecontext at this time, inject a proxy instead. This
                 * allows context attributes to be added after a class is
                 * instantiated
                 */
            } catch (Exception e) {
                if (e instanceof InvocationTargetException) {
                    Throwable ite = ((InvocationTargetException)e).getTargetException();
                    if (ite instanceof RuntimeException) {
                        throw (RuntimeException)ite;
                    }
                    throw new WebApplicationException(ite);
                }
                throw new WebApplicationException(e);
            }
        }

        // return a proxy that looks for a context on TLS during the method
        // invocation
        T proxy =
            (T)Proxy.newProxyInstance(Injectable.class.getClassLoader(),
                                      new Class[] {contextClass},
                                      new InvocationHandler() {
                                          public Object invoke(Object proxy,
                                                               Method method,
                                                               Object[] args) throws Throwable {
                                              // use runtimeContext from TLS
                                              RuntimeContext runtimeContext =
                                                  RuntimeContextTLS.getRuntimeContext();
                                              if (runtimeContext == null) {
                                                  if("toString".equals(method.getName()) && (args == null || args.length == 0)) {
                                                      return "Proxy for " + contextClass.getName();
                                                  }
                                                  throw new IllegalStateException();
                                              }
                                              // get the real context from the
                                              // RuntimeContext
                                              Object context =
                                                  runtimeContext.getAttribute(contextClass);
                                              // invoke the method on the real
                                              // context
                                              return method.invoke(context, args);
                                          }
                                      });
        return proxy;
    }

    /**
     * Get a context from on of the registered context providers
     * 
     * @param type
     * @param runtimeContext
     * @return
     */
    public <T> T getContextFromResolver(Class<T> contextClass, RuntimeContext runtimeContext) {
        if (runtimeContext == null) {
            return null;
        }
        Providers providers = runtimeContext.getProviders();
        if (providers != null) {
            HttpHeaders httpHeaders = runtimeContext.getHttpHeaders();
            MediaType mediaType = null;
            if (httpHeaders != null) {
                // this is hotfix for WINK-166
                mediaType = httpHeaders.getMediaType();
            }
            if (mediaType == null) {
                mediaType = MediaType.WILDCARD_TYPE;
            }
            ContextResolver<T> contextResolver =
                providers.getContextResolver(contextClass, mediaType);
            if (contextResolver != null) {
                T context = contextResolver.getContext(contextClass);
                if (context != null) {
                    return context;
                }
            }
        }
        return null;
    }

}

@@ -5153,115 +5153,237 @@
     
-Object context =%0A                                                  runtimeContext.getAttribute(contextClass
+// We need to call getContext() instead of getContextFromAccessor() as some context will be created from the context resolvers%0A                                              Object context = getContext(contextClass, runtimeContext
 );%0A 

Allow the injected context proxy to use context resolvers