alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (JpaFinderProxy.java)

This example Java source code file (JpaFinderProxy.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

annotation, class, collection, finder's, finderdescriptor, firstresult, jpafinderproxy, maxresults, named, object, query, reflection, runtimeexception, specified, string, util

The JpaFinderProxy.java Java example source code

/**
 * Copyright (C) 2010 Google, Inc.
 *
 * Licensed 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 com.google.inject.persist.jpa;

import com.google.common.collect.MapMaker;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import com.google.inject.persist.finder.Finder;
import com.google.inject.persist.finder.FirstResult;
import com.google.inject.persist.finder.MaxResults;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.Query;

/**
 * TODO(dhanji): Make this work!!
 *
 * @author Dhanji R. Prasanna (dhanji@gmail.com)
 */
@Singleton
class JpaFinderProxy implements MethodInterceptor {
  private final Map<Method, FinderDescriptor> finderCache = new MapMaker().weakKeys().makeMap();
  private final Provider<EntityManager> emProvider;

  @Inject
  public JpaFinderProxy(Provider<EntityManager> emProvider) {
    this.emProvider = emProvider;
  }

  public Object invoke(MethodInvocation methodInvocation) throws Throwable {
    EntityManager em = emProvider.get();

    //obtain a cached finder descriptor (or create a new one)
    JpaFinderProxy.FinderDescriptor finderDescriptor = getFinderDescriptor(methodInvocation);

    Object result = null;

    //execute as query (named params or otherwise)
    Query jpaQuery = finderDescriptor.createQuery(em);
    if (finderDescriptor.isBindAsRawParameters) {
      bindQueryRawParameters(jpaQuery, finderDescriptor, methodInvocation.getArguments());
    } else {
      bindQueryNamedParameters(jpaQuery, finderDescriptor, methodInvocation.getArguments());
    }

    //depending upon return type, decorate or return the result as is
    if (JpaFinderProxy.ReturnType.PLAIN.equals(finderDescriptor.returnType)) {
      result = jpaQuery.getSingleResult();
    } else if (JpaFinderProxy.ReturnType.COLLECTION.equals(finderDescriptor.returnType)) {
      result = getAsCollection(finderDescriptor, jpaQuery.getResultList());
    } else if (JpaFinderProxy.ReturnType.ARRAY.equals(finderDescriptor.returnType)) {
      result = jpaQuery.getResultList().toArray();
    }

    return result;
  }

  private Object getAsCollection(JpaFinderProxy.FinderDescriptor finderDescriptor,
      List results) {
    Collection<?> collection;
    try {
      collection = (Collection) finderDescriptor.returnCollectionTypeConstructor.newInstance();
    } catch (InstantiationException e) {
      throw new RuntimeException(
          "Specified collection class of Finder's returnAs could not be instantated: "
              + finderDescriptor.returnCollectionType, e);
    } catch (IllegalAccessException e) {
      throw new RuntimeException(
          "Specified collection class of Finder's returnAs could not be instantated (do not have access privileges): "
              + finderDescriptor.returnCollectionType, e);
    } catch (InvocationTargetException e) {
      throw new RuntimeException(
          "Specified collection class of Finder's returnAs could not be instantated (it threw an exception): "
              + finderDescriptor.returnCollectionType, e);
    }

    collection.addAll(results);
    return collection;
  }

  private void bindQueryNamedParameters(Query jpaQuery,
      JpaFinderProxy.FinderDescriptor descriptor, Object[] arguments) {
    for (int i = 0; i < arguments.length; i++) {
      Object argument = arguments[i];
      Object annotation = descriptor.parameterAnnotations[i];

      if (null == annotation)
      //noinspection UnnecessaryContinue
      {
        continue;   //skip param as it's not bindable
      } else if (annotation instanceof Named) {
        Named named = (Named) annotation;
        jpaQuery.setParameter(named.value(), argument);
      } else if (annotation instanceof javax.inject.Named) {
        javax.inject.Named named = (javax.inject.Named) annotation;
        jpaQuery.setParameter(named.value(), argument);
      } else if (annotation instanceof FirstResult) {
        jpaQuery.setFirstResult((Integer) argument);
      } else if (annotation instanceof MaxResults) {
        jpaQuery.setMaxResults((Integer) argument);
      }
    }
  }

  private void bindQueryRawParameters(Query jpaQuery,
      JpaFinderProxy.FinderDescriptor descriptor, Object[] arguments) {
    for (int i = 0, index = 1; i < arguments.length; i++) {
      Object argument = arguments[i];
      Object annotation = descriptor.parameterAnnotations[i];

      if (null == annotation) {
        //bind it as a raw param (1-based index, yes I know its different from Hibernate, blargh)
        jpaQuery.setParameter(index, argument);
        index++;
      } else if (annotation instanceof FirstResult) {
        jpaQuery.setFirstResult((Integer) argument);
      } else if (annotation instanceof MaxResults) {
        jpaQuery.setMaxResults((Integer) argument);
      }
    }
  }

  private JpaFinderProxy.FinderDescriptor getFinderDescriptor(MethodInvocation invocation) {
    Method method = invocation.getMethod();
    JpaFinderProxy.FinderDescriptor finderDescriptor = finderCache.get(method);
    if (null != finderDescriptor) {
      return finderDescriptor;
    }

    //otherwise reflect and cache finder info...
    finderDescriptor = new JpaFinderProxy.FinderDescriptor();

    //determine return type
    finderDescriptor.returnClass = invocation.getMethod().getReturnType();
    finderDescriptor.returnType = determineReturnType(finderDescriptor.returnClass);

    //determine finder query characteristics
    Finder finder = invocation.getMethod().getAnnotation(Finder.class);
    String query = finder.query();
    if (!"".equals(query.trim())) {
      finderDescriptor.setQuery(query);
    } else {
      finderDescriptor.setNamedQuery(finder.namedQuery());
    }

    //determine parameter annotations
    Annotation[][] parameterAnnotations = method.getParameterAnnotations();
    Object[] discoveredAnnotations = new Object[parameterAnnotations.length];
    for (int i = 0; i < parameterAnnotations.length; i++) {
      Annotation[] annotations = parameterAnnotations[i];
      //each annotation per param
      for (Annotation annotation : annotations) {
        //discover the named, first or max annotations then break out
        Class<? extends Annotation> annotationType = annotation.annotationType();
        if (Named.class.equals(annotationType) || javax.inject.Named.class.equals(annotationType)) {
          discoveredAnnotations[i] = annotation;
          finderDescriptor.isBindAsRawParameters = false;
          break;
        } else if (FirstResult.class.equals(annotationType)) {
          discoveredAnnotations[i] = annotation;
          break;
        } else if (MaxResults.class.equals(annotationType)) {
          discoveredAnnotations[i] = annotation;
          break;
        }   //leave as null for no binding
      }
    }

    //set the discovered set to our finder cache object
    finderDescriptor.parameterAnnotations = discoveredAnnotations;

    //discover the returned collection implementation if this finder returns a collection
    if (JpaFinderProxy.ReturnType.COLLECTION.equals(finderDescriptor.returnType)
        && finderDescriptor.returnClass != Collection.class) {
      finderDescriptor.returnCollectionType = finder.returnAs();
      try {
        finderDescriptor.returnCollectionTypeConstructor = finderDescriptor.returnCollectionType
            .getConstructor();
        finderDescriptor.returnCollectionTypeConstructor.setAccessible(true);   //UGH!
      } catch (NoSuchMethodException e) {
        throw new RuntimeException(
            "Finder's collection return type specified has no default constructor! returnAs: "
                + finderDescriptor.returnCollectionType, e);
      }
    }

    //cache it
    cacheFinderDescriptor(method, finderDescriptor);

    return finderDescriptor;
  }

  /**
   * writes to a chm (used to provide copy-on-write but this is bettah!)
   *
   * @param method The key
   * @param finderDescriptor The descriptor to cache
   */
  private void cacheFinderDescriptor(Method method, FinderDescriptor finderDescriptor) {
    //write to concurrent map
    finderCache.put(method, finderDescriptor);
  }

  private JpaFinderProxy.ReturnType determineReturnType(Class<?> returnClass) {
    if (Collection.class.isAssignableFrom(returnClass)) {
      return JpaFinderProxy.ReturnType.COLLECTION;
    } else if (returnClass.isArray()) {
      return JpaFinderProxy.ReturnType.ARRAY;
    }

    return JpaFinderProxy.ReturnType.PLAIN;
  }

  /**
   * A wrapper data class that caches information about a finder method.
   */
  private static class FinderDescriptor {
    private volatile boolean isKeyedQuery = false;
    volatile boolean isBindAsRawParameters = true;
        //should we treat the query as having ? instead of :named params
    volatile JpaFinderProxy.ReturnType returnType;
    volatile Class<?> returnClass;
    volatile Class<? extends Collection> returnCollectionType;
    volatile Constructor returnCollectionTypeConstructor;
    volatile Object[] parameterAnnotations;
        //contract is: null = no bind, @Named = param, @FirstResult/@MaxResults for paging

    private String query;
    private String name;

    void setQuery(String query) {
      this.query = query;
    }

    void setNamedQuery(String name) {
      this.name = name;
      isKeyedQuery = true;
    }

    public boolean isKeyedQuery() {
      return isKeyedQuery;
    }

    Query createQuery(EntityManager em) {
      return isKeyedQuery ? em.createNamedQuery(name) : em.createQuery(query);
    }
  }

  private static enum ReturnType {
    PLAIN, COLLECTION, ARRAY
  }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java JpaFinderProxy.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.