blob: 6ff2b01b0b67936952bd2f0ac601c29fab441a9d [file] [log] [blame]
/**
* 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.hadoop.hdfs.server.federation.router;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
import org.apache.hadoop.hdfs.protocol.ClientProtocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Determines the remote client protocol method and the parameter list for a
* specific location.
*/
public class RemoteMethod {
private static final Logger LOG = LoggerFactory.getLogger(RemoteMethod.class);
/** List of parameters: static and dynamic values, matchings types. */
private final Object[] params;
/** List of method parameters types, matches parameters. */
private final Class<?>[] types;
/** Class of the protocol for the method. */
private final Class<?> protocol;
/** String name of the ClientProtocol method. */
private final String methodName;
/**
* Create a remote method generator for the ClientProtocol with no parameters.
*
* @param method The string name of the protocol method.
*/
public RemoteMethod(String method) {
this(ClientProtocol.class, method);
}
/**
* Create a method with no parameters.
*
* @param proto Protocol of the method.
* @param method The string name of the ClientProtocol method.
*/
public RemoteMethod(Class<?> proto, String method) {
this.params = null;
this.types = null;
this.methodName = method;
this.protocol = proto;
}
/**
* Create a remote method generator for the ClientProtocol.
*
* @param method The string name of the ClientProtocol method.
* @param pTypes A list of types to use to locate the specific method.
* @param pParams A list of parameters for the method. The order of the
* parameter list must match the order and number of the types.
* Parameters are grouped into 2 categories:
* <ul>
* <li>Static parameters that are immutable across locations.
* <li>Dynamic parameters that are determined for each location by a
* RemoteParam object. To specify a dynamic parameter, pass an
* instance of RemoteParam in place of the parameter value.
* </ul>
* @throws IOException If the types and parameter lists are not valid.
*/
public RemoteMethod(String method, Class<?>[] pTypes, Object... pParams)
throws IOException {
this(ClientProtocol.class, method, pTypes, pParams);
}
/**
* Creates a remote method generator.
*
* @param proto Protocol of the method.
* @param method The string name of the ClientProtocol method.
* @param pTypes A list of types to use to locate the specific method.
* @param pParams A list of parameters for the method. The order of the
* parameter list must match the order and number of the types.
* Parameters are grouped into 2 categories:
* <ul>
* <li>Static parameters that are immutable across locations.
* <li>Dynamic parameters that are determined for each location by a
* RemoteParam object. To specify a dynamic parameter, pass an
* instance of RemoteParam in place of the parameter value.
* </ul>
* @throws IOException If the types and parameter lists are not valid.
*/
public RemoteMethod(Class<?> proto, String method, Class<?>[] pTypes,
Object... pParams) throws IOException {
if (pParams.length != pTypes.length) {
throw new IOException("Invalid parameters for method " + method);
}
this.protocol = proto;
this.params = pParams;
this.types = Arrays.copyOf(pTypes, pTypes.length);
this.methodName = method;
}
/**
* Get the interface/protocol for this method. For example, ClientProtocol or
* NamenodeProtocol.
*
* @return Protocol for this method.
*/
public Class<?> getProtocol() {
return this.protocol;
}
/**
* Get the represented java method.
*
* @return Method
* @throws IOException If the method cannot be found.
*/
public Method getMethod() throws IOException {
try {
if (types != null) {
return protocol.getDeclaredMethod(methodName, types);
} else {
return protocol.getDeclaredMethod(methodName);
}
} catch (NoSuchMethodException e) {
// Re-throw as an IOException
LOG.error("Cannot get method {} with types {} from {}",
methodName, Arrays.toString(types), protocol.getSimpleName(), e);
throw new IOException(e);
} catch (SecurityException e) {
LOG.error("Cannot access method {} with types {} from {}",
methodName, Arrays.toString(types), protocol.getSimpleName(), e);
throw new IOException(e);
}
}
/**
* Get the calling types for this method.
*
* @return An array of calling types.
*/
public Class<?>[] getTypes() {
return Arrays.copyOf(this.types, this.types.length);
}
/**
* Generate a list of parameters for this specific location using no context.
*
* @return A list of parameters for the method customized for the location.
*/
public Object[] getParams() {
return this.getParams(null);
}
/**
* Get the name of the method.
*
* @return Name of the method.
*/
public String getMethodName() {
return this.methodName;
}
/**
* Generate a list of parameters for this specific location. Parameters are
* grouped into 2 categories:
* <ul>
* <li>Static parameters that are immutable across locations.
* <li>Dynamic parameters that are determined for each location by a
* RemoteParam object.
* </ul>
*
* @param context The context identifying the location.
* @return A list of parameters for the method customized for the location.
*/
public Object[] getParams(RemoteLocationContext context) {
if (this.params == null) {
return new Object[] {};
}
Object[] objList = new Object[this.params.length];
for (int i = 0; i < this.params.length; i++) {
Object currentObj = this.params[i];
if (currentObj instanceof RemoteParam) {
// Map the parameter using the context
RemoteParam paramGetter = (RemoteParam) currentObj;
objList[i] = paramGetter.getParameterForContext(context);
} else {
objList[i] = currentObj;
}
}
return objList;
}
@Override
public String toString() {
return this.protocol.getSimpleName() + "#" + this.methodName + " " +
Arrays.toString(this.params);
}
}