All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.api.server.spi.MethodHierarchyReader Maven / Gradle / Ivy

There is a newer version: 2.2.2
Show newest version
/*
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * 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.api.server.spi;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ListMultimap;
import com.google.common.reflect.TypeToken;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Map;

/**
 * Utility to take a class and create maps of methods usable for endpoints and related data.
 *
 * 

On the first call, parses and stores the full hierarchy information. The cached full * hierarchy is then used for all subsequent calls. Returned iterables, maps, and lists are copied * out without reference to the original full hierarchy information to allow the caller to keep just * the needed portion of information and release the full hierarchy along with this class. * * @author Eric Orth */ public class MethodHierarchyReader { private final Class endpointClass; // Map from method signatures to a list of method overrides for that signature (ordered // subclass -> superclass). private ListMultimap endpointMethods; /** * Constructs a {@code MethodHierarchyReader} for the given class type. * * @param endpointClass Must be a concrete type (not abstract or interface). */ public MethodHierarchyReader(Class endpointClass) { Preconditions.checkArgument( !Modifier.isAbstract(endpointClass.getModifiers()) && !Modifier.isInterface(endpointClass.getModifiers()), "A concrete class is expected, but got %s.", endpointClass.getName()); this.endpointClass = endpointClass; } private void readMethodHierarchyIfNecessary() { if (endpointMethods == null) { ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); buildServiceMethods(builder, TypeToken.of(endpointClass)); endpointMethods = builder.build(); } } /** * Returns the final leaf subclass version of a method. * * @param overrides A list of method overrides ordered subclass -> superclass. */ private EndpointMethod getLeafMethod(List overrides) { // Because the list is ordered subclass -> superclass, index 0 will always contain the leaf // subclass implementation. return overrides.get(0); } /** * Returns {@link ListMultimap#asMap multimap.asMap()}, with its type * corrected from {@code Map>} to {@code Map>}. */ // Copied from com.google.common.collect.Multimaps. We can't use the actual method from // that class as appengine build magic gives us an older version of guava that doesn't yet have // this method. // TODO: Switch to Multimaps.asMap() once it becomes available in appengine. @SuppressWarnings("unchecked") // safe by specification of ListMultimap.asMap() private static Map> asMap(ListMultimap multimap) { return (Map>) (Map) multimap.asMap(); } /** * Returns a collection of public service methods defined by the class and its super classes. * Bridge methods are ignored. Only the final leaf subclass version of each method is included. */ public Iterable getLeafMethods() { readMethodHierarchyIfNecessary(); ImmutableList.Builder builder = ImmutableList.builder(); for (List overrides : asMap(endpointMethods).values()) { builder.add(getLeafMethod(overrides).getMethod()); } return builder.build(); } /** * Returns a collection of public service methods defined by the class and its super classes. * Bridge methods are ignored. Only the final leaf subclass version of each method is included. * Methods are stored in the EndpointMethod container. */ public Iterable getLeafEndpointMethods() { readMethodHierarchyIfNecessary(); ImmutableList.Builder builder = ImmutableList.builder(); for (List overrides : asMap(endpointMethods).values()) { builder.add(getLeafMethod(overrides)); } return builder.build(); } /** * Returns a collection of public service methods defined by the class and its super classes. * Bridge methods are ignored. For each method, all valid method implementations are included, * ordered subclass to superclass. */ public Iterable> getMethodOverrides() { readMethodHierarchyIfNecessary(); ImmutableList.Builder> builder = ImmutableList.builder(); for (List overrides : asMap(endpointMethods).values()) { ImmutableList.Builder methodBuilder = ImmutableList.builder(); for (EndpointMethod method : overrides) { methodBuilder.add(method.getMethod()); } builder.add(methodBuilder.build()); } return builder.build(); } /** * Returns a collection of public service methods defined by the class and its super classes. * Bridge methods are ignored. For each method, all valid method implementations are included, * ordered subclass to superclass. Methods are stored in the EndpointMethod container. */ public Iterable> getEndpointOverrides() { readMethodHierarchyIfNecessary(); return asMap(endpointMethods).values(); } /** * Returns a mapping of public service methods defined by the class and its super classes, * keyed by method name. Bridge methods are ignored. Only the final leaf subclass version of * each method is included. */ public Map getNameToLeafMethodMap() { readMethodHierarchyIfNecessary(); ImmutableMap.Builder builder = ImmutableMap.builder(); for (List overrides : asMap(endpointMethods).values()) { Method leafMethod = getLeafMethod(overrides).getMethod(); builder.put(leafMethod.getName(), leafMethod); } return builder.build(); } /** * Returns a mapping of public service methods defined by the class and its super classes, * keyed by method name. Bridge methods are ignored. All valid method implementations are * included, ordered subclass to superclass. */ public ListMultimap getNameToEndpointOverridesMap() { readMethodHierarchyIfNecessary(); ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); for (List overrides : asMap(endpointMethods).values()) { builder.putAll(getLeafMethod(overrides).getMethod().getName(), overrides); } return builder.build(); } /** * Recursively builds a map from method_names to methods. Method types will be resolved as much * as possible using {@code serviceType}. * * @param serviceType is the class object being inspected for service methods */ private void buildServiceMethods( ImmutableListMultimap.Builder builder, TypeToken serviceType) { for (TypeToken typeToken : serviceType.getTypes().classes()) { Class serviceClass = typeToken.getRawType(); if (Object.class.equals(serviceClass)) { return; } for (Method method : serviceClass.getDeclaredMethods()) { if (!isServiceMethod(method)) { continue; } EndpointMethod currentMethod = EndpointMethod.create(endpointClass, method, typeToken); builder.put(currentMethod.getResolvedMethodSignature(), currentMethod); } } } @VisibleForTesting static boolean isServiceMethod(Method method) { int modifiers = method.getModifiers(); return Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers) && !method.isBridge(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy