RpcRequest.java
/**
* Copyright 2016-2018 Dell Inc. or its subsidiaries. All rights reserved.
*
* <p>Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file
* except in compliance with the License. A copy of the License is located at
*
* <p>http://www.apache.org/licenses/LICENSE-2.0.txt
*
* <p>or in the "license" file accompanying this file. This file 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.emc.ecs.nfsclient.rpc;
import java.io.FileNotFoundException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.lang3.StringUtils;
/**
* An RPC request, as specified by RFC 1831 (https://tools.ietf.org/html/rfc1831).
*
* @author seibed
*/
public abstract class RpcRequest {
/** The default Internet charset. */
public static final Charset CHARSET = StandardCharsets.UTF_8;
/** The xid generator, so that xid values are unique. */
private static AtomicInteger _nextXid = new AtomicInteger(initializeXid());
/**
* RFC1831: the xid field is only used for clients matching reply messages with call messages or
* for servers detecting retransmissions; the service side cannot treat this id as any type of
* sequence number.
*/
private final int _xid;
/**
* Requests must be calls (CALL = 0). Specified by RFC 1831 (https://tools.ietf.org/html/rfc1831).
*/
private final int _direction = 0;
/** The RPC version, as specified by RFC 1831 (https://tools.ietf.org/html/rfc1831). */
private final int _rpcVersion = 2;
/** The program number of the service. */
private final int _serviceProgram;
/** The version number of the service. */
private final int _serviceVersion;
/** The service procedure number to be called. */
private final int _serviceProcedure;
/** The authentication credential to be used for the call. */
private final Credential _credential;
/**
* <ul>
* <li><code>true</code> if the client should use a privileged local port (below 1024).
* <li><code>false</code> if the client should use any available local port.
* </ul>
*
* This starts as <code>false</code>, and is changed to <code>true</code> if the rpc call is
* rejected with an authentication error.
*/
private volatile boolean _usePrivilegedPort = false;
/**
* Create the request, as specified by RFC 1831 (https://tools.ietf.org/html/rfc1831).
*
* @param serviceProgram The program number of the service.
* @param serviceVersion The version number of the service.
* @param serviceProcedure The service procedure number to be called.
* @param credential The authentication credential to be used for the call.
*/
public RpcRequest(
int serviceProgram, int serviceVersion, int serviceProcedure, Credential credential) {
_xid = nextXid();
_serviceProgram = serviceProgram;
_serviceVersion = serviceVersion;
_serviceProcedure = serviceProcedure;
_credential = credential;
}
/** Writes the RPC call header and authentication credentials to the XDR buffer. */
public void marshalling(Xdr xdr) {
xdr.setXid(_xid);
xdr.putInt(_xid);
xdr.putInt(_direction);
xdr.putInt(_rpcVersion);
xdr.putInt(_serviceProgram);
xdr.putInt(_serviceVersion);
xdr.putInt(_serviceProcedure);
_credential.marshalling(xdr);
}
/**
* Returns
*
* <ul>
* <li><code>true</code> if the client should use a privileged local port (below 1024).
* <li><code>false</code> if the client should use any available local port.
* </ul>
*
* .
*/
public boolean isUsePrivilegedPort() {
return _usePrivilegedPort;
}
/**
* Set the flag.
*
* @param usePrivilegedPort
* <ul>
* <li><code>true</code> if the client should use a privileged local port (below 1024).
* <li><code>false</code> if the client should use any available local port.
* </ul>
*/
public void setUsePrivilegedPort(boolean usePrivilegedPort) {
_usePrivilegedPort = usePrivilegedPort;
}
/**
* Returns the next xid, which should be unique for all rpc requests.
*
* @return The next xid, which should be unique for all rpc requests
*/
private static int nextXid() {
return _nextXid.getAndIncrement();
}
/**
* Returns the first xid, based on the system time in nanoseconds (OpenSolaris approach).
*
* @return The first xid, based on the system time in nanoseconds (OpenSolaris approach).
*/
private static int initializeXid() {
long cur = System.nanoTime();
long seconds = cur / 1000000000;
long nano = cur % 1000000000;
int firstXid = (int) ((seconds << 20) | (nano >> 10));
return firstXid;
}
/**
* Start a StringBuilder for use in toString() (for logging).
*
* @return the builder
*/
protected StringBuilder startToString(String requestLabel) {
StringBuilder stringBuilder = new StringBuilder(requestLabel);
stringBuilder.append(" serviceVersion:").append(String.valueOf(_serviceVersion));
stringBuilder.append(" xid:").append(String.valueOf(_xid));
stringBuilder.append(" usePrivilegedPort:").append(String.valueOf(_usePrivilegedPort));
return stringBuilder;
}
/** Returns an appropriate error message. */
public abstract String getErrorMessage();
/**
* Utility function for cloning file handles. These can never be null.
*
* @param fileHandle The file handle to clone.
* @return A clone if <code>fileHandle</code> is not <code>null</code>.
* @throws FileNotFoundException If the file handle is null.
*/
protected static final byte[] cloneFileHandle(byte[] fileHandle) throws FileNotFoundException {
if (fileHandle == null) {
throw new FileNotFoundException("The file handle is null, so this file does not exist.");
}
return fileHandle.clone();
}
/**
* Trims a file name and rejects blank names.
*
* @param name The file name to trim.
* @return The trimmed file name.
* @throws IllegalArgumentException If the file name is blank.
*/
protected static final String trimFileName(String name) throws IllegalArgumentException {
if (StringUtils.isBlank(name)) {
throw new IllegalArgumentException("The file name cannot be blank.");
}
return name.trim();
}
}