content: /*
   * 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.aries.proxy.impl;
  
  import static java.lang.String.format;
  
  import java.lang.reflect.Constructor;
  import java.lang.reflect.InvocationHandler;
  import java.lang.reflect.InvocationTargetException;
  import java.util.Collection;
  import java.util.concurrent.Callable;
  
  import org.apache.aries.proxy.InvocationListener;
  import org.apache.aries.proxy.ProxyManager;
  import org.apache.aries.proxy.UnableToProxyException;
  import org.apache.aries.proxy.weaving.WovenProxy;
  import org.osgi.framework.Bundle;
  import org.osgi.framework.wiring.BundleWiring;
  
  public abstract class AbstractProxyManager implements ProxyManager
  {
    public final Object createDelegatingProxy(Bundle clientBundle, Collection<Class<?>> classes,
        Callable<Object> dispatcher, Object template) 
      throws UnableToProxyException
    {
      return createDelegatingInterceptingProxy(clientBundle, classes, dispatcher, template, null);
    }
    
    public Object createInterceptingProxy(Bundle clientBundle,
        Collection<Class<?>> classes, Object delegate, InvocationListener listener)
        throws UnableToProxyException {
      
      if (delegate instanceof WovenProxy) {
        WovenProxy proxy = ((WovenProxy) delegate).
                org_apache_aries_proxy_weaving_WovenProxy_createNewProxyInstance(
                new SingleInstanceDispatcher(delegate), listener);
        return proxy;
      } else {
        return createDelegatingInterceptingProxy(clientBundle, classes, 
            new SingleInstanceDispatcher(delegate), delegate, listener);
      }
    }
  
    public final Object createDelegatingInterceptingProxy(Bundle clientBundle, Collection<Class<?>> classes,
        Callable<Object> dispatcher, Object template, InvocationListener listener)
        throws UnableToProxyException 
    {
      if(dispatcher == null)
        throw new NullPointerException("You must specify a dipatcher");
      
      if (template instanceof WovenProxy) {
        WovenProxy proxy = ((WovenProxy) template).
               org_apache_aries_proxy_weaving_WovenProxy_createNewProxyInstance(
               dispatcher, listener);
        return proxy;
      }
      
      Object proxyObject = duplicateProxy(classes, dispatcher, template, listener);
      
      if (proxyObject == null) {
        proxyObject = createNewProxy(clientBundle, classes, dispatcher, listener);
      }
      
      return proxyObject;
    }
     
    public final Callable<Object> unwrap(Object proxy) 
    {
      Callable<Object> target = null;
      
      if(proxy instanceof WovenProxy) {
        //Woven proxies are a bit different, they can be proxies without
        //having a dispatcher, so we fake one up if we need to 
        
        WovenProxy wp = (WovenProxy) proxy;
        if(wp.org_apache_aries_proxy_weaving_WovenProxy_isProxyInstance()) {
          target = wp.org_apache_aries_proxy_weaving_WovenProxy_unwrap();
          if(target == null) {
            target = new SingleInstanceDispatcher(proxy);
          }
        }
      } else {
        InvocationHandler ih = getInvocationHandler(proxy);
        
        if (ih instanceof ProxyHandler) {
          target = ((ProxyHandler)ih).getTarget();
        }
      }
      return target;
    }
    
    public final boolean isProxy(Object proxy)
    {
      return (proxy != null && 
          ((proxy instanceof WovenProxy && ((WovenProxy)proxy).org_apache_aries_proxy_weaving_WovenProxy_isProxyInstance()) || 
          getInvocationHandler(proxy) instanceof ProxyHandler));
    }
    
    protected abstract Object createNewProxy(Bundle clientBundle, Collection<Class<?>> classes,
        Callable<Object> dispatcher, InvocationListener listener) throws UnableToProxyException;
    protected abstract InvocationHandler getInvocationHandler(Object proxy);
    protected abstract boolean isProxyClass(Class<?> clazz);
  
    protected synchronized ClassLoader getClassLoader(final Bundle clientBundle, Collection<Class<?>> classes) 
    {
      if (clientBundle != null && clientBundle.getState() == Bundle.UNINSTALLED) {
        throw new IllegalStateException(format("The bundle %s at version %s with id %d has been uninstalled.", 
                                               clientBundle.getSymbolicName(), clientBundle.getVersion(), clientBundle.getBundleId()));
      }
      
      ClassLoader cl = null;
      
      if (classes.size() == 1) cl = classes.iterator().next().getClassLoader();
  
      if (cl == null) {
          cl = getWiringClassloader(clientBundle);
      }
      
      return cl;
    }
  
    private ClassLoader getWiringClassloader(final Bundle bundle) {
      BundleWiring wiring = bundle != null ? bundle.adapt(BundleWiring.class) : null;
      return wiring != null ? wiring.getClassLoader() : null;
    }
  
    private Object duplicateProxy(Collection<Class<?>> classes, Callable<Object> dispatcher, 
        Object template, InvocationListener listener)
    {
      Object proxyObject = null;
      Class<?> classToProxy = null;
      
      if (template != null) {
        if(isProxyClass(template.getClass()))
          classToProxy = template.getClass();
      } else if (classes.size() == 1) {
  
        classToProxy = classes.iterator().next();
  
        if(!isProxyClass(classToProxy))
          classToProxy = null;
      }
  
      if (classToProxy != null) {
        try {
          /*
           * the class is already a proxy, we should just invoke
           * the constructor to get a new instance of the proxy
           * with a new Collaborator using the specified delegate
           */
          if(WovenProxy.class.isAssignableFrom(classToProxy)) {
            Constructor<?> c = classToProxy.getDeclaredConstructor(Callable.class, 
                InvocationListener.class);
            c.setAccessible(true);
            proxyObject = c.newInstance(dispatcher, listener);
          } else {
            proxyObject = classToProxy.getConstructor(InvocationHandler.class).
            newInstance(new ProxyHandler(this, dispatcher, listener));
          }
        } catch (InvocationTargetException e) {
        } catch (NoSuchMethodException e) {
        } catch (InstantiationException e) {
        } catch (IllegalArgumentException e) {
        } catch (SecurityException e) {
        } catch (IllegalAccessException e) {
        }
      }
      
      return proxyObject;
    }
  }