1  /*
     2   * Copyright the original author or authors.
     3   * 
     4   * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   * 
     8   *      http://www.mozilla.org/MPL/MPL-1.1.html
     9   * 
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  import org.as2lib.env.reflect.ProxyFactory;
    18  import org.as2lib.env.reflect.InterfaceProxyFactory;
    19  import org.as2lib.env.reflect.InvocationHandler;
    20  import org.as2lib.io.conn.core.client.ClientServiceProxy;
    21  import org.as2lib.io.conn.core.client.ClientServiceProxyFactory;
    22  import org.as2lib.io.conn.core.client.AbstractClientServiceProxyFactory;
    23  import org.as2lib.io.conn.local.client.SimpleClientServiceProxyFactory;
    24  import org.as2lib.io.conn.core.event.MethodInvocationCallback;
    25  
    26  /**
    27   * {@code LocalClientServiceProxyFactory} acts as central provider of client service
    28   * proxies.
    29   * 
    30   * <p>This provision is in the simplest case just the returning of a new client 
    31   * service proxy.
    32   * <code>
    33   *   var clientFactory:LocalClientServiceProxyFactory = new LocalClientServiceProxyFactory();
    34   *   var client:ClientServiceProxy = clientFactory.getClientServiceProxy("local.as2lib.org/myService");
    35   * </code>
    36   * 
    37   * <p>In a more complex case this means creating a client service proxy for a specific
    38   * type, mostly an interface, that is the same type of the 'remote' service.
    39   * <code>
    40   *   var clientFactory:LocalClientServiceProxyFactory = new LocalClientServiceProxyFactory();
    41   *   var client:MyType = clientFactory.getClientServiceProxy("local.as2lib.org/myService", MyType);
    42   *   client.myMethod("myArg1", "myArg2");
    43   * </code>
    44   * 
    45   * <p>There is sadly one flaw with the last type of usage. That is that the method
    46   * cannot response directly due to the asynchronity of the call. To get a response
    47   * you therefore have to pass a third argument of type {@code MethodInvocationCallback}.
    48   * <code>
    49   *   var clientFactory:LocalClientServiceProxyFactory = new LocalClientServiceProxyFactory();
    50   *   var client:MyType = clientFactory.getClientServiceProxy("local.as2lib.org/myService", MyType);
    51   *   var callback:MethodInvocationCallback = new MethodInvocationCallback();
    52   *   client.myMethod("myArg1", "myArg2", callback);
    53   *   callback.onReturn = function(returnInfo:MethodInvocationReturnInfo):Void {
    54   *       trace("myMethod - return value: " + returnInfo.getReturnValue());
    55   *   }
    56   *   callback.onError = function(errorInfo:MethodInvocationErrorInfo):Void {
    57   *       trace("myMethod - error: " + errorInfo.getException());
    58   *   }
    59   * </code>
    60   *
    61   * @author Simon Wacker
    62   * @author Christoph Atteneder
    63   * @see org.as2lib.io.conn.core.event.MethodInvocationCallback
    64   */
    65  class org.as2lib.io.conn.local.client.LocalClientServiceProxyFactory extends AbstractClientServiceProxyFactory implements ClientServiceProxyFactory {
    66  	
    67  	/** The currently used proxy factory to create proxies for a specific type. */
    68  	private var typeProxyFactory:ProxyFactory;
    69  	
    70  	/** Stores the client service proxy factory used to get client service proxy instances. */
    71  	private var clientServiceProxyFactory:ClientServiceProxyFactory;
    72  	
    73  	/**
    74  	 * Constructs a new {@code LocalClientServiceProxyFactory} instance.
    75  	 */
    76  	public function LocalClientServiceProxyFactory(Void) {
    77  	}
    78  	
    79  	/**
    80  	 * Returns the currently used type proxy factory that is used to create proxies for
    81  	 * a specific type.
    82  	 * 
    83  	 * <p>That is either the proxy factory set via {@link #setTypeProxyFactory}
    84  	 * or the default one, which is an instance of type {@link InterfaceProxyFactory}.
    85  	 * 
    86  	 * <p>The default {@link InterfaceProxyFactory} can only be used to create proxies
    87  	 * of interfaces.
    88  	 *
    89  	 * @return the currently used type proxy factory
    90  	 */
    91  	public function getTypeProxyFactory(Void):ProxyFactory {
    92  		if (!typeProxyFactory) typeProxyFactory = new InterfaceProxyFactory();
    93  		return typeProxyFactory;
    94  	}
    95  	
    96  	/**
    97  	 * Sets the new type proxy factory that is used to create proxies for a specific type.
    98  	 * 
    99  	 * <p>If you set a type proxy factory of value {@code null}, {@link #getTypeProxyFactory}
   100  	 * will return the default factory.
   101  	 *
   102  	 * @param proxyFactory the new type proxy factory
   103  	 */
   104  	public function setTypeProxyFactory(typeServiceProxyFactory:ProxyFactory):Void {
   105  		this.typeProxyFactory = typeServiceProxyFactory;
   106  	}
   107  	
   108  	/**
   109  	 * Returns the client service proxy factory used to create client service proxy
   110  	 * instances.
   111  	 * 
   112  	 * <p>The returned factory is either the one set via {@link #setClientServiceProxyFactory}
   113  	 * or the default one which is an instance of {@link SimpleClientServiceProxyFactory}.
   114  	 *
   115  	 * @return the currently used client service proxy factory
   116  	 */
   117  	public function getClientServiceProxyFactory(Void):ClientServiceProxyFactory {
   118  		if (!clientServiceProxyFactory) clientServiceProxyFactory = new SimpleClientServiceProxyFactory();
   119  		return clientServiceProxyFactory;
   120  	}
   121  	
   122  	/**
   123  	 * Sets a new client service proxy factory used to get client service
   124  	 * proxy instances.
   125  	 *
   126  	 * <p>If you set a new factory of value null or undefined {@link #getClientServiceProxyFactory}
   127  	 * will return the default factory.
   128  	 *
   129  	 * @param clientServiceProxyFactory the new client service proxy factory
   130  	 */
   131  	public function setClientServiceProxyFactory(clientServiceProxyFactory:ClientServiceProxyFactory):Void {
   132  		this.clientServiceProxyFactory = clientServiceProxyFactory;
   133  	}
   134  	
   135  	/**
   136  	 * Returns a client service proxy for the service specified by the passed-in
   137  	 * {@code url}.
   138  	 * 
   139  	 * <p>You can use the returned proxy to invoke methods on the 'remote' service and
   140  	 * to handle responses.
   141  	 * 
   142  	 * @param url the url of the 'remote' service
   143  	 * @return a client service proxy to invoke methods on the 'remote' service
   144  	 */
   145  	public function getClientServiceProxyByUrl(url:String):ClientServiceProxy {
   146  		return getClientServiceProxyFactory().getClientServiceProxy(url);
   147  	}
   148  	
   149  	/**
   150  	 * Returns a client service proxy that can be typed to the passed-in {@code type}
   151  	 * (class or interface).
   152  	 * 
   153  	 * <p>The type is therefore normally the type of the 'remote' service you wanna
   154  	 * invoke methods on.
   155  	 * 
   156  	 * <p>If {@code type} is {@code null}, an instance of type {@link ClientServiceProxy}
   157  	 * will be returned. That means this method will then do the same as the
   158  	 * {@link #getClientServiceProxyByUrl} method.
   159  	 *
   160  	 * <p>Note that with the default configuration only interfaces can be used as
   161  	 * {@code type}. You can edit this behavior through the {@link #setTypeProxyFactory}.
   162  	 * method.
   163  	 * 
   164  	 * @param url the url of the 'remote' service
   165  	 * @param type the type of the 'remote' service
   166  	 * @return a client service proxy that can be casted to the passed-in {@code type}
   167  	 */
   168  	public function getClientServiceProxyByUrlAndType(url:String, type:Function) {
   169  		var serviceProxy:ClientServiceProxy = getClientServiceProxyByUrl(url);
   170  		if (!type) return serviceProxy;
   171  		var handler:InvocationHandler = getBlankInvocationHandler();
   172  		handler.invoke = function(proxy, methodName:String, args:Array) {
   173  			if (args[args.length-1] instanceof MethodInvocationCallback) {
   174  				var callback:MethodInvocationCallback = MethodInvocationCallback(args.pop());
   175  				return serviceProxy.invokeByNameAndArgumentsAndCallback(methodName, args, callback);
   176  			} else {
   177  				return serviceProxy.invokeByNameAndArguments(methodName, args);
   178  			}
   179  		};
   180  		return getTypeProxyFactory().createProxy(type, handler);
   181  	}
   182  	
   183  	/**
   184  	 * Returns a blank invocation handler. This is a handler with no methods implemented.
   185  	 * 
   186  	 * @return a blank invocation handler
   187  	 */
   188  	private function getBlankInvocationHandler(Void):InvocationHandler {
   189  		var result = new Object();
   190  		result.__proto__ = InvocationHandler["prototype"];
   191  		result.__constructor__ = InvocationHandler;
   192  		return result;
   193  	}
   194  	
   195  }