/** {@inheritDoc} */
  public void persistShoppingCart(
      final HttpServletRequest httpServletRequest,
      final HttpServletResponse httpServletResponse,
      final ShoppingCart shoppingCart) {

    CartTuplizer tuplizer = null;
    try {

      tuplizer = (CartTuplizer<HttpServletRequest, HttpServletResponse>) tuplizerPool.getTarget();
      try {
        tuplizer.tuplize(httpServletRequest, httpServletResponse, shoppingCart);
      } catch (CartTuplizationException e) {
        ShopCodeContext.getLog(this)
            .error(MessageFormat.format("Unable to create cookies from {0} cart", shoppingCart), e);
      }

    } catch (Exception e) {
      ShopCodeContext.getLog(this).error("Can process request", e);
    } finally {
      if (tuplizer != null) {
        try {
          tuplizerPool.releaseTarget(tuplizer);
        } catch (Exception e) {
          ShopCodeContext.getLog(this).error("Can return object to pool ", e);
        }
      }
    }
  }
  /**
   * Implementation of <code>InvocationHandler.invoke</code>.
   *
   * <p>Callers will see exactly the exception thrown by the target, unless a hook method throws an
   * exception.
   */
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    MethodInvocation invocation = null;
    Object oldProxy = null;
    boolean setProxyContext = false;

    TargetSource targetSource = this.advised.targetSource;
    Class targetClass = null;
    Object target = null;

    try {
      if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
        // The target does not implement the equals(Object) method itself.
        return (equals(args[0]) ? Boolean.TRUE : Boolean.FALSE);
      }
      if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
        // The target does not implement the hashCode() method itself.
        return new Integer(hashCode());
      }
      if (!this.advised.opaque
          && method.getDeclaringClass().isInterface()
          && method.getDeclaringClass().isAssignableFrom(Advised.class)) {
        // Service invocations on ProxyConfig with the proxy config...
        return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
      }

      Object retVal = null;

      if (this.advised.exposeProxy) {
        // Make invocation available if necessary.
        oldProxy = AopContext.setCurrentProxy(proxy);
        setProxyContext = true;
      }

      // May be <code>null</code>. Get as late as possible to minimize the time we "own" the target,
      // in case it comes from a pool.
      target = targetSource.getTarget();
      if (target != null) {
        targetClass = target.getClass();
      }

      // Get the interception chain for this method.
      List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

      // Check whether we have any advice. If we don't, we can fallback on direct
      // reflective invocation of the target, and avoid creating a MethodInvocation.
      if (chain.isEmpty()) {
        // We can skip creating a MethodInvocation: just invoke the target directly
        // Note that the final invoker must be an InvokerInterceptor so we know it does
        // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
        retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
      } else {
        // We need to create a method invocation...
        invocation =
            new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
        // Proceed to the joinpoint through the interceptor chain.
        retVal = invocation.proceed();
      }

      // Massage return value if necessary.
      if (retVal != null
          && retVal == target
          && method.getReturnType().isInstance(proxy)
          && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
        // Special case: it returned "this" and the return type of the method
        // is type-compatible. Note that we can't help if the target sets
        // a reference to itself in another returned object.
        retVal = proxy;
      }
      return retVal;
    } finally {
      if (target != null && !targetSource.isStatic()) {
        // Must have come from TargetSource.
        targetSource.releaseTarget(target);
      }
      if (setProxyContext) {
        // Restore old proxy.
        AopContext.setCurrentProxy(oldProxy);
      }
    }
  }