mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
NFS server support added to filesystem server, includes mount and portmapper services plus an NFS v3 server.
Not enabled or wired in yet. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@4742 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
48
source/java/org/alfresco/filesys/server/oncrpc/AuthType.java
Normal file
48
source/java/org/alfresco/filesys/server/oncrpc/AuthType.java
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc;
|
||||
|
||||
/**
|
||||
* Authentication Types Class
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public final class AuthType {
|
||||
|
||||
// Authentication type contants
|
||||
|
||||
public static final int Null = 0;
|
||||
public static final int Unix = 1;
|
||||
public static final int Short = 2;
|
||||
public static final int DES = 3;
|
||||
|
||||
// Authentication type strings
|
||||
|
||||
private static final String[] _authTypes = { "Null", "Unix", "Short", "DES" };
|
||||
|
||||
/**
|
||||
* Return the authentication type as string
|
||||
*
|
||||
* @param type int
|
||||
* @return String
|
||||
*/
|
||||
public static final String getTypeAsString(int type) {
|
||||
if ( type < 0 || type >= _authTypes.length)
|
||||
return "" + type;
|
||||
return _authTypes[type];
|
||||
}
|
||||
}
|
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc;
|
||||
|
||||
import org.alfresco.config.ConfigElement;
|
||||
import org.alfresco.filesys.server.SrvSession;
|
||||
import org.alfresco.filesys.server.auth.ClientInfo;
|
||||
import org.alfresco.filesys.server.config.InvalidConfigurationException;
|
||||
import org.alfresco.filesys.server.config.ServerConfiguration;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Default RPC Authenticator Class
|
||||
*
|
||||
* <p>RPC authenticator implementation that allows any client to access the RPC servers.
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class DefaultRpcAuthenticator implements RpcAuthenticator {
|
||||
|
||||
// Debug logging
|
||||
|
||||
private static final Log logger = LogFactory.getLog("org.alfresco.nfs.protocol.auth");
|
||||
|
||||
// Authentication types aupported by this implementation
|
||||
|
||||
private int[] _authTypes = { AuthType.Null, AuthType.Unix };
|
||||
|
||||
/**
|
||||
* Authenticate an RPC client and create a unique session id key.
|
||||
*
|
||||
* @param authType int
|
||||
* @param rpc RpcPacket
|
||||
* @return Object
|
||||
* @throws RpcAuthenticationException
|
||||
*/
|
||||
public Object authenticateRpcClient(int authType, RpcPacket rpc)
|
||||
throws RpcAuthenticationException {
|
||||
|
||||
// Create a unique session key depending on the authentication type
|
||||
|
||||
Object sessKey = null;
|
||||
|
||||
switch (authType) {
|
||||
|
||||
// Null authentication
|
||||
|
||||
case AuthType.Null:
|
||||
sessKey = new Integer(rpc.getClientAddress().hashCode());
|
||||
break;
|
||||
|
||||
// Unix authentication
|
||||
|
||||
case AuthType.Unix:
|
||||
|
||||
// Get the gid and uid from the credentials data in the request
|
||||
|
||||
rpc.positionAtCredentialsData();
|
||||
rpc.skipBytes(4);
|
||||
int nameLen = rpc.unpackInt();
|
||||
rpc.skipBytes(nameLen);
|
||||
|
||||
int gid = rpc.unpackInt();
|
||||
int uid = rpc.unpackInt();
|
||||
|
||||
// Check if the Unix authentication session table is valid
|
||||
|
||||
sessKey = new Long((((long) rpc.getClientAddress().hashCode()) << 32) + (gid << 16) + uid);
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if the session key is valid, if not then the authentication
|
||||
// type is unsupported
|
||||
|
||||
if (sessKey == null)
|
||||
throw new RpcAuthenticationException(Rpc.AuthBadCred, "Unsupported auth type, " + authType);
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("RpcAuth: RPC from " + rpc.getClientDetails()
|
||||
+ ", authType=" + AuthType.getTypeAsString(authType)
|
||||
+ ", sessKey=" + sessKey);
|
||||
|
||||
// Return the session key
|
||||
|
||||
return sessKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the authentication types that are supported by this
|
||||
* implementation.
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public int[] getRpcAuthenticationTypes() {
|
||||
return _authTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the client information for the specified RPC request
|
||||
*
|
||||
* @param sessKey
|
||||
* Object
|
||||
* @param rpc
|
||||
* RpcPacket
|
||||
* @return ClientInfo
|
||||
*/
|
||||
public ClientInfo getRpcClientInformation(Object sessKey, RpcPacket rpc) {
|
||||
|
||||
// Create a client information object to hold the client details
|
||||
|
||||
ClientInfo cInfo = new ClientInfo("", null);
|
||||
|
||||
// Get the authentication type
|
||||
|
||||
int authType = rpc.getCredentialsType();
|
||||
cInfo.setNFSAuthenticationType(authType);
|
||||
|
||||
// Unpack the client details from the RPC request
|
||||
|
||||
switch (authType) {
|
||||
|
||||
// Null authentication
|
||||
|
||||
case AuthType.Null:
|
||||
cInfo.setClientAddress(rpc.getClientAddress().getHostAddress());
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("RpcAuth: Client info, type=" + AuthType.getTypeAsString(authType) + ", addr="
|
||||
+ rpc.getClientAddress().getHostAddress());
|
||||
break;
|
||||
|
||||
// Unix authentication
|
||||
|
||||
case AuthType.Unix:
|
||||
|
||||
// Unpack the credentials data
|
||||
|
||||
rpc.positionAtCredentialsData();
|
||||
rpc.skipBytes(4); // stamp id
|
||||
|
||||
cInfo.setClientAddress(rpc.unpackString());
|
||||
cInfo.setUid(rpc.unpackInt());
|
||||
cInfo.setGid(rpc.unpackInt());
|
||||
|
||||
// Check for an additional groups list
|
||||
|
||||
int grpLen = rpc.unpackInt();
|
||||
if (grpLen > 0) {
|
||||
int[] groups = new int[grpLen];
|
||||
rpc.unpackIntArray(groups);
|
||||
|
||||
cInfo.setGroupsList(groups);
|
||||
}
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("RpcAuth: Client info, type=" + AuthType.getTypeAsString(authType) + ", name="
|
||||
+ cInfo.getClientAddress() + ", uid=" + cInfo.getUid() + ", gid=" + cInfo.getGid() + ", groups=" + grpLen);
|
||||
break;
|
||||
}
|
||||
|
||||
// Return the client information
|
||||
|
||||
return cInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the RPC authenticator
|
||||
*
|
||||
* @param config ServerConfiguration
|
||||
* @param params NameValueList
|
||||
* @throws InvalidConfigurationException
|
||||
*/
|
||||
public void initialize(ServerConfiguration config, ConfigElement params)
|
||||
throws InvalidConfigurationException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current authenticated user context for this thread
|
||||
*
|
||||
* @param sess SrvSession
|
||||
* @param client ClientInfo
|
||||
*/
|
||||
public void setCurrentUser( SrvSession sess, ClientInfo client)
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
/**
|
||||
* Multi-Threaded Tcp Rpc Packet Handler Class
|
||||
*
|
||||
* <p>Adds multi-threaded processing of RPC requests to the standard TCP RPC handler.
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class MultiThreadedTcpRpcPacketHandler extends TcpRpcPacketHandler implements RpcPacketHandler {
|
||||
|
||||
/**
|
||||
* Class constructor to create a TCP RPC handler for a server.
|
||||
*
|
||||
* @param handler TcpRpcSessionHandler
|
||||
* @param sessId int
|
||||
* @param server RpcProcessor
|
||||
* @param socket Socket
|
||||
* @param maxRpcSize int
|
||||
* @throws IOException
|
||||
*/
|
||||
public MultiThreadedTcpRpcPacketHandler(TcpRpcSessionHandler handler, int sessId, RpcProcessor server,
|
||||
Socket socket, int maxRpcSize) throws IOException
|
||||
{
|
||||
super(handler, sessId, server, socket, maxRpcSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the multi-threaded RPC session handler
|
||||
*
|
||||
* @return MultiThreadedTcpRpcSessionHandler
|
||||
*/
|
||||
protected final MultiThreadedTcpRpcSessionHandler getSessionHandler()
|
||||
{
|
||||
return (MultiThreadedTcpRpcSessionHandler) getHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate an RPC packet from the packet pool
|
||||
*
|
||||
* @param maxSize int
|
||||
* @return RpcPacket
|
||||
*/
|
||||
protected RpcPacket allocateRpcPacket(int maxSize)
|
||||
{
|
||||
|
||||
// Use the session handler to allocate the RPC packet
|
||||
|
||||
return getSessionHandler().allocateRpcPacket(maxSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deallocate an RPC packet, return the packet to the pool.
|
||||
*
|
||||
* @param pkt RpcPacket
|
||||
*/
|
||||
protected void deallocateRpcPacket(RpcPacket pkt)
|
||||
{
|
||||
|
||||
// Return the packet to the pool
|
||||
|
||||
if (pkt.isAllocatedFromPool())
|
||||
pkt.getOwnerPacketPool().releasePacket(pkt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an RPC request by passing the request to a pool of worker threads.
|
||||
*
|
||||
* @param rpc RpcPacket
|
||||
* @throws IOException
|
||||
*/
|
||||
protected void processRpc(RpcPacket rpc)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
// Link the RPC request to this handler
|
||||
|
||||
rpc.setPacketHandler(this);
|
||||
|
||||
// Queue the RPC request to the session handlers thread pool for processing
|
||||
|
||||
getSessionHandler().queueRpcRequest(rpc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an RPC response using the TCP socket connection
|
||||
*
|
||||
* @param rpc RpcPacket
|
||||
* @throws IOException
|
||||
*/
|
||||
public void sendRpcResponse(RpcPacket rpc)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
// Send the RPC response
|
||||
|
||||
sendRpc(rpc);
|
||||
}
|
||||
}
|
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
import org.alfresco.filesys.server.NetworkServer;
|
||||
|
||||
/**
|
||||
* Multi-threaded TCP RPC Session Handler Class
|
||||
*
|
||||
* <p>Extend the basic TCP RPC handler class to process RPC requests using a thread pool.
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class MultiThreadedTcpRpcSessionHandler extends TcpRpcSessionHandler {
|
||||
|
||||
// Constants
|
||||
//
|
||||
// Default packet pool size
|
||||
|
||||
public static final int DefaultPacketPoolSize = 50;
|
||||
public static final int DefaultSmallPacketSize = 512;
|
||||
|
||||
// RPC packet pool
|
||||
|
||||
private RpcPacketPool m_packetPool;
|
||||
|
||||
// Request handler thread pool
|
||||
|
||||
private RpcRequestThreadPool m_threadPool;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param name String
|
||||
* @param protocol String
|
||||
* @param rpcServer RpcProcessor
|
||||
* @param server NetworkServer
|
||||
* @param addr InetAddress
|
||||
* @param port int
|
||||
* @param maxSize int
|
||||
*/
|
||||
public MultiThreadedTcpRpcSessionHandler(String name, String protocol, RpcProcessor rpcServer,
|
||||
NetworkServer server, InetAddress addr, int port, int maxSize)
|
||||
{
|
||||
super(name, protocol, rpcServer, server, addr, port, maxSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the session socket handler
|
||||
*
|
||||
* @param server
|
||||
* @throws IOException
|
||||
*/
|
||||
public void initializeSessionHandler(NetworkServer server)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
// If the packet pool has not been created, create a default packet pool
|
||||
|
||||
if (m_packetPool == null)
|
||||
m_packetPool = new RpcPacketPool(DefaultSmallPacketSize, DefaultPacketPoolSize, getMaximumRpcSize(),
|
||||
DefaultPacketPoolSize);
|
||||
|
||||
// Create the RPC request handling thread pool, if not already created
|
||||
|
||||
if (m_threadPool == null)
|
||||
m_threadPool = new RpcRequestThreadPool(getHandlerName(), getRpcProcessor());
|
||||
|
||||
// Call the base class initialization
|
||||
|
||||
super.initializeSessionHandler(server);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate an RPC packet from the packet pool
|
||||
*
|
||||
* @param size int
|
||||
* @return RpcPacket
|
||||
*/
|
||||
protected final RpcPacket allocateRpcPacket(int size)
|
||||
{
|
||||
|
||||
// Allocate an RPC packet from the packet pool
|
||||
|
||||
return m_packetPool.allocatePacket(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue an RPC request to the thread pool for processing
|
||||
*
|
||||
* @param rpc RpcPacket
|
||||
*/
|
||||
protected final void queueRpcRequest(RpcPacket rpc)
|
||||
{
|
||||
|
||||
// DEBUG
|
||||
|
||||
// Debug.println("MTRpcSessHandler Queue rpc=" + rpc.toString());
|
||||
|
||||
// Queue the RPC request to the thread pool for processing
|
||||
|
||||
m_threadPool.queueRpcRequest(rpc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a multi-threaded packet handler for the new session
|
||||
*
|
||||
* @param sessId int
|
||||
* @param sock Socket
|
||||
* @return TcpRpcPacketHandler
|
||||
* @throws IOException
|
||||
*/
|
||||
protected TcpRpcPacketHandler createPacketHandler(int sessId, Socket sock)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
// Create a multi-threaded packet handler to use the session handlers thread pool to
|
||||
// process the RPC requests
|
||||
|
||||
return new MultiThreadedTcpRpcPacketHandler(this, sessId, getRpcProcessor(), sock, getMaximumRpcSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the packet pool size
|
||||
*
|
||||
* @param smallSize int
|
||||
* @param smallPool int
|
||||
* @param largeSize int
|
||||
* @param largePool int
|
||||
*/
|
||||
public final void setPacketPool(int smallSize, int smallPool, int largeSize, int largePool)
|
||||
{
|
||||
|
||||
// Create the packet pool, if not already initialized
|
||||
|
||||
if (m_packetPool == null)
|
||||
{
|
||||
|
||||
// Create the packet pool
|
||||
|
||||
m_packetPool = new RpcPacketPool(smallSize, smallPool, largeSize, largePool);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the packet pool size
|
||||
*
|
||||
* @param poolSize int
|
||||
*/
|
||||
public final void setPacketPool(int poolSize)
|
||||
{
|
||||
|
||||
// Create the packet pool, if not already initialized
|
||||
|
||||
if (m_packetPool == null)
|
||||
{
|
||||
|
||||
// Create the packet pool
|
||||
|
||||
m_packetPool = new RpcPacketPool(DefaultSmallPacketSize, poolSize, getMaximumRpcSize(), poolSize);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the packet pool
|
||||
*
|
||||
* @param pktPool RpcPacketPool
|
||||
*/
|
||||
public final void setPacketPool(RpcPacketPool pktPool)
|
||||
{
|
||||
|
||||
// Set the packet pool, if not already initialized
|
||||
|
||||
if (m_packetPool == null)
|
||||
m_packetPool = pktPool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the thread pool size
|
||||
*
|
||||
* @param numThreads int
|
||||
*/
|
||||
public final void setThreadPool(int numThreads)
|
||||
{
|
||||
|
||||
// Create the thread pool, if not already initialized
|
||||
|
||||
if (m_threadPool == null)
|
||||
{
|
||||
|
||||
// Create the thread pool
|
||||
|
||||
m_threadPool = new RpcRequestThreadPool(getHandlerName(), numThreads, getRpcProcessor());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the thread pool
|
||||
*
|
||||
* @param threadPool RpcRequestThreadPool
|
||||
*/
|
||||
public final void setThreadPool(RpcRequestThreadPool threadPool)
|
||||
{
|
||||
|
||||
// Set the thread pool, if not already initialized
|
||||
|
||||
if (m_threadPool == null)
|
||||
m_threadPool = threadPool;
|
||||
}
|
||||
}
|
@@ -0,0 +1,420 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
import org.alfresco.filesys.server.NetworkServer;
|
||||
|
||||
/**
|
||||
* Multi-Threaded UDP RPC Datagram Handler Class
|
||||
*
|
||||
* <p>Extend the basic UDP RPC handler class to process RPC requests using a thread pool.
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class MultiThreadedUdpRpcDatagramHandler extends UdpRpcDatagramHandler implements RpcPacketHandler {
|
||||
|
||||
// Constants
|
||||
//
|
||||
// Default packet pool size
|
||||
|
||||
public static final int DefaultPacketPoolSize = 50;
|
||||
public static final int DefaultSmallPacketSize = 512;
|
||||
|
||||
// RPC packet pool
|
||||
|
||||
private RpcPacketPool m_packetPool;
|
||||
|
||||
// Request handler thread pool
|
||||
|
||||
private RpcRequestThreadPool m_threadPool;
|
||||
|
||||
// RPC response queue
|
||||
|
||||
private RpcRequestQueue m_txQueue;
|
||||
|
||||
// Datagram sender thread
|
||||
|
||||
private DatagramSender m_txThread;
|
||||
|
||||
// Current receive RPC packet
|
||||
|
||||
private RpcPacket m_rxPkt;
|
||||
|
||||
/**
|
||||
* Datagram Sender Thread Inner Class
|
||||
*/
|
||||
protected class DatagramSender implements Runnable
|
||||
{
|
||||
|
||||
// Worker thread
|
||||
|
||||
private Thread mi_thread;
|
||||
|
||||
// RPC sender thread datagram packet
|
||||
|
||||
private DatagramPacket mi_txPkt;
|
||||
|
||||
// Shutdown flag
|
||||
|
||||
private boolean mi_shutdown = false;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param name String
|
||||
*/
|
||||
public DatagramSender(String name)
|
||||
{
|
||||
|
||||
// Create the worker thread
|
||||
|
||||
mi_thread = new Thread(this);
|
||||
mi_thread.setName(name);
|
||||
mi_thread.setDaemon(true);
|
||||
mi_thread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Request the worker thread to shutdown
|
||||
*/
|
||||
public final void shutdownRequest()
|
||||
{
|
||||
mi_shutdown = true;
|
||||
try
|
||||
{
|
||||
mi_thread.interrupt();
|
||||
} catch (Exception ex)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the thread
|
||||
*/
|
||||
public void run()
|
||||
{
|
||||
|
||||
// Allocate the datagram packet for sending the RPC responses
|
||||
|
||||
mi_txPkt = new DatagramPacket(new byte[4], 4);
|
||||
|
||||
// Loop until shutdown
|
||||
|
||||
RpcPacket rpc = null;
|
||||
|
||||
while (mi_shutdown == false)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
// Wait for an RPC response to be queued
|
||||
|
||||
rpc = m_txQueue.removeRequest();
|
||||
} catch (InterruptedException ex)
|
||||
{
|
||||
|
||||
// Check for shutdown
|
||||
|
||||
if (mi_shutdown == true)
|
||||
break;
|
||||
}
|
||||
|
||||
// If the request is valid process it
|
||||
|
||||
if (rpc != null)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
// Initialize the datagram packet for this response
|
||||
|
||||
mi_txPkt.setAddress(rpc.getClientAddress());
|
||||
mi_txPkt.setPort(rpc.getClientPort());
|
||||
mi_txPkt.setData(rpc.getBuffer(), rpc.getOffset(), rpc.getLength());
|
||||
|
||||
// Send the RPC response
|
||||
|
||||
getDatagramSocket().send(mi_txPkt);
|
||||
} catch (Throwable ex)
|
||||
{
|
||||
|
||||
// Do not display errors if shutting down
|
||||
|
||||
if (mi_shutdown == false)
|
||||
{
|
||||
logger.debug("DatagramSender " + Thread.currentThread().getName() + ":");
|
||||
logger.debug(ex);
|
||||
}
|
||||
} finally
|
||||
{
|
||||
|
||||
// Release the RPC packet back to the packet pool
|
||||
|
||||
if (rpc.isAllocatedFromPool())
|
||||
rpc.getOwnerPacketPool().releasePacket(rpc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param name String
|
||||
* @param protocol String
|
||||
* @param rpcServer RpcProcessor
|
||||
* @param server NetworkServer
|
||||
* @param addr InetAddress
|
||||
* @param port int
|
||||
* @param maxSize int
|
||||
*/
|
||||
public MultiThreadedUdpRpcDatagramHandler(String name, String protocol, RpcProcessor rpcServer,
|
||||
NetworkServer server, InetAddress addr, int port, int maxSize)
|
||||
{
|
||||
super(name, protocol, rpcServer, server, addr, port, maxSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the session handler
|
||||
*
|
||||
* @param server NetworkServer
|
||||
* @throws IOException
|
||||
*/
|
||||
public void initializeSessionHandler(NetworkServer server)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
// Create the RPC response queue
|
||||
|
||||
m_txQueue = new RpcRequestQueue();
|
||||
|
||||
// Create the datagram sender thread
|
||||
|
||||
m_txThread = new DatagramSender("UDP_Tx_" + getProtocolName());
|
||||
|
||||
// If the packet pool has not been created, create a default packet pool
|
||||
|
||||
if (m_packetPool == null)
|
||||
m_packetPool = new RpcPacketPool(DefaultSmallPacketSize, DefaultPacketPoolSize, getMaximumDatagramSize(),
|
||||
DefaultPacketPoolSize);
|
||||
|
||||
// Create the RPC request handling thread pool, if not already created
|
||||
|
||||
if (m_threadPool == null)
|
||||
m_threadPool = new RpcRequestThreadPool(getHandlerName(), getRpcProcessor());
|
||||
|
||||
// Call the base class initialization
|
||||
|
||||
super.initializeSessionHandler(server);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the RPC request
|
||||
*
|
||||
* @param pkt DatagramPacket
|
||||
* @return boolean
|
||||
* @throws IOException
|
||||
*/
|
||||
protected boolean processDatagram(DatagramPacket pkt)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
// Make sure that the received data is using the same buffer that we allocated in the
|
||||
// allocateBuffer() method, if not the buffer did not come from the packet pool.
|
||||
|
||||
if (pkt.getData() != m_rxPkt.getBuffer())
|
||||
throw new IOException("Received datagram is not in expected buffer");
|
||||
|
||||
// Update the RPC packet details
|
||||
|
||||
m_rxPkt.setBuffer(pkt.getData(), 0, pkt.getLength());
|
||||
|
||||
// Set the client details
|
||||
|
||||
m_rxPkt.setClientDetails(pkt.getAddress(), pkt.getPort(), Rpc.UDP);
|
||||
|
||||
// Set the packet handler interface to be used to send the RPC reply
|
||||
|
||||
m_rxPkt.setPacketHandler(this);
|
||||
|
||||
// Queue the request to the thread pool for processing
|
||||
|
||||
queueRpcRequest(m_rxPkt);
|
||||
|
||||
// Indicate that the datagram buffer cannot be re-used, the main datagram receiving thread must
|
||||
// allocate a new buffer for the next request.
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue an RPC request to the thread pool for processing
|
||||
*
|
||||
* @param rpc RpcPacket
|
||||
*/
|
||||
protected final void queueRpcRequest(RpcPacket rpc)
|
||||
{
|
||||
|
||||
// Queue the RPC request to the thread pool for processing
|
||||
|
||||
m_threadPool.queueRpcRequest(rpc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a buffer for the next datagram
|
||||
*
|
||||
* @param bufSize int
|
||||
* @return byte[]
|
||||
*/
|
||||
protected byte[] allocateBuffer(int bufSize)
|
||||
{
|
||||
|
||||
// Allocate an RPC packet from the packet pool
|
||||
|
||||
m_rxPkt = m_packetPool.allocatePacket(bufSize);
|
||||
|
||||
// Return the buffer from the RPC packet
|
||||
|
||||
return m_rxPkt.getBuffer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an RPC response using the datagram socket
|
||||
*
|
||||
* @param rpc RpcPacket
|
||||
* @throws IOException
|
||||
*/
|
||||
public void sendRpcResponse(RpcPacket rpc)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
// Queue the RPC response to the datagram sender thread
|
||||
|
||||
m_txQueue.addRequest(rpc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the packet pool size
|
||||
*
|
||||
* @param smallSize int
|
||||
* @param smallPool int
|
||||
* @param largeSize int
|
||||
* @param largePool int
|
||||
*/
|
||||
public final void setPacketPool(int smallSize, int smallPool, int largeSize, int largePool)
|
||||
{
|
||||
|
||||
// Create the packet pool, if not already initialized
|
||||
|
||||
if (m_packetPool == null)
|
||||
{
|
||||
|
||||
// Create the packet pool
|
||||
|
||||
m_packetPool = new RpcPacketPool(smallSize, smallPool, largeSize, largePool);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the packet pool size
|
||||
*
|
||||
* @param poolSize int
|
||||
*/
|
||||
public final void setPacketPool(int poolSize)
|
||||
{
|
||||
|
||||
// Create the packet pool, if not already initialized
|
||||
|
||||
if (m_packetPool == null)
|
||||
{
|
||||
|
||||
// Create the packet pool
|
||||
|
||||
m_packetPool = new RpcPacketPool(DefaultSmallPacketSize, poolSize, getMaximumDatagramSize(), poolSize);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the packet pool
|
||||
*
|
||||
* @param pktPool RpcPacketPool
|
||||
*/
|
||||
public final void setPacketPool(RpcPacketPool pktPool)
|
||||
{
|
||||
|
||||
// Set the packet pool, if not already initialized
|
||||
|
||||
if (m_packetPool == null)
|
||||
m_packetPool = pktPool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the thread pool size
|
||||
*
|
||||
* @param numThreads int
|
||||
*/
|
||||
public final void setThreadPool(int numThreads)
|
||||
{
|
||||
|
||||
// Create the thread pool, if not already initialized
|
||||
|
||||
if (m_threadPool == null)
|
||||
{
|
||||
|
||||
// Create the thread pool
|
||||
|
||||
m_threadPool = new RpcRequestThreadPool(getHandlerName(), numThreads, getRpcProcessor());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the thread pool
|
||||
*
|
||||
* @param threadPool RpcRequestThreadPool
|
||||
*/
|
||||
public final void setThreadPool(RpcRequestThreadPool threadPool)
|
||||
{
|
||||
|
||||
// Set the thread pool, if not already initialized
|
||||
|
||||
if (m_threadPool == null)
|
||||
m_threadPool = threadPool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the session handler
|
||||
*
|
||||
* @param server NetworkServer
|
||||
*/
|
||||
public void closeSessionHandler(NetworkServer server)
|
||||
{
|
||||
|
||||
// Shutdown the datagram sender thread
|
||||
|
||||
m_txThread.shutdownRequest();
|
||||
|
||||
// Call the base class
|
||||
|
||||
super.closeSessionHandler(server);
|
||||
}
|
||||
}
|
145
source/java/org/alfresco/filesys/server/oncrpc/PortMapping.java
Normal file
145
source/java/org/alfresco/filesys/server/oncrpc/PortMapping.java
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc;
|
||||
|
||||
/**
|
||||
* Port Details Class
|
||||
*
|
||||
* <p>Contains the details of an RPC service registered with the PortMapper service.
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class PortMapping {
|
||||
|
||||
// Program id and version
|
||||
|
||||
private int m_programId;
|
||||
private int m_versionId;
|
||||
|
||||
// Protocol type (UDP or TCP)
|
||||
|
||||
private int m_protocol;
|
||||
|
||||
// Port
|
||||
|
||||
private int m_port;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param progId int
|
||||
* @param verId int
|
||||
* @param protocol int
|
||||
* @param port int
|
||||
*/
|
||||
public PortMapping(int progId, int verId, int protocol, int port)
|
||||
{
|
||||
m_programId = progId;
|
||||
m_versionId = verId;
|
||||
m_protocol = protocol;
|
||||
m_port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the program id
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getProgramId()
|
||||
{
|
||||
return m_programId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the version id
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getVersionId()
|
||||
{
|
||||
return m_versionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the protocol type
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getProtocol()
|
||||
{
|
||||
return m_protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the port number
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getPort()
|
||||
{
|
||||
return m_port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash code for the port mapping
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public int hashCode()
|
||||
{
|
||||
|
||||
// Create a hash code from the program id + version + protocol
|
||||
|
||||
return generateHashCode(m_programId, m_versionId, m_protocol);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a hash code for the specified program, version and protocol
|
||||
*
|
||||
* @param progId int
|
||||
* @param verId int
|
||||
* @param proto int
|
||||
* @return int
|
||||
*/
|
||||
public final static int generateHashCode(int progId, int verId, int proto)
|
||||
{
|
||||
|
||||
// Create a hash code from the program id + version + protocol
|
||||
|
||||
return (progId << 16) + (verId << 8) + proto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the port details as a string
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
StringBuffer str = new StringBuffer(64);
|
||||
|
||||
str.append("[");
|
||||
str.append(getProgramId());
|
||||
str.append(":");
|
||||
str.append(getVersionId());
|
||||
str.append(getProtocol() == Rpc.TCP ? ",TCP," : ",UDP,");
|
||||
str.append(getPort());
|
||||
str.append("]");
|
||||
|
||||
return str.toString();
|
||||
}
|
||||
}
|
104
source/java/org/alfresco/filesys/server/oncrpc/Rpc.java
Normal file
104
source/java/org/alfresco/filesys/server/oncrpc/Rpc.java
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc;
|
||||
|
||||
/**
|
||||
* ONC/RPC Constants Class
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class Rpc {
|
||||
|
||||
// RPC length/flags
|
||||
|
||||
public static final int LastFragment = 0x80000000;
|
||||
public static final int LengthMask = 0x7FFFFFFF;
|
||||
|
||||
// RPC message types
|
||||
|
||||
public static final int Call = 0;
|
||||
public static final int Reply = 1;
|
||||
|
||||
// Call status
|
||||
|
||||
public static final int CallAccepted = 0;
|
||||
public static final int CallDenied = 1;
|
||||
|
||||
// Required RPC version
|
||||
|
||||
public static final int RpcVersion = 2;
|
||||
|
||||
// Call accepted status codes
|
||||
|
||||
public static final int StsSuccess = 0; // RPC executed successfully
|
||||
public static final int StsProgUnavail = 1; // program not available
|
||||
public static final int StsProgMismatch = 2; // program version mismatch
|
||||
public static final int StsProcUnavail = 3; // program does not support procedure
|
||||
public static final int StsBadArgs = 4; // bad arguments in request
|
||||
|
||||
// Call rejected status codes
|
||||
|
||||
public static final int StsRpcMismatch = 0; // RPC version number does not equal 2
|
||||
public static final int StsAuthError = 1; // authentication error
|
||||
|
||||
// Authentication failure status codes
|
||||
|
||||
public static final int AuthBadCred = 1; // bad credentials
|
||||
public static final int AuthRejectCred = 2; // client must begin new session
|
||||
public static final int AuthBadVerf = 3; // bad verifier
|
||||
public static final int AuthRejectedVerf = 4; // verifier rejected or replayed
|
||||
public static final int AuthTooWeak = 5; // rejected for security reasons
|
||||
|
||||
// True/false values
|
||||
|
||||
public static final int True = 1;
|
||||
public static final int False = 0;
|
||||
|
||||
// Protocol ids
|
||||
|
||||
public static final int TCP = 6;
|
||||
public static final int UDP = 17;
|
||||
|
||||
/**
|
||||
* Return a program id as a service name
|
||||
*
|
||||
* @param progId int
|
||||
* @return String
|
||||
*/
|
||||
public final static String getServiceName(int progId)
|
||||
{
|
||||
String svcName = null;
|
||||
|
||||
switch (progId)
|
||||
{
|
||||
case 100005:
|
||||
svcName = "Mount";
|
||||
break;
|
||||
case 100003:
|
||||
svcName = "NFS";
|
||||
break;
|
||||
case 100000:
|
||||
svcName = "Portmap";
|
||||
break;
|
||||
default:
|
||||
svcName = "" + progId;
|
||||
break;
|
||||
}
|
||||
|
||||
return svcName;
|
||||
}
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc;
|
||||
|
||||
/**
|
||||
* RPC Authentication Exception Class
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class RpcAuthenticationException extends Exception {
|
||||
|
||||
// Object version id
|
||||
|
||||
private static final long serialVersionUID = 7599358351809146330L;
|
||||
|
||||
// Authentication failure error code
|
||||
|
||||
private int m_authError;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param authError int
|
||||
*/
|
||||
public RpcAuthenticationException(int authError)
|
||||
{
|
||||
m_authError = authError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param authError int
|
||||
* @param msg String
|
||||
*/
|
||||
public RpcAuthenticationException(int authError, String msg)
|
||||
{
|
||||
super(msg);
|
||||
m_authError = authError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the authentication error code
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getAuthenticationErrorCode()
|
||||
{
|
||||
return m_authError;
|
||||
}
|
||||
}
|
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc;
|
||||
|
||||
import org.alfresco.config.ConfigElement;
|
||||
import org.alfresco.filesys.server.SrvSession;
|
||||
import org.alfresco.filesys.server.auth.ClientInfo;
|
||||
import org.alfresco.filesys.server.config.InvalidConfigurationException;
|
||||
import org.alfresco.filesys.server.config.ServerConfiguration;
|
||||
|
||||
/**
|
||||
* RPC Authenticator Interface
|
||||
*
|
||||
* <p>Provides authentication support for ONC/RPC requests.
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public interface RpcAuthenticator
|
||||
{
|
||||
/**
|
||||
* Initialize the RPC authenticator
|
||||
*
|
||||
* @param config ServerConfiguration
|
||||
* @param params NameValueList
|
||||
* @exception InvalidConfigurationException
|
||||
*/
|
||||
public void initialize(ServerConfiguration config, ConfigElement params)
|
||||
throws InvalidConfigurationException;
|
||||
|
||||
/**
|
||||
* Authenticate an RPC client using the credentials within the RPC request.
|
||||
* The object that is returned is used as the key to find the associated
|
||||
* session object.
|
||||
*
|
||||
* @param authType int
|
||||
* @param rpc RpcPacket
|
||||
* @return Object
|
||||
* @exception RpcAuthenticationException
|
||||
*/
|
||||
public Object authenticateRpcClient(int authType, RpcPacket rpc)
|
||||
throws RpcAuthenticationException;
|
||||
|
||||
/**
|
||||
* Get RPC client information from the RPC request.
|
||||
*
|
||||
* <p>
|
||||
* This method is called when a new session object is created by an RPC
|
||||
* server.
|
||||
*
|
||||
* @param sessKey Object
|
||||
* @param rpc RpcPacket
|
||||
* @return ClientInfo
|
||||
*/
|
||||
public ClientInfo getRpcClientInformation(Object sessKey, RpcPacket rpc);
|
||||
|
||||
/**
|
||||
* Return a list of the authentication types that the RPC authenticator
|
||||
* implementation supports. The authentication types are specified in the
|
||||
* AuthType class.
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public int[] getRpcAuthenticationTypes();
|
||||
|
||||
/**
|
||||
* Set the current authenticated user context for this thread
|
||||
*
|
||||
* @param sess SrvSession
|
||||
* @param client ClientInfo
|
||||
*/
|
||||
public void setCurrentUser( SrvSession sess, ClientInfo client);
|
||||
}
|
146
source/java/org/alfresco/filesys/server/oncrpc/RpcClient.java
Normal file
146
source/java/org/alfresco/filesys/server/oncrpc/RpcClient.java
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
/**
|
||||
* RPC Client Class
|
||||
*
|
||||
* <p>Provides either a socket or datagram connection to an RPC server.
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public abstract class RpcClient {
|
||||
|
||||
// Network address and port to connect to on the remote RPC server
|
||||
|
||||
private InetAddress m_server;
|
||||
private int m_port;
|
||||
|
||||
// Protocol type
|
||||
|
||||
private int m_protocol;
|
||||
|
||||
// Maximum RPC size to send/receive
|
||||
|
||||
private int m_maxRpcSize;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param addr InetAddress
|
||||
* @param port int
|
||||
* @param proto int
|
||||
* @param maxRpcSize int
|
||||
* @throws IOException
|
||||
* @throws SocketException
|
||||
*/
|
||||
protected RpcClient(InetAddress addr, int port, int proto, int maxRpcSize) throws IOException, SocketException
|
||||
{
|
||||
|
||||
// Save the server address, port and the protocol type
|
||||
|
||||
m_server = addr;
|
||||
m_port = port;
|
||||
|
||||
m_protocol = proto;
|
||||
|
||||
// Set the maximum RPC size to send/recieve
|
||||
|
||||
m_maxRpcSize = maxRpcSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum RPC size
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getMaximumRpcSize()
|
||||
{
|
||||
return m_maxRpcSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the server address
|
||||
*
|
||||
* @return InetAddress
|
||||
*/
|
||||
public final InetAddress getServerAddress()
|
||||
{
|
||||
return m_server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the server port
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getServerPort()
|
||||
{
|
||||
return m_port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the protocol type
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int isProtocol()
|
||||
{
|
||||
return m_protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an RPC request to the server
|
||||
*
|
||||
* @param rpc RpcPacket
|
||||
* @param rxRpc RpcPacket
|
||||
* @return RpcPacket
|
||||
* @throws IOException
|
||||
*/
|
||||
public abstract RpcPacket sendRPC(RpcPacket rpc, RpcPacket rxRpc)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Close the connection to the remote RPC server
|
||||
*/
|
||||
public abstract void closeConnection();
|
||||
|
||||
/**
|
||||
* Return the RPC connection details as a string
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
StringBuffer str = new StringBuffer();
|
||||
|
||||
str.append("[");
|
||||
str.append(isProtocol() == Rpc.TCP ? "TCP:" : "UDP:");
|
||||
str.append(getServerAddress().getHostAddress());
|
||||
str.append(":");
|
||||
str.append(getServerPort());
|
||||
|
||||
str.append(",");
|
||||
str.append(getMaximumRpcSize());
|
||||
str.append("]");
|
||||
|
||||
return str.toString();
|
||||
}
|
||||
}
|
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
import org.alfresco.filesys.server.NetworkServer;
|
||||
import org.alfresco.filesys.server.config.ServerConfiguration;
|
||||
import org.alfresco.filesys.server.oncrpc.portmap.PortMapper;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* RPC Network Server Abstract Class
|
||||
*
|
||||
* <p>Provides the base class for RPC servers (such as mount and NFS).
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public abstract class RpcNetworkServer extends NetworkServer implements RpcProcessor {
|
||||
|
||||
// Debug logging
|
||||
|
||||
protected static final Log logger = LogFactory.getLog("org.alfresco.nfs.protocol");
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param name String
|
||||
* @param config ServerConfiguration
|
||||
*/
|
||||
public RpcNetworkServer(String name, ServerConfiguration config)
|
||||
{
|
||||
super(name, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a port/protocol for the RPC server
|
||||
*
|
||||
* @param mapping PortMapping
|
||||
* @throws IOException
|
||||
*/
|
||||
protected final void registerRPCServer(PortMapping mapping)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
// Call the main registration method
|
||||
|
||||
PortMapping[] mappings = new PortMapping[1];
|
||||
mappings[0] = mapping;
|
||||
|
||||
registerRPCServer(mappings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a set of ports/protocols for the RPC server
|
||||
*
|
||||
* @param mappings PortMapping[]
|
||||
* @throws IOException
|
||||
*/
|
||||
protected final void registerRPCServer(PortMapping[] mappings)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
// Connect to the local portmapper service to register the RPC service
|
||||
|
||||
InetAddress localHost = InetAddress.getByName("127.0.0.1");
|
||||
|
||||
TcpRpcClient rpcClient = new TcpRpcClient(localHost, PortMapper.DefaultPort, 512);
|
||||
|
||||
// Allocate RPC request and response packets
|
||||
|
||||
RpcPacket setPortRpc = new RpcPacket(512);
|
||||
RpcPacket rxRpc = new RpcPacket(512);
|
||||
|
||||
// Loop through the port mappings and register each port with the portmapper service
|
||||
|
||||
for (int i = 0; i < mappings.length; i++)
|
||||
{
|
||||
|
||||
// Build the RPC request header
|
||||
|
||||
setPortRpc.buildRequestHeader(PortMapper.ProgramId, PortMapper.VersionId, PortMapper.ProcSet, 0, null, 0,
|
||||
null);
|
||||
|
||||
// Pack the request parameters and set the request length
|
||||
|
||||
setPortRpc.packPortMapping(mappings[i]);
|
||||
setPortRpc.setLength();
|
||||
|
||||
// Send the RPC request and receive a response
|
||||
|
||||
rxRpc = rpcClient.sendRPC(setPortRpc, rxRpc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a port/protocol for the RPC server
|
||||
*
|
||||
* @param mapping PortMapping
|
||||
* @throws IOException
|
||||
*/
|
||||
protected final void unregisterRPCServer(PortMapping mapping)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
// Call the main unregister ports method
|
||||
|
||||
PortMapping[] mappings = new PortMapping[1];
|
||||
mappings[0] = mapping;
|
||||
|
||||
unregisterRPCServer(mappings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a set of ports/protocols for the RPC server
|
||||
*
|
||||
* @param mappings PortMapping[]
|
||||
* @throws IOException
|
||||
*/
|
||||
protected final void unregisterRPCServer(PortMapping[] mappings)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
// Connect to the local portmapper service to unregister the RPC service
|
||||
|
||||
InetAddress localHost = InetAddress.getByName("127.0.0.1");
|
||||
|
||||
TcpRpcClient rpcClient = new TcpRpcClient(localHost, PortMapper.DefaultPort, 512);
|
||||
|
||||
// Allocate RPC request and response packets
|
||||
|
||||
RpcPacket setPortRpc = new RpcPacket(512);
|
||||
RpcPacket rxRpc = new RpcPacket(512);
|
||||
|
||||
// Loop through the port mappings and unregister each port with the portmapper service
|
||||
|
||||
for (int i = 0; i < mappings.length; i++)
|
||||
{
|
||||
|
||||
// Build the RPC request header
|
||||
|
||||
setPortRpc.buildRequestHeader(PortMapper.ProgramId, PortMapper.VersionId, PortMapper.ProcUnSet, 0, null, 0,
|
||||
null);
|
||||
|
||||
// Pack the request parameters and set the request length
|
||||
|
||||
setPortRpc.packPortMapping(mappings[i]);
|
||||
setPortRpc.setLength();
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("[" + getProtocolName() + "] UnRegister server RPC " + setPortRpc.toString());
|
||||
|
||||
// Send the RPC request and receive a response
|
||||
|
||||
rxRpc = rpcClient.sendRPC(setPortRpc, rxRpc);
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("[" + getProtocolName() + "] UnRegister response " + rxRpc.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the RPC server
|
||||
*/
|
||||
public abstract void startServer();
|
||||
|
||||
/**
|
||||
* Shutdown the RPC server
|
||||
*
|
||||
* @param immediate boolean
|
||||
*/
|
||||
public abstract void shutdownServer(boolean immediate);
|
||||
|
||||
/**
|
||||
* Process an RPC request
|
||||
*
|
||||
* @param rpc RpcPacket
|
||||
* @return RpcPacket
|
||||
* @throws IOException
|
||||
*/
|
||||
public abstract RpcPacket processRpc(RpcPacket rpc)
|
||||
throws IOException;
|
||||
}
|
1319
source/java/org/alfresco/filesys/server/oncrpc/RpcPacket.java
Normal file
1319
source/java/org/alfresco/filesys/server/oncrpc/RpcPacket.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* RPC Packet Handler Interface
|
||||
*
|
||||
* <p>Interface used by an RpcPacket to send a response RPC via either TCP or UDP.
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public interface RpcPacketHandler {
|
||||
|
||||
/**
|
||||
* Send an RPC response
|
||||
*
|
||||
* @param rpc RpcPacket
|
||||
* @exception IOException
|
||||
*/
|
||||
public void sendRpcResponse(RpcPacket rpc)
|
||||
throws IOException;
|
||||
}
|
@@ -0,0 +1,439 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Rpc Packet Pool Class
|
||||
*
|
||||
* <p>Contains a pool of small and large RpcPacket objects for use by multi-threaded RPC servers.
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class RpcPacketPool {
|
||||
|
||||
// Debug logging
|
||||
|
||||
private static final Log logger = LogFactory.getLog(RpcPacketPool.class);
|
||||
|
||||
// Constants
|
||||
//
|
||||
// Default small/large packet sizes
|
||||
|
||||
public static final int DefaultSmallSize = 512;
|
||||
public static final int DefaultLargeSize = 32768;
|
||||
public static final int DefaultSmallLimit = -1; // no allocation limit
|
||||
public static final int DefaultLargeLimit = -1; // " " "
|
||||
|
||||
// Small/large packet lists
|
||||
|
||||
private Vector m_smallPackets;
|
||||
private Vector m_largePackets;
|
||||
|
||||
// Small packet size and maximum allowed packets
|
||||
|
||||
private int m_smallPktSize;
|
||||
private int m_smallPktLimit;
|
||||
|
||||
// Large packet size and maximum allowed packets
|
||||
|
||||
private int m_largePktSize;
|
||||
private int m_largePktLimit;
|
||||
|
||||
// Count of allocated small/large packets
|
||||
|
||||
private int m_smallPktCount;
|
||||
private int m_largePktCount;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public RpcPacketPool()
|
||||
{
|
||||
|
||||
// Create the small/large packet lists
|
||||
|
||||
m_smallPackets = new Vector();
|
||||
m_largePackets = new Vector();
|
||||
|
||||
// Set the packet sizes/limits
|
||||
|
||||
m_smallPktSize = DefaultSmallSize;
|
||||
m_smallPktLimit = DefaultSmallLimit;
|
||||
|
||||
m_largePktSize = DefaultLargeSize;
|
||||
m_largePktLimit = DefaultLargeLimit;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param smallSize int
|
||||
* @param smallLimit int
|
||||
* @param largeSize int
|
||||
* @param largeLimit int
|
||||
*/
|
||||
public RpcPacketPool(int smallSize, int smallLimit, int largeSize, int largeLimit)
|
||||
{
|
||||
|
||||
// Create the small/large packet lists
|
||||
|
||||
m_smallPackets = new Vector();
|
||||
m_largePackets = new Vector();
|
||||
|
||||
// Save the packet sizes/limits
|
||||
|
||||
m_smallPktSize = smallSize;
|
||||
m_smallPktLimit = smallLimit;
|
||||
|
||||
m_largePktSize = largeSize;
|
||||
m_largePktLimit = largeLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param largeSize int
|
||||
* @param largeLimit int
|
||||
*/
|
||||
public RpcPacketPool(int largeSize, int largeLimit)
|
||||
{
|
||||
|
||||
// Create the small/large packet lists
|
||||
|
||||
m_smallPackets = new Vector();
|
||||
m_largePackets = new Vector();
|
||||
|
||||
// Save the packet sizes/limits
|
||||
|
||||
m_smallPktSize = DefaultSmallSize;
|
||||
m_smallPktLimit = largeLimit;
|
||||
|
||||
m_largePktSize = largeSize;
|
||||
m_largePktLimit = largeLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the small packet size
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getSmallPacketSize()
|
||||
{
|
||||
return m_smallPktSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the count of allocated small packets
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getSmallPacketCount()
|
||||
{
|
||||
return m_smallPktCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the small packet allocation limit
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getSmallPacketAllocationLimit()
|
||||
{
|
||||
return m_smallPktLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the count of available large packets
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int availableLargePackets()
|
||||
{
|
||||
return m_largePackets.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the large packet size
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getLargePacketSize()
|
||||
{
|
||||
return m_largePktSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the count of allocated large packets
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getLargePacketCount()
|
||||
{
|
||||
return m_largePktCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the large packet allocation limit
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getLargePacketAllocationLimit()
|
||||
{
|
||||
return m_largePktLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the count of available small packets
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int availableSmallPackets()
|
||||
{
|
||||
return m_smallPackets.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a packet from the packet pool
|
||||
*
|
||||
* @param reqSize int
|
||||
* @return RpcPacket
|
||||
*/
|
||||
public final RpcPacket allocatePacket(int reqSize)
|
||||
{
|
||||
|
||||
// Check if the packet should come from the small or large packet list
|
||||
|
||||
RpcPacket pkt = null;
|
||||
|
||||
if (reqSize <= m_smallPktSize)
|
||||
{
|
||||
|
||||
// Allocate a packet from the small packet list
|
||||
|
||||
pkt = allocateSmallPacket();
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("RpcPacketPool Allocated (small) " + pkt.getBuffer() + ", len=" + pkt.getBuffer().length
|
||||
+ ", list=" + m_smallPackets.size() + "/" + m_smallPktLimit);
|
||||
} else
|
||||
{
|
||||
|
||||
// Allocate a packet from the large packet list
|
||||
|
||||
pkt = allocateLargePacket();
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("RpcPacketPool Allocated (large) " + pkt.getBuffer() + ", len=" + pkt.getBuffer().length
|
||||
+ ", list=" + m_largePackets.size() + "/" + m_largePktLimit);
|
||||
}
|
||||
|
||||
// Return the allocated packet
|
||||
|
||||
return pkt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release an RPC packet back to the pool
|
||||
*
|
||||
* @param pkt RpcPacket
|
||||
*/
|
||||
public final void releasePacket(RpcPacket pkt)
|
||||
{
|
||||
|
||||
// Check if the packet should be released to the small or large list
|
||||
|
||||
if (pkt.getBuffer().length >= m_largePktSize)
|
||||
{
|
||||
|
||||
// Release the packet to the large packet list
|
||||
|
||||
synchronized (m_largePackets)
|
||||
{
|
||||
|
||||
// Add the packet back to the free list
|
||||
|
||||
m_largePackets.addElement(pkt);
|
||||
|
||||
// Signal any waiting threads that there are packets available
|
||||
|
||||
m_largePackets.notify();
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("RpcPacketPool Released (large) " + pkt.getBuffer() + ", len="
|
||||
+ pkt.getBuffer().length + ", list=" + m_largePackets.size());
|
||||
}
|
||||
} else
|
||||
{
|
||||
|
||||
// Release the packet to the small packet list
|
||||
|
||||
synchronized (m_smallPackets)
|
||||
{
|
||||
|
||||
// Add the packet back to the free list
|
||||
|
||||
m_smallPackets.addElement(pkt);
|
||||
|
||||
// Signal any waiting threads that there are packets available
|
||||
|
||||
m_smallPackets.notify();
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("RpcPacketPool Released (small) " + pkt.getBuffer() + ", len="
|
||||
+ pkt.getBuffer().length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate, or create, a small RPC packet
|
||||
*
|
||||
* @return RpcPacket
|
||||
*/
|
||||
private final RpcPacket allocateSmallPacket()
|
||||
{
|
||||
|
||||
RpcPacket pkt = null;
|
||||
|
||||
synchronized (m_smallPackets)
|
||||
{
|
||||
|
||||
// Check if there is a packet available from the small packet list
|
||||
|
||||
if (m_smallPackets.size() > 0)
|
||||
{
|
||||
|
||||
// Remove a packet from the head of the free list
|
||||
|
||||
pkt = (RpcPacket) m_smallPackets.elementAt(0);
|
||||
m_smallPackets.removeElementAt(0);
|
||||
} else if (m_smallPktLimit == -1 || m_smallPktCount < m_smallPktLimit)
|
||||
{
|
||||
|
||||
// Allocate a new packet
|
||||
|
||||
pkt = new RpcPacket(m_smallPktSize, this);
|
||||
m_smallPktCount++;
|
||||
} else
|
||||
{
|
||||
|
||||
// Wait for a packet to be released to the small packet list
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
// Wait for a packet
|
||||
|
||||
m_smallPackets.wait();
|
||||
|
||||
// Try to get the packet from the small packet list again
|
||||
|
||||
if (m_smallPackets.size() > 0)
|
||||
{
|
||||
|
||||
// Remove a packet from the head of the free list
|
||||
|
||||
pkt = (RpcPacket) m_smallPackets.elementAt(0);
|
||||
m_smallPackets.removeElementAt(0);
|
||||
}
|
||||
} catch (InterruptedException ex)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the allocated packet
|
||||
|
||||
return pkt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate, or create, a large RPC packet
|
||||
*
|
||||
* @return RpcPacket
|
||||
*/
|
||||
private final RpcPacket allocateLargePacket()
|
||||
{
|
||||
|
||||
RpcPacket pkt = null;
|
||||
|
||||
synchronized (m_largePackets)
|
||||
{
|
||||
|
||||
// Check if there is a packet available from the large packet list
|
||||
|
||||
if (m_largePackets.size() > 0)
|
||||
{
|
||||
|
||||
// Remove a packet from the head of the free list
|
||||
|
||||
pkt = (RpcPacket) m_largePackets.elementAt(0);
|
||||
m_largePackets.removeElementAt(0);
|
||||
} else if (m_largePktLimit == -1 || m_largePktCount < m_largePktLimit)
|
||||
{
|
||||
|
||||
// Allocate a new packet
|
||||
|
||||
pkt = new RpcPacket(m_largePktSize, this);
|
||||
m_largePktCount++;
|
||||
} else
|
||||
{
|
||||
|
||||
// Wait for a packet to be released to the large packet list
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
// Wait for a packet
|
||||
|
||||
m_largePackets.wait();
|
||||
|
||||
// Try to get the packet from the large packet list again
|
||||
|
||||
if (m_largePackets.size() > 0)
|
||||
{
|
||||
|
||||
// Remove a packet from the head of the free list
|
||||
|
||||
pkt = (RpcPacket) m_largePackets.elementAt(0);
|
||||
m_largePackets.removeElementAt(0);
|
||||
}
|
||||
} catch (InterruptedException ex)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the allocated packet
|
||||
|
||||
return pkt;
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* RPC Processor Interface
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public interface RpcProcessor {
|
||||
|
||||
/**
|
||||
* Process an RPC request
|
||||
*
|
||||
* @param rpc RpcPacket
|
||||
* @return RpcPacket
|
||||
* @throws IOException
|
||||
*/
|
||||
public RpcPacket processRpc(RpcPacket rpc)
|
||||
throws IOException;
|
||||
}
|
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* RPC Request Queue Class
|
||||
*
|
||||
* <p>Provides a request queue for a thread pool of worker threads.
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class RpcRequestQueue {
|
||||
|
||||
// List of RPC requests
|
||||
|
||||
private LinkedList m_queue;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public RpcRequestQueue()
|
||||
{
|
||||
m_queue = new LinkedList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of requests in the queue
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final synchronized int numberOfRequests()
|
||||
{
|
||||
return m_queue.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a request to the queue
|
||||
*
|
||||
* @param req RpcPacket
|
||||
*/
|
||||
public final synchronized void addRequest(RpcPacket req)
|
||||
{
|
||||
|
||||
// Add the request to the queue
|
||||
|
||||
m_queue.add(req);
|
||||
|
||||
// Notify workers that there is a request to process
|
||||
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a request from the head of the queue
|
||||
*
|
||||
* @return RpcPacket
|
||||
* @exception InterruptedException
|
||||
*/
|
||||
public final synchronized RpcPacket removeRequest()
|
||||
throws InterruptedException
|
||||
{
|
||||
|
||||
// Wait until there is a request
|
||||
|
||||
waitWhileEmpty();
|
||||
|
||||
// Get the request from the head of the queue
|
||||
|
||||
return (RpcPacket) m_queue.removeFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a request to be added to the queue
|
||||
*
|
||||
* @exception InterruptedException
|
||||
*/
|
||||
public final synchronized void waitWhileEmpty()
|
||||
throws InterruptedException
|
||||
{
|
||||
|
||||
// Wait until some work arrives on the queue
|
||||
|
||||
while (m_queue.size() == 0)
|
||||
wait();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the request queue to be emptied
|
||||
*
|
||||
* @exception InterruptedException
|
||||
*/
|
||||
public final synchronized void waitUntilEmpty()
|
||||
throws InterruptedException
|
||||
{
|
||||
|
||||
// Wait until the request queue is empty
|
||||
|
||||
while (m_queue.size() != 0)
|
||||
wait();
|
||||
}
|
||||
}
|
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* ONC/RPC Request Thread Pool Class
|
||||
*
|
||||
* <p>Processes RPC requests using a pool of worker threads.
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class RpcRequestThreadPool {
|
||||
|
||||
// Debug logging
|
||||
|
||||
private static final Log logger = LogFactory.getLog(RpcRequestThreadPool.class);
|
||||
|
||||
// Default/minimum/maximum number of worker threads to use
|
||||
|
||||
public static final int DefaultWorkerThreads = 8;
|
||||
public static final int MinimumWorkerThreads = 4;
|
||||
public static final int MaximumWorkerThreads = 50;
|
||||
|
||||
// Queue of RPC requests
|
||||
|
||||
private RpcRequestQueue m_queue;
|
||||
|
||||
// Worker threads
|
||||
|
||||
private ThreadWorker[] m_workers;
|
||||
|
||||
// RPC dispatcher
|
||||
|
||||
private RpcProcessor m_rpcProcessor;
|
||||
|
||||
/**
|
||||
* Thread Worker Inner Class
|
||||
*/
|
||||
protected class ThreadWorker implements Runnable
|
||||
{
|
||||
|
||||
// Worker thread
|
||||
|
||||
private Thread mi_thread;
|
||||
|
||||
// Worker unique id
|
||||
|
||||
private int mi_id;
|
||||
|
||||
// Shutdown flag
|
||||
|
||||
private boolean mi_shutdown = false;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param name String
|
||||
* @param id int
|
||||
*/
|
||||
public ThreadWorker(String name, int id)
|
||||
{
|
||||
|
||||
// Save the thread id
|
||||
|
||||
mi_id = id;
|
||||
|
||||
// Create the worker thread
|
||||
|
||||
mi_thread = new Thread(this);
|
||||
mi_thread.setName(name);
|
||||
mi_thread.setDaemon(true);
|
||||
mi_thread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Request the worker thread to shutdown
|
||||
*/
|
||||
public final void shutdownRequest()
|
||||
{
|
||||
mi_shutdown = true;
|
||||
try
|
||||
{
|
||||
mi_thread.interrupt();
|
||||
} catch (Exception ex)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the thread
|
||||
*/
|
||||
public void run()
|
||||
{
|
||||
|
||||
// Loop until shutdown
|
||||
|
||||
RpcPacket rpc = null;
|
||||
RpcPacket response = null;
|
||||
|
||||
while (mi_shutdown == false)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
// Wait for an RPC request to be queued
|
||||
|
||||
rpc = m_queue.removeRequest();
|
||||
} catch (InterruptedException ex)
|
||||
{
|
||||
|
||||
// Check for shutdown
|
||||
|
||||
if (mi_shutdown == true)
|
||||
break;
|
||||
}
|
||||
|
||||
// If the request is valid process it
|
||||
|
||||
if (rpc != null)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
// Process the request
|
||||
|
||||
response = m_rpcProcessor.processRpc(rpc);
|
||||
if (response != null)
|
||||
response.getPacketHandler().sendRpcResponse(response);
|
||||
} catch (Throwable ex)
|
||||
{
|
||||
|
||||
// Do not display errors if shutting down
|
||||
|
||||
if (mi_shutdown == false)
|
||||
{
|
||||
if ( logger.isDebugEnabled()) {
|
||||
logger.debug("Worker " + Thread.currentThread().getName() + ":");
|
||||
logger.debug(ex);
|
||||
}
|
||||
}
|
||||
} finally
|
||||
{
|
||||
|
||||
// Release the RPC packet(s) back to the packet pool
|
||||
|
||||
if (rpc.getClientProtocol() == Rpc.TCP && rpc.isAllocatedFromPool())
|
||||
rpc.getOwnerPacketPool().releasePacket(rpc);
|
||||
|
||||
if (response != null && response.getClientProtocol() == Rpc.TCP
|
||||
&& response.getBuffer() != rpc.getBuffer() && response.isAllocatedFromPool())
|
||||
response.getOwnerPacketPool().releasePacket(response);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param threadName String
|
||||
* @param rpcServer RpcProcessor
|
||||
* @param pktHandler PacketHandlerInterface
|
||||
*/
|
||||
public RpcRequestThreadPool(String threadName, RpcProcessor rpcServer)
|
||||
{
|
||||
this(threadName, DefaultWorkerThreads, rpcServer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param threadName String
|
||||
* @param poolSize int
|
||||
* @param rpcServer RpcProcessor
|
||||
*/
|
||||
public RpcRequestThreadPool(String threadName, int poolSize, RpcProcessor rpcServer)
|
||||
{
|
||||
|
||||
// Save the RPC handler
|
||||
|
||||
m_rpcProcessor = rpcServer;
|
||||
|
||||
// Create the request queue
|
||||
|
||||
m_queue = new RpcRequestQueue();
|
||||
|
||||
// Check that we have at least minimum worker threads
|
||||
|
||||
if (poolSize < MinimumWorkerThreads)
|
||||
poolSize = MinimumWorkerThreads;
|
||||
|
||||
// Create the worker threads
|
||||
|
||||
m_workers = new ThreadWorker[poolSize];
|
||||
|
||||
for (int i = 0; i < m_workers.length; i++)
|
||||
m_workers[i] = new ThreadWorker(threadName + (i + 1), i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of requests in the queue
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getNumberOfRequests()
|
||||
{
|
||||
return m_queue.numberOfRequests();
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue an RPC request to the thread pool for processing
|
||||
*
|
||||
* @param rpc RpcPacket
|
||||
*/
|
||||
public final void queueRpcRequest(RpcPacket pkt)
|
||||
{
|
||||
m_queue.addRequest(pkt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown the thread pool and release all resources
|
||||
*/
|
||||
public void shutdownThreadPool()
|
||||
{
|
||||
|
||||
// Shutdown the worker threads
|
||||
|
||||
if (m_workers != null)
|
||||
{
|
||||
for (int i = 0; i < m_workers.length; i++)
|
||||
m_workers[i].shutdownRequest();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
/**
|
||||
* TCP RPC Client Connection Class
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class TcpRpcClient extends RpcClient {
|
||||
|
||||
// TCP RPC client connection
|
||||
|
||||
private TcpRpcPacketHandler m_client;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param addr InetAddress
|
||||
* @param port int
|
||||
* @param maxRpcSize int
|
||||
* @throws IOException
|
||||
*/
|
||||
public TcpRpcClient(InetAddress addr, int port, int maxRpcSize) throws IOException
|
||||
{
|
||||
super(addr, port, Rpc.TCP, maxRpcSize);
|
||||
|
||||
// Connect a socket to the remote server
|
||||
|
||||
Socket sock = new Socket(getServerAddress(), getServerPort());
|
||||
|
||||
// Create the TCP RPC packet handler for the client connection
|
||||
|
||||
m_client = new TcpRpcPacketHandler(sock, maxRpcSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an RPC request using the socket connection, and receive a response
|
||||
*
|
||||
* @param rpc RpcPacket
|
||||
* @param rxRpc RpcPacket
|
||||
* @return RpcPacket
|
||||
* @throws IOException
|
||||
*/
|
||||
public RpcPacket sendRPC(RpcPacket rpc, RpcPacket rxRpc)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
// Use the TCP packet handler to send the RPC
|
||||
|
||||
m_client.sendRpc(rpc);
|
||||
|
||||
// Receive a response RPC
|
||||
|
||||
RpcPacket rxPkt = rxRpc;
|
||||
if (rxPkt == null)
|
||||
rxPkt = new RpcPacket(getMaximumRpcSize());
|
||||
|
||||
m_client.receiveRpc(rxPkt);
|
||||
|
||||
// Return the RPC response
|
||||
|
||||
return rxPkt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the connection to the remote RPC server
|
||||
*/
|
||||
public void closeConnection()
|
||||
{
|
||||
|
||||
// Close the packet handler
|
||||
|
||||
if (m_client != null)
|
||||
{
|
||||
m_client.closePacketHandler();
|
||||
m_client = null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
import org.alfresco.filesys.server.SocketPacketHandler;
|
||||
import org.alfresco.filesys.util.DataPacker;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* TCP RPC Packet Handler Class
|
||||
*
|
||||
* <p>Processes RPC requests received via TCP session.
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class TcpRpcPacketHandler extends SocketPacketHandler implements Runnable {
|
||||
|
||||
// Debug logging
|
||||
|
||||
private static final Log logger = LogFactory.getLog(TcpRpcPacketHandler.class);
|
||||
|
||||
// Session handler that owns this session
|
||||
|
||||
private TcpRpcSessionHandler m_handler;
|
||||
|
||||
// RPC server implementation used to process the requests
|
||||
|
||||
private RpcProcessor m_rpcProcessor;
|
||||
|
||||
// Session id
|
||||
|
||||
private int m_sessId;
|
||||
|
||||
// RPC processing thread shutdown flag
|
||||
|
||||
private boolean m_shutdown;
|
||||
|
||||
// Maximum RPC size accepted
|
||||
|
||||
private int m_maxRpcSize;
|
||||
|
||||
// Packet buffer for receiving incoming RPC requests
|
||||
|
||||
private RpcPacket m_rxPkt;
|
||||
|
||||
// Fragment header buffer
|
||||
|
||||
private byte[] m_fragBuf;
|
||||
|
||||
/**
|
||||
* Class constructor to create a TCP RPC handler for a server.
|
||||
*
|
||||
* @param handler TcpRpcSessionHandler
|
||||
* @param sessId int
|
||||
* @param server RpcProcessor
|
||||
* @param socket Socket
|
||||
* @param maxRpcSize int
|
||||
* @throws IOException
|
||||
*/
|
||||
public TcpRpcPacketHandler(TcpRpcSessionHandler handler, int sessId, RpcProcessor server, Socket socket,
|
||||
int maxRpcSize) throws IOException
|
||||
{
|
||||
super(socket);
|
||||
|
||||
// Set the session handler that owns this session
|
||||
|
||||
m_handler = handler;
|
||||
|
||||
// set the session id
|
||||
|
||||
m_sessId = sessId;
|
||||
|
||||
// Set the RPC server to be used to process requests
|
||||
|
||||
m_rpcProcessor = server;
|
||||
|
||||
// Set the maximum RPC size accepted
|
||||
|
||||
m_maxRpcSize = maxRpcSize;
|
||||
|
||||
// Allocate the RPC fragment header buffer
|
||||
|
||||
m_fragBuf = new byte[4];
|
||||
|
||||
// Create a thread to run the RPC processing for this session
|
||||
|
||||
Thread th = new Thread(this);
|
||||
th.setName(handler.getProtocolName() + "_" + getSessionId());
|
||||
th.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor to create a TCP RPC handler for a client.
|
||||
*
|
||||
* @param socket Socket
|
||||
* @param maxRpcSize int
|
||||
* @throws IOException
|
||||
*/
|
||||
public TcpRpcPacketHandler(Socket socket, int maxRpcSize) throws IOException
|
||||
{
|
||||
super(socket);
|
||||
|
||||
// Allocate the RPC fragment header buffer
|
||||
|
||||
m_maxRpcSize = maxRpcSize;
|
||||
m_fragBuf = new byte[4];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the protocol name
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String getProtocolName()
|
||||
{
|
||||
return "TCP RPC";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the session id
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getSessionId()
|
||||
{
|
||||
return m_sessId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum RPC size accepted
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getMaximumRpcSize()
|
||||
{
|
||||
return m_maxRpcSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the associated session handler
|
||||
*
|
||||
* @return TcpRpcSessionHandler
|
||||
*/
|
||||
protected final TcpRpcSessionHandler getHandler()
|
||||
{
|
||||
return m_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Thread to read and process the RPC requests for this session
|
||||
*/
|
||||
public void run()
|
||||
{
|
||||
|
||||
// Loop until shutdown
|
||||
|
||||
int rxLen = 0;
|
||||
RpcPacket rpcPkt = null;
|
||||
|
||||
while (m_shutdown == false)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
// allocate an RPC packet to receive an incoming request
|
||||
|
||||
rpcPkt = allocateRpcPacket(getMaximumRpcSize());
|
||||
|
||||
// Read an RPC request
|
||||
|
||||
rxLen = receiveRpc(rpcPkt);
|
||||
|
||||
if (rxLen == -1)
|
||||
{
|
||||
|
||||
// Release the packet
|
||||
|
||||
deallocateRpcPacket(rpcPkt);
|
||||
|
||||
// Receive error, client has closed the socket
|
||||
|
||||
m_handler.closeSession(getSessionId());
|
||||
break;
|
||||
}
|
||||
} catch (SocketException ex)
|
||||
{
|
||||
|
||||
// Release the packet
|
||||
|
||||
if (rpcPkt != null)
|
||||
deallocateRpcPacket(rpcPkt);
|
||||
|
||||
// Socket error, close the session
|
||||
|
||||
m_handler.closeSession(getSessionId());
|
||||
break;
|
||||
} catch (IOException ex)
|
||||
{
|
||||
|
||||
// Only dump errors if not shutting down
|
||||
|
||||
if (m_shutdown == false)
|
||||
logger.debug(ex);
|
||||
}
|
||||
|
||||
// Process the RPC request
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
// Validate the RPC header
|
||||
|
||||
if (rpcPkt.getRpcVersion() != Rpc.RpcVersion)
|
||||
{
|
||||
|
||||
// Build/send an error response
|
||||
|
||||
rpcPkt.buildRpcMismatchResponse();
|
||||
sendRpc(rpcPkt);
|
||||
} else
|
||||
{
|
||||
|
||||
// Process the RPC request
|
||||
|
||||
processRpc(rpcPkt);
|
||||
}
|
||||
} catch (IOException ex)
|
||||
{
|
||||
|
||||
// Only dump errors if not shutting down
|
||||
|
||||
if (m_shutdown == false)
|
||||
logger.debug(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the session
|
||||
*/
|
||||
public void closePacketHandler()
|
||||
{
|
||||
|
||||
// Request the RPC processing thread to shutdown
|
||||
|
||||
m_shutdown = true;
|
||||
|
||||
// Close the input/output streams and socket
|
||||
|
||||
super.closePacketHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an RPC request/response packet
|
||||
*
|
||||
* @param rpc RpcPacket
|
||||
* @exception IOException
|
||||
*/
|
||||
protected final void sendRpc(RpcPacket rpc)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
// Write the RPC response, this includes the fragment header
|
||||
//
|
||||
// If the fragment header is written seperately to the main RPC response packet trace tools
|
||||
// such as Ethereal will not display the details properly.
|
||||
|
||||
writePacket(rpc.getBuffer(), 0, rpc.getTxLength());
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an RPC request/response
|
||||
*
|
||||
* @param rpc RpcPacket
|
||||
* @return int
|
||||
* @throws IOException
|
||||
*/
|
||||
protected final int receiveRpc(RpcPacket rpc)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
// Use the main receive method
|
||||
|
||||
int rxLen = receiveRpc(rpc.getBuffer(), RpcPacket.FragHeaderLen, rpc.getBuffer().length
|
||||
- RpcPacket.FragHeaderLen);
|
||||
if (rxLen > 0)
|
||||
{
|
||||
|
||||
// Set the received length
|
||||
|
||||
rpc.setBuffer(RpcPacket.FragHeaderLen, rxLen + RpcPacket.FragHeaderLen);
|
||||
|
||||
// Set the client details
|
||||
|
||||
rpc.setClientDetails(getSocket().getInetAddress(), getSocket().getPort(), Rpc.TCP);
|
||||
}
|
||||
|
||||
// Return the received data length
|
||||
|
||||
return rxLen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an RPC request/response
|
||||
*
|
||||
* @param buffer byte[]
|
||||
* @param offset int
|
||||
* @param maxLen int
|
||||
* @return int
|
||||
* @throws IOException
|
||||
*/
|
||||
protected final int receiveRpc(byte[] buffer, int offset, int maxLen)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
// Fill the buffer until the last fragment is received
|
||||
|
||||
int rxLen = 0;
|
||||
int totLen = 0;
|
||||
int rxOffset = offset;
|
||||
int fragLen = 0;
|
||||
boolean lastFrag = false;
|
||||
|
||||
while (lastFrag == false)
|
||||
{
|
||||
|
||||
// Read in a header to get the fragment length
|
||||
|
||||
rxLen = readPacket(m_fragBuf, 0, 4);
|
||||
if (rxLen == -1)
|
||||
return rxLen;
|
||||
|
||||
// Check if we received the last fragment
|
||||
|
||||
fragLen = DataPacker.getInt(m_fragBuf, 0);
|
||||
|
||||
if ((fragLen & Rpc.LastFragment) != 0)
|
||||
{
|
||||
lastFrag = true;
|
||||
fragLen = fragLen & Rpc.LengthMask;
|
||||
}
|
||||
|
||||
// Check if the buffer is large enough to receive the request
|
||||
|
||||
if (fragLen > (buffer.length - rxOffset))
|
||||
throw new IOException("Receive RPC buffer overflow, fragment len = " + fragLen);
|
||||
|
||||
// Read the data part of the packet into the users buffer, this may take
|
||||
// several reads
|
||||
|
||||
while (fragLen > 0)
|
||||
{
|
||||
|
||||
// Read the data
|
||||
|
||||
rxLen = readPacket(buffer, offset, fragLen);
|
||||
|
||||
// Check if the connection has been closed
|
||||
|
||||
if (rxLen == -1)
|
||||
return -1;
|
||||
|
||||
// Update the received length and remaining data length
|
||||
|
||||
totLen += rxLen;
|
||||
fragLen -= rxLen;
|
||||
|
||||
// Update the user buffer offset as more reads will be required
|
||||
// to complete the data read
|
||||
|
||||
offset += rxLen;
|
||||
|
||||
} // end while reading data
|
||||
|
||||
} // end while fragments
|
||||
|
||||
// Return the total length read
|
||||
|
||||
return totLen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate an RPC packet for receiving an incoming request. This method must be overridden for
|
||||
* multi-threaded implementations.
|
||||
*
|
||||
* @param maxSize int
|
||||
* @return RpcPacket
|
||||
*/
|
||||
protected RpcPacket allocateRpcPacket(int maxSize)
|
||||
{
|
||||
|
||||
// Check if the receive packet has been allocated
|
||||
|
||||
if (m_rxPkt == null)
|
||||
m_rxPkt = new RpcPacket(maxSize);
|
||||
|
||||
// Return the RPC receive packet
|
||||
|
||||
return m_rxPkt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deallocate an RPC packet, default method does nothing but a pooled implementation may
|
||||
* return the packet to the pool.
|
||||
*
|
||||
* @param pkt RpcPacket
|
||||
*/
|
||||
protected void deallocateRpcPacket(RpcPacket pkt)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an RPC request. This method must be overridden for multi-threaded implementations.
|
||||
*
|
||||
* @param rpc RpcPacket
|
||||
* @exception IOException
|
||||
*/
|
||||
protected void processRpc(RpcPacket rpc)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
// Process the RPC request in the current thread
|
||||
|
||||
RpcPacket response = m_rpcProcessor.processRpc(rpc);
|
||||
|
||||
// Send the RPC response
|
||||
|
||||
if (response != null)
|
||||
sendRpc(response);
|
||||
}
|
||||
}
|
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
|
||||
import org.alfresco.filesys.server.NetworkServer;
|
||||
import org.alfresco.filesys.server.PacketHandlerInterface;
|
||||
import org.alfresco.filesys.server.SocketSessionHandler;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* TCP RPC Session Handler Class
|
||||
*
|
||||
* <p>Receives session requests via a TCP socketRPC requests via a datagram and passes the request to the registered RPC server.
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class TcpRpcSessionHandler extends SocketSessionHandler {
|
||||
|
||||
// Debug logging
|
||||
|
||||
private static final Log logger = LogFactory.getLog(TcpRpcSessionHandler.class);
|
||||
|
||||
// RPC server implementation that handles the RPC processing
|
||||
|
||||
private RpcProcessor m_rpcProcessor;
|
||||
|
||||
// Maximum request size allowed
|
||||
|
||||
private int m_maxRpcSize;
|
||||
|
||||
// List of active sessions
|
||||
|
||||
private Hashtable m_sessions;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param name String
|
||||
* @param protocol String
|
||||
* @param rpcServer RpcProcessor
|
||||
* @param server NetworkServer
|
||||
* @param addr InetAddress
|
||||
* @param port int
|
||||
* @param maxSize int
|
||||
*/
|
||||
public TcpRpcSessionHandler(String name, String protocol, RpcProcessor rpcServer, NetworkServer server,
|
||||
InetAddress addr, int port, int maxSize)
|
||||
{
|
||||
super(name, protocol, server, addr, port);
|
||||
|
||||
// Set the RPC server implementation that will handle the actual requests
|
||||
|
||||
m_rpcProcessor = rpcServer;
|
||||
|
||||
// Set the maximum RPC request size allowed
|
||||
|
||||
m_maxRpcSize = maxSize;
|
||||
|
||||
// Create the active session list
|
||||
|
||||
m_sessions = new Hashtable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum RPC size allowed
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected int getMaximumRpcSize()
|
||||
{
|
||||
return m_maxRpcSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the RPC server used to process the requests
|
||||
*
|
||||
* @return RpcProcessor
|
||||
*/
|
||||
protected final RpcProcessor getRpcProcessor()
|
||||
{
|
||||
return m_rpcProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept an incoming session request
|
||||
*
|
||||
* @param sock Socket
|
||||
*/
|
||||
protected void acceptConnection(Socket sock)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
// Set the socket for no delay
|
||||
|
||||
sock.setTcpNoDelay(true);
|
||||
|
||||
// Create a packet handler for the new session and add to the active session list
|
||||
|
||||
int sessId = getNextSessionId();
|
||||
TcpRpcPacketHandler pktHandler = createPacketHandler(sessId, sock);
|
||||
|
||||
// Add the packet handler to the active session table
|
||||
|
||||
m_sessions.put(new Integer(sessId), pktHandler);
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("[" + getProtocolName() + "] Created new session id = " + sessId + ", from = "
|
||||
+ sock.getInetAddress().getHostAddress() + ":" + sock.getPort());
|
||||
} catch (IOException ex)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a session from the active session list
|
||||
*
|
||||
* @param sessId int
|
||||
*/
|
||||
protected final void closeSession(int sessId)
|
||||
{
|
||||
|
||||
// Remove the specified session from the active session table
|
||||
|
||||
PacketHandlerInterface pktHandler = (PacketHandlerInterface) m_sessions.remove(new Integer(sessId));
|
||||
if (pktHandler != null)
|
||||
{
|
||||
|
||||
// Close the session
|
||||
|
||||
pktHandler.closePacketHandler();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the session handler, close all active sessions.
|
||||
*
|
||||
* @param server NetworkServer
|
||||
*/
|
||||
public void closeSessionHandler(NetworkServer server)
|
||||
{
|
||||
super.closeSessionHandler(server);
|
||||
|
||||
// Close all active sessions
|
||||
|
||||
if (m_sessions.size() > 0)
|
||||
{
|
||||
|
||||
// Enumerate the sessions
|
||||
|
||||
Enumeration enm = m_sessions.elements();
|
||||
|
||||
while (enm.hasMoreElements())
|
||||
{
|
||||
|
||||
// Get the current packet handler
|
||||
|
||||
PacketHandlerInterface handler = (PacketHandlerInterface) enm.nextElement();
|
||||
handler.closePacketHandler();
|
||||
}
|
||||
|
||||
// Clear the session list
|
||||
|
||||
m_sessions.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a packet handler for a new session
|
||||
*
|
||||
* @param sessId int
|
||||
* @param sock Socket
|
||||
* @return TcpRpcPacketHandler
|
||||
* @exception IOException
|
||||
*/
|
||||
protected TcpRpcPacketHandler createPacketHandler(int sessId, Socket sock)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
// Create a single threaded TCP RPC packet handler
|
||||
|
||||
return new TcpRpcPacketHandler(this, sessId, m_rpcProcessor, sock, getMaximumRpcSize());
|
||||
}
|
||||
}
|
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
import org.alfresco.filesys.server.DatagramSessionHandler;
|
||||
import org.alfresco.filesys.server.NetworkServer;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* UDP RPC Datagram Handler Class
|
||||
*
|
||||
* <p>Receives RPC requests via a datagram and passes the request to the registered RPC server.
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class UdpRpcDatagramHandler extends DatagramSessionHandler
|
||||
{
|
||||
// Debug logging
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(UdpRpcDatagramHandler.class);
|
||||
|
||||
// RPC server implementation that handles the RPC processing
|
||||
|
||||
private RpcProcessor m_rpcProcessor;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param name String
|
||||
* @param protocol String
|
||||
* @param rpcServer RpcProcessor
|
||||
* @param server NetworkServer
|
||||
* @param addr InetAddress
|
||||
* @param port int
|
||||
* @param maxSize int
|
||||
*/
|
||||
public UdpRpcDatagramHandler(String name, String protocol, RpcProcessor rpcServer, NetworkServer server,
|
||||
InetAddress addr, int port, int maxSize)
|
||||
{
|
||||
super(name, protocol, server, addr, port);
|
||||
|
||||
// Set the RPC server implementation that will handle the actual requests
|
||||
|
||||
m_rpcProcessor = rpcServer;
|
||||
|
||||
// Set the maximum RPC request size allowed
|
||||
|
||||
setMaximumDatagramSize(maxSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the RPC server used to process the requests
|
||||
*
|
||||
* @return RpcProcessor
|
||||
*/
|
||||
protected final RpcProcessor getRpcProcessor()
|
||||
{
|
||||
return m_rpcProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the RPC datagram
|
||||
*
|
||||
* @param pkt DatagramPacket
|
||||
* @return boolean
|
||||
* @throws IOException
|
||||
*/
|
||||
protected boolean processDatagram(DatagramPacket pkt)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
// The default implementation processes the RPC immediately then returns to the main datagram handler
|
||||
// to wait for the next datagram to be received. In this case the datagram packet can be re-used as
|
||||
// processing is done sequentially.
|
||||
|
||||
// Wrap the datagram data up as an RPC request
|
||||
|
||||
RpcPacket rpcPkt = new RpcPacket(pkt.getData(), 0, pkt.getLength());
|
||||
|
||||
// Set the client details
|
||||
|
||||
rpcPkt.setClientDetails(pkt.getAddress(), pkt.getPort(), Rpc.UDP);
|
||||
|
||||
// Validate the RPC header
|
||||
|
||||
if (rpcPkt.getRpcVersion() != Rpc.RpcVersion)
|
||||
{
|
||||
|
||||
// Build/send an error response
|
||||
|
||||
rpcPkt.buildRpcMismatchResponse();
|
||||
pkt.setData(rpcPkt.getBuffer(), rpcPkt.getOffset(), RpcPacket.ResponseMismatchLen);
|
||||
|
||||
sendDatagram(pkt);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// Pass the request to the registered RPC server to process
|
||||
|
||||
RpcPacket response = m_rpcProcessor.processRpc(rpcPkt);
|
||||
|
||||
// Send the RPC response
|
||||
|
||||
if (response != null)
|
||||
{
|
||||
pkt.setData(response.getBuffer(), response.getOffset(), response.getLength());
|
||||
sendDatagram(pkt);
|
||||
}
|
||||
}
|
||||
|
||||
// Indicate that the existing datagram packet can be re-used for the next request
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc.mount;
|
||||
|
||||
/**
|
||||
* Mount Server Constants Class
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public final class Mount {
|
||||
|
||||
// Program and version id
|
||||
|
||||
public static final int ProgramId = 100005;
|
||||
public static final int VersionId1 = 1;
|
||||
public static final int VersionId3 = 3;
|
||||
|
||||
// RPC procedure ids (version 1)
|
||||
|
||||
public static final int ProcNull1 = 0;
|
||||
public static final int ProcMnt1 = 1;
|
||||
public static final int ProcDump1 = 2;
|
||||
public static final int ProcUMnt1 = 3;
|
||||
public static final int ProcUMntAll1 = 4;
|
||||
public static final int ProcExport1 = 5;
|
||||
public static final int ProcExportAll1 = 6;
|
||||
public static final int ProcMax1 = 6;
|
||||
|
||||
// RPC procedure ids (version 3)
|
||||
|
||||
public static final int ProcNull3 = 0;
|
||||
public static final int ProcMnt3 = 1;
|
||||
public static final int ProcDump3 = 2;
|
||||
public static final int ProcUMnt3 = 3;
|
||||
public static final int ProcUMntAll3 = 4;
|
||||
public static final int ProcExport3 = 5;
|
||||
public static final int ProcMax3 = 5;
|
||||
|
||||
// Mount server status codes
|
||||
|
||||
public static final int StsSuccess = 0;
|
||||
public static final int StsPerm = 1;
|
||||
public static final int StsNoEnt = 2;
|
||||
public static final int StsIO = 5;
|
||||
public static final int StsAccess = 13;
|
||||
public static final int StsNotDir = 20;
|
||||
public static final int StsInval = 22;
|
||||
public static final int StsNameTooLong = 63;
|
||||
public static final int StsNotSupp = 10004;
|
||||
public static final int StsServerFault = 10006;
|
||||
|
||||
// Data structure limits
|
||||
|
||||
public static final int FileHandleSize1 = 32;
|
||||
public static final int FileHandleSize3 = 32; // can be 64 for v3
|
||||
|
||||
// RPC procedure names
|
||||
|
||||
private static final String[] _procNames = { "Null", "Mount", "Dump",
|
||||
"UnMount", "UnMountAll", "Export", "ExportAll" };
|
||||
|
||||
/**
|
||||
* Return a procedure id as a name
|
||||
*
|
||||
* @param id
|
||||
* int
|
||||
* @return String
|
||||
*/
|
||||
public final static String getProcedureName(int id) {
|
||||
if (id < 0 || id > ProcMax1)
|
||||
return null;
|
||||
return _procNames[id];
|
||||
}
|
||||
}
|
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc.mount;
|
||||
|
||||
/**
|
||||
* Mount Entry Class
|
||||
*
|
||||
* <p>Contains the details of an active NFS mount.
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class MountEntry {
|
||||
|
||||
// Remote host name/address
|
||||
|
||||
private String m_host;
|
||||
|
||||
// Mount path
|
||||
|
||||
private String m_path;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param host String
|
||||
* @param path String
|
||||
*/
|
||||
public MountEntry(String host, String path) {
|
||||
m_host = host;
|
||||
m_path = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the host name/address
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public final String getHost() {
|
||||
return m_host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mount path
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public final String getPath() {
|
||||
return m_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mount entry as a string
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String toString() {
|
||||
StringBuffer str = new StringBuffer();
|
||||
|
||||
str.append("[");
|
||||
str.append(getHost());
|
||||
str.append(":");
|
||||
str.append(getPath());
|
||||
str.append("]");
|
||||
|
||||
return str.toString();
|
||||
}
|
||||
}
|
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc.mount;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Mount Entry List Class
|
||||
*
|
||||
* <p>Contains a list of active mount entries.
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class MountEntryList {
|
||||
|
||||
// Mount entry list
|
||||
|
||||
private Vector<MountEntry> m_mounts;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public MountEntryList() {
|
||||
m_mounts = new Vector<MountEntry>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ad an entry to the list
|
||||
*
|
||||
* @param entry MountEntry
|
||||
*/
|
||||
public synchronized final void addEntry(MountEntry entry) {
|
||||
m_mounts.addElement(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of entries in the list
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public synchronized final int numberOfEntries() {
|
||||
return m_mounts.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the specified entry
|
||||
*
|
||||
* @param idx
|
||||
* @return MountEntry
|
||||
*/
|
||||
public synchronized final MountEntry getEntryAt(int idx) {
|
||||
if ( idx < 0 || idx >= m_mounts.size())
|
||||
return null;
|
||||
return (MountEntry) m_mounts.elementAt(idx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an entry in the list
|
||||
*
|
||||
* @param path String
|
||||
* @param host String
|
||||
* @return MountEntry
|
||||
*/
|
||||
public synchronized final MountEntry findEntry(String path, String host) {
|
||||
for ( int i = 0; i < m_mounts.size(); i++) {
|
||||
MountEntry entry = (MountEntry) m_mounts.elementAt(i);
|
||||
|
||||
if ( host.compareTo(entry.getHost()) == 0 && path.compareTo(entry.getPath()) == 0)
|
||||
return entry;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an entry from the list
|
||||
*
|
||||
* @param path String
|
||||
* @param host String
|
||||
* @return MountEntry
|
||||
*/
|
||||
public synchronized final MountEntry removeEntry(String path, String host) {
|
||||
for ( int i = 0; i < m_mounts.size(); i++) {
|
||||
MountEntry entry = (MountEntry) m_mounts.elementAt(i);
|
||||
|
||||
if ( host.compareTo(entry.getHost()) == 0 && path.compareTo(entry.getPath()) == 0) {
|
||||
m_mounts.removeElementAt(i);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all entries from the list for the specified host
|
||||
*
|
||||
* @param host String
|
||||
*/
|
||||
public synchronized final void removeHostEntries(String host) {
|
||||
for ( int i = 0; i < m_mounts.size(); i++) {
|
||||
MountEntry entry = (MountEntry) m_mounts.elementAt(i);
|
||||
|
||||
if ( host.compareTo(entry.getHost()) == 0)
|
||||
m_mounts.removeElementAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all items for the specified host and return as a new list
|
||||
*
|
||||
* @param host String
|
||||
* @return MountEntryList
|
||||
*/
|
||||
public synchronized final MountEntryList findSessionEntries(String host) {
|
||||
|
||||
// Allocate the list to hold the matching entries
|
||||
|
||||
MountEntryList list = new MountEntryList();
|
||||
|
||||
// Find the matching entries
|
||||
|
||||
for ( int i = 0; i < m_mounts.size(); i++) {
|
||||
MountEntry entry = (MountEntry) m_mounts.elementAt(i);
|
||||
if ( host.compareTo(entry.getHost()) == 0)
|
||||
list.addEntry(entry);
|
||||
}
|
||||
|
||||
// Check if the list is empty, return the list
|
||||
|
||||
if ( list.numberOfEntries() == 0)
|
||||
list = null;
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remote all entries from the list
|
||||
*/
|
||||
public synchronized final void removeAllItems() {
|
||||
m_mounts.removeAllElements();
|
||||
}
|
||||
}
|
@@ -0,0 +1,885 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc.mount;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import org.alfresco.filesys.server.ServerListener;
|
||||
import org.alfresco.filesys.server.auth.acl.AccessControl;
|
||||
import org.alfresco.filesys.server.auth.acl.AccessControlManager;
|
||||
import org.alfresco.filesys.server.config.ServerConfiguration;
|
||||
import org.alfresco.filesys.server.core.ShareType;
|
||||
import org.alfresco.filesys.server.core.SharedDevice;
|
||||
import org.alfresco.filesys.server.core.SharedDeviceList;
|
||||
import org.alfresco.filesys.server.filesys.DiskInterface;
|
||||
import org.alfresco.filesys.server.filesys.FileInfo;
|
||||
import org.alfresco.filesys.server.filesys.TreeConnection;
|
||||
import org.alfresco.filesys.server.filesys.TreeConnectionHash;
|
||||
import org.alfresco.filesys.server.oncrpc.PortMapping;
|
||||
import org.alfresco.filesys.server.oncrpc.Rpc;
|
||||
import org.alfresco.filesys.server.oncrpc.RpcAuthenticationException;
|
||||
import org.alfresco.filesys.server.oncrpc.RpcAuthenticator;
|
||||
import org.alfresco.filesys.server.oncrpc.RpcNetworkServer;
|
||||
import org.alfresco.filesys.server.oncrpc.RpcPacket;
|
||||
import org.alfresco.filesys.server.oncrpc.RpcProcessor;
|
||||
import org.alfresco.filesys.server.oncrpc.TcpRpcSessionHandler;
|
||||
import org.alfresco.filesys.server.oncrpc.UdpRpcDatagramHandler;
|
||||
import org.alfresco.filesys.server.oncrpc.nfs.NFSHandle;
|
||||
import org.alfresco.filesys.server.oncrpc.nfs.NFSSrvSession;
|
||||
|
||||
/**
|
||||
* Mount Server Class
|
||||
*
|
||||
* <p>Contains the NFS mount server.
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class MountServer extends RpcNetworkServer implements RpcProcessor {
|
||||
|
||||
// Constants
|
||||
//
|
||||
// Maximum request size to accept
|
||||
|
||||
public final static int MaxRequestSize = 8192;
|
||||
|
||||
// Unix path seperator
|
||||
|
||||
public static final String UNIX_SEPERATOR = "/";
|
||||
public static final char UNIX_SEPERATOR_CHAR = '/';
|
||||
public static final String DOS_SEPERATOR = "\\";
|
||||
public static final char DOS_SEPERATOR_CHAR = '\\';
|
||||
|
||||
// Incoming datagram handler for UDP requests
|
||||
|
||||
private UdpRpcDatagramHandler m_udpHandler;
|
||||
|
||||
// Incoming session handler for TCP requests
|
||||
|
||||
private TcpRpcSessionHandler m_tcpHandler;
|
||||
|
||||
// Tree connection hash
|
||||
|
||||
private TreeConnectionHash m_connections;
|
||||
|
||||
// List of active mounts
|
||||
|
||||
private MountEntryList m_mounts;
|
||||
|
||||
// Port number to listen on (UDP and TCP)
|
||||
|
||||
private int m_port;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param config
|
||||
* ServerConfiguration
|
||||
*/
|
||||
public MountServer(ServerConfiguration config) {
|
||||
super("Mount", config);
|
||||
|
||||
// Enable/disable debug output
|
||||
|
||||
setDebug(config.hasMountServerDebug());
|
||||
|
||||
// Set the port to bind the server to
|
||||
|
||||
setPort(config.getMountServerPort());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the port to bind to
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getPort() {
|
||||
return m_port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the port to use
|
||||
*
|
||||
* @param port
|
||||
* int
|
||||
*/
|
||||
public final void setPort(int port) {
|
||||
m_port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the mount server
|
||||
*/
|
||||
public void startServer() {
|
||||
|
||||
try {
|
||||
|
||||
// Create the UDP handler for accepting incoming requests
|
||||
|
||||
m_udpHandler = new UdpRpcDatagramHandler("Mountd", "Mnt", this, this, null, getPort(), MaxRequestSize);
|
||||
m_udpHandler.initializeSessionHandler(this);
|
||||
|
||||
// Start the UDP request listener is a seperate thread
|
||||
|
||||
Thread udpThread = new Thread(m_udpHandler);
|
||||
udpThread.setName("Mountd_UDP");
|
||||
udpThread.start();
|
||||
|
||||
// Create the TCP handler for accepting incoming requests
|
||||
|
||||
m_tcpHandler = new TcpRpcSessionHandler("Mountd", "Mnt", this, this, null, getPort(), MaxRequestSize);
|
||||
m_tcpHandler.initializeSessionHandler(this);
|
||||
|
||||
// Start the UDP request listener is a seperate thread
|
||||
|
||||
Thread tcpThread = new Thread(m_tcpHandler);
|
||||
tcpThread.setName("Mountd_TCP");
|
||||
tcpThread.start();
|
||||
|
||||
// Register the mount server with the portmapper
|
||||
|
||||
PortMapping[] mappings = new PortMapping[4];
|
||||
mappings[0] = new PortMapping(Mount.ProgramId, Mount.VersionId1, Rpc.UDP, m_udpHandler.getPort());
|
||||
mappings[1] = new PortMapping(Mount.ProgramId, Mount.VersionId3, Rpc.UDP, m_udpHandler.getPort());
|
||||
mappings[2] = new PortMapping(Mount.ProgramId, Mount.VersionId1, Rpc.TCP, m_tcpHandler.getPort());
|
||||
mappings[3] = new PortMapping(Mount.ProgramId, Mount.VersionId3, Rpc.TCP, m_tcpHandler.getPort());
|
||||
|
||||
registerRPCServer(mappings);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.error(ex);
|
||||
}
|
||||
|
||||
// Allocate the tree connection hash list and populate with the
|
||||
// available share names
|
||||
|
||||
m_connections = new TreeConnectionHash();
|
||||
|
||||
SharedDeviceList shareList = getConfiguration().getShareMapper()
|
||||
.getShareList(getConfiguration().getServerName(), null, false);
|
||||
Enumeration shares = shareList.enumerateShares();
|
||||
|
||||
while (shares.hasMoreElements()) {
|
||||
|
||||
// Get the shared device
|
||||
|
||||
SharedDevice share = (SharedDevice) shares.nextElement();
|
||||
|
||||
// Check if it is a disk type shared device, if so then add a
|
||||
// connection to the tree connection hash
|
||||
|
||||
if (share != null && share.getType() == ShareType.DISK)
|
||||
m_connections.addConnection(new TreeConnection(share));
|
||||
}
|
||||
|
||||
// Allocate the active mount list
|
||||
|
||||
m_mounts = new MountEntryList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown the mount server
|
||||
*
|
||||
* @param immediate
|
||||
* boolean
|
||||
*/
|
||||
public void shutdownServer(boolean immediate) {
|
||||
|
||||
// Unregister the mount server with the portmapper
|
||||
|
||||
try {
|
||||
PortMapping[] mappings = new PortMapping[4];
|
||||
mappings[0] = new PortMapping(Mount.ProgramId, Mount.VersionId1,
|
||||
Rpc.UDP, m_udpHandler.getPort());
|
||||
mappings[1] = new PortMapping(Mount.ProgramId, Mount.VersionId3,
|
||||
Rpc.UDP, m_udpHandler.getPort());
|
||||
mappings[2] = new PortMapping(Mount.ProgramId, Mount.VersionId1,
|
||||
Rpc.TCP, m_tcpHandler.getPort());
|
||||
mappings[3] = new PortMapping(Mount.ProgramId, Mount.VersionId3,
|
||||
Rpc.TCP, m_tcpHandler.getPort());
|
||||
|
||||
unregisterRPCServer(mappings);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
logger.debug(ex);
|
||||
}
|
||||
|
||||
// Stop the RPC handlers
|
||||
|
||||
if (m_udpHandler != null) {
|
||||
m_udpHandler.closeSessionHandler(this);
|
||||
m_udpHandler = null;
|
||||
}
|
||||
|
||||
if (m_tcpHandler != null) {
|
||||
m_tcpHandler.closeSessionHandler(this);
|
||||
m_tcpHandler = null;
|
||||
}
|
||||
|
||||
// Fire a shutdown notification event
|
||||
|
||||
fireServerEvent(ServerListener.ServerShutdown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an RPC request
|
||||
*
|
||||
* @param rpc
|
||||
* RpcPacket
|
||||
* @return RpcPacket
|
||||
* @throws IOException
|
||||
*/
|
||||
public RpcPacket processRpc(RpcPacket rpc) throws IOException {
|
||||
|
||||
// Validate the request
|
||||
|
||||
int version = rpc.getProgramVersion();
|
||||
|
||||
if (rpc.getProgramId() != Mount.ProgramId) {
|
||||
|
||||
// Request is not for us
|
||||
|
||||
rpc.buildAcceptErrorResponse(Rpc.StsProgUnavail);
|
||||
return rpc;
|
||||
}
|
||||
else if (version != Mount.VersionId1 && version != Mount.VersionId3) {
|
||||
|
||||
// Request is not for this version of mount
|
||||
|
||||
rpc.buildProgramMismatchResponse(Mount.VersionId1, Mount.VersionId3);
|
||||
return rpc;
|
||||
}
|
||||
|
||||
// Authenticate the request
|
||||
|
||||
NFSSrvSession sess = null;
|
||||
|
||||
try {
|
||||
|
||||
// Create a temporary session for the request
|
||||
|
||||
sess = createTemporarySession(rpc);
|
||||
}
|
||||
catch (RpcAuthenticationException ex) {
|
||||
|
||||
// Failed to authenticate the RPC client
|
||||
|
||||
rpc.buildAuthFailResponse(ex.getAuthenticationErrorCode());
|
||||
return rpc;
|
||||
}
|
||||
|
||||
// Position the RPC buffer pointer at the start of the call parameters
|
||||
|
||||
rpc.positionAtParameters();
|
||||
|
||||
// Process the RPC request
|
||||
|
||||
RpcPacket response = null;
|
||||
|
||||
if (version == Mount.VersionId1) {
|
||||
|
||||
// Version 1 requests
|
||||
|
||||
switch (rpc.getProcedureId()) {
|
||||
|
||||
// Null request
|
||||
|
||||
case Mount.ProcNull1:
|
||||
response = procNull(rpc);
|
||||
break;
|
||||
|
||||
// Mount request
|
||||
|
||||
case Mount.ProcMnt1:
|
||||
response = procMount(sess, rpc, version);
|
||||
break;
|
||||
|
||||
// Dump request
|
||||
|
||||
case Mount.ProcDump1:
|
||||
response = procDump(sess, rpc, version);
|
||||
break;
|
||||
|
||||
// Unmount request
|
||||
|
||||
case Mount.ProcUMnt1:
|
||||
response = procUnMount(sess, rpc, version);
|
||||
break;
|
||||
|
||||
// Unmount all request
|
||||
|
||||
case Mount.ProcUMntAll1:
|
||||
response = procUnMountAll(sess, rpc, version);
|
||||
break;
|
||||
|
||||
// Export request
|
||||
|
||||
case Mount.ProcExport1:
|
||||
response = procExport(sess, rpc, version);
|
||||
break;
|
||||
|
||||
// Export all request
|
||||
|
||||
case Mount.ProcExportAll1:
|
||||
response = procExportAll(sess, rpc);
|
||||
break;
|
||||
}
|
||||
} else if (version == Mount.VersionId3) {
|
||||
|
||||
// Version 1 requests
|
||||
|
||||
switch (rpc.getProcedureId()) {
|
||||
|
||||
// Null request
|
||||
|
||||
case Mount.ProcNull3:
|
||||
response = procNull(rpc);
|
||||
break;
|
||||
|
||||
// Mount request
|
||||
|
||||
case Mount.ProcMnt3:
|
||||
response = procMount(sess, rpc, version);
|
||||
break;
|
||||
|
||||
// Dump request
|
||||
|
||||
case Mount.ProcDump3:
|
||||
response = procDump(sess, rpc, version);
|
||||
break;
|
||||
|
||||
// Unmount request
|
||||
|
||||
case Mount.ProcUMnt3:
|
||||
response = procUnMount(sess, rpc, version);
|
||||
break;
|
||||
|
||||
// Unmount all request
|
||||
|
||||
case Mount.ProcUMntAll3:
|
||||
response = procUnMountAll(sess, rpc, version);
|
||||
break;
|
||||
|
||||
// Export request
|
||||
|
||||
case Mount.ProcExport3:
|
||||
response = procExport(sess, rpc, version);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the RPC response
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the null request
|
||||
*
|
||||
* @param rpc
|
||||
* RpcPacket
|
||||
* @return RpcPacket
|
||||
*/
|
||||
private final RpcPacket procNull(RpcPacket rpc) {
|
||||
|
||||
// Build the response
|
||||
|
||||
rpc.buildResponseHeader();
|
||||
return rpc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the mount request
|
||||
*
|
||||
* @param sess
|
||||
* NFSSrvSession
|
||||
* @param rpc
|
||||
* RpcPacket
|
||||
* @param version
|
||||
* int
|
||||
* @return RpcPacket
|
||||
*/
|
||||
private final RpcPacket procMount(NFSSrvSession sess, RpcPacket rpc,
|
||||
int version) {
|
||||
|
||||
// Get the request parameters
|
||||
|
||||
String mountPath = rpc.unpackString();
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("[Mount] Mount request from " + rpc.getClientDetails() + " path=" + mountPath);
|
||||
|
||||
// Allocate the file handle
|
||||
|
||||
byte[] handle = allocateFileHandle(version);
|
||||
|
||||
// Mount the path
|
||||
|
||||
int sts = mountPath(sess, mountPath, handle);
|
||||
|
||||
// Pack mount the response
|
||||
|
||||
rpc.buildResponseHeader();
|
||||
|
||||
// Pack the file handle status structure, version 1 format
|
||||
|
||||
if (version == 1) {
|
||||
|
||||
// Version 1 response format
|
||||
|
||||
rpc.packInt(sts);
|
||||
if (sts == Mount.StsSuccess)
|
||||
rpc.packByteArray(handle);
|
||||
}
|
||||
else if (version == 3) {
|
||||
|
||||
// Version 3 response format
|
||||
|
||||
rpc.packInt(sts);
|
||||
if (sts == Mount.StsSuccess)
|
||||
rpc.packByteArrayWithLength(handle);
|
||||
|
||||
// Create an authentication flavours array
|
||||
|
||||
rpc.packIntArrayWithLength(getConfiguration().getRpcAuthenticator()
|
||||
.getRpcAuthenticationTypes());
|
||||
}
|
||||
|
||||
// Return the mount response
|
||||
|
||||
rpc.setLength();
|
||||
return rpc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the dump request, return the list of active mounts
|
||||
*
|
||||
* @param sess
|
||||
* NFSSrvSession
|
||||
* @param rpc
|
||||
* RpcPacket
|
||||
* @param version
|
||||
* int
|
||||
* @return RpcPacket
|
||||
*/
|
||||
private final RpcPacket procDump(NFSSrvSession sess, RpcPacket rpc,
|
||||
int version) {
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("[Mount] Dump request from " + rpc.getClientDetails());
|
||||
|
||||
// Take a snapshot of the active mount list
|
||||
|
||||
MountEntryList activeList = null;
|
||||
|
||||
synchronized (m_mounts) {
|
||||
|
||||
// Check if there are active mounts, if so then copy the mount list
|
||||
|
||||
if (m_mounts.numberOfEntries() > 0) {
|
||||
activeList = new MountEntryList();
|
||||
for (int i = 0; i < m_mounts.numberOfEntries(); i++)
|
||||
activeList.addEntry(m_mounts.getEntryAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
// Build the response header
|
||||
|
||||
rpc.buildResponseHeader();
|
||||
|
||||
// Pack the mount list structures into the response
|
||||
|
||||
if (activeList != null) {
|
||||
|
||||
// Pack the active mount entry details
|
||||
|
||||
for (int i = 0; i < activeList.numberOfEntries(); i++) {
|
||||
|
||||
// Get the current entry
|
||||
|
||||
MountEntry mntEntry = activeList.getEntryAt(i);
|
||||
|
||||
rpc.packInt(Rpc.True);
|
||||
rpc.packString(mntEntry.getPath());
|
||||
rpc.packString(mntEntry.getHost());
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the end of the mount list and set the response length
|
||||
|
||||
rpc.packInt(Rpc.False);
|
||||
rpc.setLength();
|
||||
|
||||
// Return the RPC response
|
||||
|
||||
return rpc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the unmount request
|
||||
*
|
||||
* @param sess
|
||||
* NFSSrvSession
|
||||
* @param rpc
|
||||
* RpcPacket
|
||||
* @param version
|
||||
* int
|
||||
* @return RpcPacket
|
||||
*/
|
||||
private final RpcPacket procUnMount(NFSSrvSession sess, RpcPacket rpc,
|
||||
int version) {
|
||||
|
||||
// Get the request parameters
|
||||
|
||||
String mountPath = rpc.unpackString();
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("[Mount] UnMount request from " + rpc.getClientDetails() + " path=" + mountPath);
|
||||
|
||||
// Remove the mount details from the active mount list
|
||||
|
||||
m_mounts.removeEntry(mountPath, sess.getRemoteName());
|
||||
|
||||
// Build the RPC response
|
||||
|
||||
rpc.buildResponseHeader();
|
||||
|
||||
// Return the RPC response
|
||||
|
||||
return rpc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the unmoount all request
|
||||
*
|
||||
* @param sess
|
||||
* NFSSrvSession
|
||||
* @param rpc
|
||||
* RpcPacket
|
||||
* @param version
|
||||
* int
|
||||
* @return RpcPacket
|
||||
*/
|
||||
private final RpcPacket procUnMountAll(NFSSrvSession sess, RpcPacket rpc,
|
||||
int version) {
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("[Mount] UnMountAll request from " + rpc.getClientDetails());
|
||||
|
||||
// Remove all the mount details for the specified host
|
||||
|
||||
m_mounts.removeHostEntries(sess.getRemoteName());
|
||||
|
||||
// Build the RPC response
|
||||
|
||||
rpc.buildResponseHeader();
|
||||
|
||||
// Return the RPC response
|
||||
|
||||
return rpc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the export request
|
||||
*
|
||||
* @param sess
|
||||
* NFSSrvSession
|
||||
* @param rpc
|
||||
* RpcPacket
|
||||
* @param version
|
||||
* int
|
||||
* @return RpcPacket
|
||||
*/
|
||||
private final RpcPacket procExport(NFSSrvSession sess, RpcPacket rpc,
|
||||
int version) {
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("[Mount] Export request from " + rpc.getClientDetails());
|
||||
|
||||
// Get the share list from the server
|
||||
|
||||
SharedDeviceList shareList = sess.getServer().getShareMapper()
|
||||
.getShareList(getConfiguration().getServerName(), sess, false);
|
||||
|
||||
// Check if there is an access control manager configured
|
||||
|
||||
if (sess.getServer().hasAccessControlManager()) {
|
||||
|
||||
// Filter the list of available shares by applying any access
|
||||
// control rules
|
||||
|
||||
AccessControlManager aclMgr = sess.getServer().getAccessControlManager();
|
||||
|
||||
shareList = aclMgr.filterShareList(sess, shareList);
|
||||
}
|
||||
|
||||
// Build the response header
|
||||
|
||||
rpc.buildResponseHeader();
|
||||
|
||||
// Add the visible shares to the export list
|
||||
|
||||
Enumeration enm = shareList.enumerateShares();
|
||||
|
||||
while (enm.hasMoreElements()) {
|
||||
|
||||
// Get the current share
|
||||
|
||||
SharedDevice share = (SharedDevice) enm.nextElement();
|
||||
|
||||
// Add to the list of exports if it is a disk type share
|
||||
|
||||
if (share.getType() == ShareType.DISK) {
|
||||
|
||||
// Pack the share details
|
||||
|
||||
rpc.packInt(Rpc.True);
|
||||
rpc.packString("/" + share.getName());
|
||||
|
||||
// No group information
|
||||
|
||||
rpc.packInt(Rpc.False);
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the end of the list
|
||||
|
||||
rpc.packInt(Rpc.False);
|
||||
rpc.setLength();
|
||||
|
||||
// Return the response
|
||||
|
||||
return rpc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the export all request
|
||||
*
|
||||
* @param sess
|
||||
* NFSSrvSession
|
||||
* @param rpc
|
||||
* RpcPacket
|
||||
* @return RpcPacket
|
||||
*/
|
||||
private final RpcPacket procExportAll(NFSSrvSession sess, RpcPacket rpc) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a temporary session for a request. There is no need to cache
|
||||
* sessions in the mount server as usually only single requests are made.
|
||||
*
|
||||
* @param rpc
|
||||
* RpcPacket
|
||||
* @return NFSSrvSession
|
||||
* @exception RpcAuthenticationException
|
||||
*/
|
||||
private final NFSSrvSession createTemporarySession(RpcPacket rpc)
|
||||
throws RpcAuthenticationException {
|
||||
|
||||
// Authenticate the request
|
||||
|
||||
RpcAuthenticator rpcAuth = getConfiguration().getRpcAuthenticator();
|
||||
Object sessKey = rpcAuth.authenticateRpcClient( rpc.getCredentialsType(), rpc);
|
||||
|
||||
// Create an NFS session for the request
|
||||
|
||||
NFSSrvSession nfsSess = new NFSSrvSession(this, rpc.getClientAddress(), rpc.getClientPort(), rpc.getClientProtocol());
|
||||
|
||||
// Set the client information for the request
|
||||
|
||||
nfsSess.setClientInformation(rpcAuth.getRpcClientInformation(sessKey, rpc));
|
||||
|
||||
// Return the session
|
||||
|
||||
return nfsSess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mount a path. Used by the mount server to validate a path and initialize
|
||||
* any NFS resources
|
||||
*
|
||||
* @param sess
|
||||
* NFSSrvSession
|
||||
* @param path
|
||||
* String
|
||||
* @param handle
|
||||
* byte[]
|
||||
* @return int
|
||||
*/
|
||||
protected final int mountPath(NFSSrvSession sess, String path, byte[] handle) {
|
||||
|
||||
// Debug
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("MountPath path=" + path);
|
||||
|
||||
// Parse the path into share and additional path components
|
||||
|
||||
if (path.startsWith(UNIX_SEPERATOR) && path.length() >= 2) {
|
||||
|
||||
// Split the path into share and any additional path
|
||||
|
||||
String shareName = null;
|
||||
String extraPath = null;
|
||||
int shareId = -1;
|
||||
|
||||
int pos = path.indexOf(UNIX_SEPERATOR, 1);
|
||||
if (pos != -1) {
|
||||
shareName = path.substring(1, pos);
|
||||
extraPath = path.substring(pos);
|
||||
} else {
|
||||
shareName = path.substring(1);
|
||||
}
|
||||
|
||||
// Search for a share with the specified name
|
||||
|
||||
SharedDevice share = null;
|
||||
|
||||
try {
|
||||
share = getConfiguration().getShareMapper().findShare(
|
||||
getConfiguration().getServerName(), shareName,
|
||||
ShareType.DISK, sess, false);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
}
|
||||
|
||||
// Check if the share exists
|
||||
|
||||
if (share != null) {
|
||||
|
||||
// Check if there is an access control manager configured
|
||||
|
||||
if (getConfiguration().hasAccessControlManager()) {
|
||||
|
||||
// Check the access control to the shared filesystem
|
||||
|
||||
AccessControlManager aclMgr = getConfiguration().getAccessControlManager();
|
||||
|
||||
if (aclMgr.checkAccessControl(sess, share) == AccessControl.NoAccess) {
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Failed to mount path=" + path + ", access denied");
|
||||
|
||||
// Return a does not exist type error
|
||||
|
||||
return Mount.StsNoEnt;
|
||||
}
|
||||
}
|
||||
|
||||
// The share id is the hash of the share name
|
||||
|
||||
shareId = shareName.hashCode();
|
||||
|
||||
// Check if there is an extra path to validate
|
||||
|
||||
if (extraPath != null) {
|
||||
|
||||
// Convert the path to an SMB share relative path
|
||||
|
||||
extraPath = extraPath.replace(UNIX_SEPERATOR_CHAR, DOS_SEPERATOR_CHAR);
|
||||
if (extraPath.endsWith(DOS_SEPERATOR))
|
||||
extraPath = extraPath.substring(0, extraPath.length() - 2);
|
||||
|
||||
try {
|
||||
|
||||
// Get the disk shared device
|
||||
|
||||
TreeConnection conn = m_connections.findConnection(shareId);
|
||||
DiskInterface disk = (DiskInterface) conn.getSharedDevice().getInterface();
|
||||
|
||||
// Validate the path
|
||||
|
||||
FileInfo finfo = disk.getFileInformation(sess, conn, extraPath);
|
||||
|
||||
if (finfo == null)
|
||||
return Mount.StsNoEnt;
|
||||
else if (finfo.isDirectory() == false)
|
||||
return Mount.StsNotDir;
|
||||
|
||||
// Fill in the handle for the directory
|
||||
|
||||
NFSHandle.packDirectoryHandle(shareId, finfo.getFileId(), handle);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// Fill in the handle using a share type handle
|
||||
|
||||
NFSHandle.packShareHandle(share.getName(), handle);
|
||||
}
|
||||
|
||||
// Add a new entry to the active mount list
|
||||
|
||||
m_mounts.addEntry(new MountEntry(sess.getRemoteName(), path));
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Mounted path=" + path + ", handle=" + NFSHandle.asString(handle));
|
||||
|
||||
// Return a success status
|
||||
|
||||
return Mount.StsSuccess;
|
||||
} else {
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Failed to mount path=" + path);
|
||||
|
||||
// Indicate that the share does not exist
|
||||
|
||||
return Mount.StsNoEnt;
|
||||
}
|
||||
}
|
||||
|
||||
// Return an invalid path error
|
||||
|
||||
return Mount.StsNoEnt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a buffer for a file handle, the size depends on the RPC version
|
||||
*
|
||||
* @param version
|
||||
* int
|
||||
* @return byte[]
|
||||
*/
|
||||
private final byte[] allocateFileHandle(int version) {
|
||||
byte[] handle = null;
|
||||
if (version == 1)
|
||||
handle = new byte[Mount.FileHandleSize1];
|
||||
else if (version == 3)
|
||||
handle = new byte[Mount.FileHandleSize3];
|
||||
return handle;
|
||||
}
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc.nfs;
|
||||
|
||||
/**
|
||||
* Bad Cookie Exception Class
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class BadCookieException extends Exception {
|
||||
|
||||
// Object version id
|
||||
|
||||
private static final long serialVersionUID = -6689748925525944869L;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public BadCookieException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param msg String
|
||||
*/
|
||||
public BadCookieException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc.nfs;
|
||||
|
||||
/**
|
||||
* Bad Handle Exception Class
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class BadHandleException extends Exception {
|
||||
|
||||
// Object version id
|
||||
|
||||
private static final long serialVersionUID = 5928520475130958599L;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public BadHandleException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param msg String
|
||||
*/
|
||||
public BadHandleException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc.nfs;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* File Id Cache Class
|
||||
*
|
||||
* <p>Converts a file/directory id to a share relative path.
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class FileIdCache {
|
||||
|
||||
// File id to path cache
|
||||
|
||||
private Hashtable<Integer, String> m_idCache;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public FileIdCache() {
|
||||
m_idCache = new Hashtable<Integer, String>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an entry to the cache
|
||||
*
|
||||
* @param fid int
|
||||
* @param path String
|
||||
*/
|
||||
public final void addPath(int fid, String path) {
|
||||
m_idCache.put(new Integer(fid), path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a file id to a path
|
||||
*
|
||||
* @param fid int
|
||||
* @return String
|
||||
*/
|
||||
public final String findPath(int fid) {
|
||||
return (String) m_idCache.get(new Integer(fid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an entry from the cache
|
||||
*
|
||||
* @param fid int
|
||||
*/
|
||||
public final void deletePath(int fid) {
|
||||
m_idCache.remove(new Integer(fid));
|
||||
}
|
||||
}
|
267
source/java/org/alfresco/filesys/server/oncrpc/nfs/NFS.java
Normal file
267
source/java/org/alfresco/filesys/server/oncrpc/nfs/NFS.java
Normal file
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc.nfs;
|
||||
|
||||
/**
|
||||
* NFS Server Constants Class
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public final class NFS {
|
||||
|
||||
// Default NFS server port
|
||||
|
||||
public static final int DefaultPort = 2049;
|
||||
|
||||
// Program and version id
|
||||
|
||||
public static final int ProgramId = 100003;
|
||||
public static final int VersionId = 3;
|
||||
|
||||
// RPC procedure ids
|
||||
|
||||
public static final int ProcNull = 0;
|
||||
public static final int ProcGetAttr = 1;
|
||||
public static final int ProcSetAttr = 2;
|
||||
public static final int ProcLookup = 3;
|
||||
public static final int ProcAccess = 4;
|
||||
public static final int ProcReadLink = 5;
|
||||
public static final int ProcRead = 6;
|
||||
public static final int ProcWrite = 7;
|
||||
public static final int ProcCreate = 8;
|
||||
public static final int ProcMkDir = 9;
|
||||
public static final int ProcSymLink = 10;
|
||||
public static final int ProcMkNode = 11;
|
||||
public static final int ProcRemove = 12;
|
||||
public static final int ProcRmDir = 13;
|
||||
public static final int ProcRename = 14;
|
||||
public static final int ProcLink = 15;
|
||||
public static final int ProcReadDir = 16;
|
||||
public static final int ProcReadDirPlus = 17;
|
||||
public static final int ProcFsStat = 18;
|
||||
public static final int ProcFsInfo = 19;
|
||||
public static final int ProcPathConf = 20;
|
||||
public static final int ProcCommit = 21;
|
||||
public static final int ProcMax = 21;
|
||||
|
||||
// NFS server status codes
|
||||
|
||||
public static final int StsSuccess = 0;
|
||||
public static final int StsPerm = 1;
|
||||
public static final int StsNoEnt = 2;
|
||||
public static final int StsIO = 5;
|
||||
public static final int StsNxIO = 6;
|
||||
public static final int StsAccess = 13;
|
||||
public static final int StsExist = 17;
|
||||
public static final int StsXDev = 18;
|
||||
public static final int StsNoDev = 19;
|
||||
public static final int StsNotDir = 20;
|
||||
public static final int StsIsDir = 21;
|
||||
public static final int StsInVal = 22;
|
||||
public static final int StsFBig = 27;
|
||||
public static final int StsNoSpc = 28;
|
||||
public static final int StsROFS = 30;
|
||||
public static final int StsMLink = 31;
|
||||
public static final int StsNameTooLong = 63;
|
||||
public static final int StsNotEmpty = 66;
|
||||
public static final int StsDQuot = 69;
|
||||
public static final int StsStale = 70;
|
||||
public static final int StsRemote = 71;
|
||||
public static final int StsBadHandle = 10001;
|
||||
public static final int StsNotSync = 10002;
|
||||
public static final int StsBadCookie = 10003;
|
||||
public static final int StsNotSupp = 10004;
|
||||
public static final int StsTooSmall = 10005;
|
||||
public static final int StsServerFault = 10006;
|
||||
public static final int StsBadType = 10007;
|
||||
public static final int StsJukeBox = 10008;
|
||||
|
||||
// Data structure limits
|
||||
|
||||
public static final int FileHandleSize = 32; // can be 64 for NFS v3
|
||||
public static final int WriteVerfSize = 8;
|
||||
public static final int CreateVerfSize = 8;
|
||||
public static final int CookieVerfSize = 8;
|
||||
|
||||
// File types
|
||||
|
||||
public static final int FileTypeReg = 1;
|
||||
public static final int FileTypeDir = 2;
|
||||
public static final int FileTypeBlk = 3;
|
||||
public static final int FileTypeChr = 4;
|
||||
public static final int FileTypeLnk = 5;
|
||||
public static final int FileTypeSock = 6;
|
||||
public static final int FileTypeFifo = 7;
|
||||
|
||||
// Filesystem properties
|
||||
|
||||
public static final int FileSysLink = 0x0001; // supports hard links
|
||||
public static final int FileSysSymLink = 0x0002; // supports symbolic links
|
||||
public static final int FileSysHomogeneuos = 0x0004; // PATHCONF valid for all files
|
||||
public static final int FileSysCanSetTime = 0x0008; // can set time on server side
|
||||
|
||||
// Access mask
|
||||
|
||||
public static final int AccessRead = 0x0001;
|
||||
public static final int AccessLookup = 0x0002;
|
||||
public static final int AccessModify = 0x0004;
|
||||
public static final int AccessExtend = 0x0008;
|
||||
public static final int AccessDelete = 0x0010;
|
||||
public static final int AccessExecute = 0x0020;
|
||||
public static final int AccessAll = 0x003F;
|
||||
|
||||
// Create mode values
|
||||
|
||||
public static final int CreateUnchecked = 1;
|
||||
public static final int CreateGuarded = 2;
|
||||
public static final int CreateExclusive = 3;
|
||||
|
||||
// Write request stable values
|
||||
|
||||
public static final int WriteUnstable = 0;
|
||||
public static final int WriteDataSync = 1;
|
||||
public static final int WriteFileSync = 2;
|
||||
|
||||
// Set attributes file timestamp settings
|
||||
|
||||
public static final int DoNotSetTime = 0;
|
||||
public static final int SetTimeServer = 1;
|
||||
public static final int SetTimeClient = 2;
|
||||
|
||||
// RPC procedure names
|
||||
|
||||
private static final String[] _procNames = { "Null", "GetAttr", "SetAttr",
|
||||
"Lookup", "Access", "ReadLink", "Read", "Write", "Create", "MkDir",
|
||||
"SymLink", "MkNode", "Remove", "RmDir", "Rename", "Link",
|
||||
"ReadDir", "ReadDirPlus", "FsStat", "FsInfo", "PathConf", "Commit" };
|
||||
|
||||
/**
|
||||
* Return a procedure id as a name
|
||||
*
|
||||
* @param id
|
||||
* int
|
||||
* @return String
|
||||
*/
|
||||
public final static String getProcedureName(int id) {
|
||||
if (id < 0 || id > ProcMax)
|
||||
return null;
|
||||
return _procNames[id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an error status string for the specified status code
|
||||
*
|
||||
* @param sts
|
||||
* int
|
||||
* @return String
|
||||
*/
|
||||
public static final String getStatusString(int sts) {
|
||||
String str = null;
|
||||
|
||||
switch (sts) {
|
||||
case StsSuccess:
|
||||
str = "Success status";
|
||||
break;
|
||||
case StsAccess:
|
||||
str = "Access denied";
|
||||
break;
|
||||
case StsBadCookie:
|
||||
str = "Bad cookie";
|
||||
break;
|
||||
case StsBadHandle:
|
||||
str = "Bad handle";
|
||||
break;
|
||||
case StsBadType:
|
||||
str = "Bad type";
|
||||
break;
|
||||
case StsDQuot:
|
||||
str = "Quota exceeded";
|
||||
break;
|
||||
case StsPerm:
|
||||
str = "No permission";
|
||||
break;
|
||||
case StsExist:
|
||||
str = "Already exists";
|
||||
break;
|
||||
case StsFBig:
|
||||
str = "File too large";
|
||||
break;
|
||||
case StsInVal:
|
||||
str = "Invalid argument";
|
||||
break;
|
||||
case StsIO:
|
||||
str = "I/O error";
|
||||
break;
|
||||
case StsIsDir:
|
||||
str = "Is directory";
|
||||
break;
|
||||
case StsJukeBox:
|
||||
str = "Jukebox";
|
||||
break;
|
||||
case StsMLink:
|
||||
str = "Too many hard links";
|
||||
break;
|
||||
case StsNameTooLong:
|
||||
str = "Name too long";
|
||||
break;
|
||||
case StsNoDev:
|
||||
str = "No such device";
|
||||
break;
|
||||
case StsNoEnt:
|
||||
str = "No entity";
|
||||
break;
|
||||
case StsNoSpc:
|
||||
str = "No space left on device";
|
||||
break;
|
||||
case StsNotSync:
|
||||
str = "Update synchronization mismatch";
|
||||
break;
|
||||
case StsNotDir:
|
||||
str = "Not directory";
|
||||
break;
|
||||
case StsNotEmpty:
|
||||
str = "Not empty";
|
||||
break;
|
||||
case StsNotSupp:
|
||||
str = "Not supported";
|
||||
break;
|
||||
case StsNxIO:
|
||||
str = "Nxio";
|
||||
break;
|
||||
case StsRemote:
|
||||
str = "Too many levels of remote in path";
|
||||
break;
|
||||
case StsROFS:
|
||||
str = "Readonly filesystem";
|
||||
break;
|
||||
case StsServerFault:
|
||||
str = "Server fault";
|
||||
break;
|
||||
case StsStale:
|
||||
str = "Stale";
|
||||
break;
|
||||
case StsTooSmall:
|
||||
str = "Too small";
|
||||
break;
|
||||
case StsXDev:
|
||||
str = "Cross device hard link attempted";
|
||||
break;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
}
|
@@ -0,0 +1,417 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc.nfs;
|
||||
|
||||
import org.alfresco.filesys.server.oncrpc.RpcPacket;
|
||||
import org.alfresco.filesys.util.DataPacker;
|
||||
|
||||
/**
|
||||
* NFS Handle Class
|
||||
*
|
||||
* <p>Contains constants and static methods used with NFS handles.
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class NFSHandle {
|
||||
|
||||
// Version
|
||||
|
||||
public static final byte VERSION = 1;
|
||||
public static final byte MIN_VERSION = 1;
|
||||
public static final byte MAX_VERSION = 1;
|
||||
|
||||
// Handle types
|
||||
|
||||
public static final byte TYPE_SHARE = 1;
|
||||
public static final byte TYPE_DIR = 2;
|
||||
public static final byte TYPE_FILE = 3;
|
||||
|
||||
// Offsets to fields within the handle
|
||||
|
||||
private static final int VERSION_OFFSET = 0;
|
||||
private static final int TYPE_OFFSET = 1;
|
||||
private static final int SHARE_OFFSET = 2;
|
||||
private static final int DIR_OFFSET = 6;
|
||||
private static final int FILE_OFFSET = 10;
|
||||
private static final int NAME_OFFSET = 14;
|
||||
|
||||
/**
|
||||
* Return the handle version
|
||||
*
|
||||
* @param handle byte[]
|
||||
*/
|
||||
public static final int isVersion(byte[] handle)
|
||||
{
|
||||
return (int) handle[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the handle type
|
||||
*
|
||||
* @param handle byte[]
|
||||
* @return int
|
||||
*/
|
||||
public static final int isType(byte[] handle)
|
||||
{
|
||||
return (int) handle[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the handle is a share type handle
|
||||
*
|
||||
* @param handle byte[]
|
||||
* @return boolean
|
||||
*/
|
||||
public static final boolean isShareHandle(byte[] handle)
|
||||
{
|
||||
if (handle[1] == TYPE_SHARE)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the handle is a directory type handle
|
||||
*
|
||||
* @param handle byte[]
|
||||
* @return boolean
|
||||
*/
|
||||
public static final boolean isDirectoryHandle(byte[] handle)
|
||||
{
|
||||
if (handle[1] == TYPE_DIR)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the handle is a file type handle
|
||||
*
|
||||
* @param handle byte[]
|
||||
* @return boolean
|
||||
*/
|
||||
public static final boolean isFileHandle(byte[] handle)
|
||||
{
|
||||
if (handle[1] == TYPE_FILE)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pack a share handle
|
||||
*
|
||||
* @param name String
|
||||
* @param handle byte[]
|
||||
*/
|
||||
public static final void packShareHandle(String name, byte[] handle)
|
||||
{
|
||||
|
||||
// Pack a share handle
|
||||
|
||||
handle[0] = VERSION;
|
||||
handle[1] = TYPE_SHARE;
|
||||
|
||||
// Pack the hash code of the share name
|
||||
|
||||
DataPacker.putInt(name.hashCode(), handle, SHARE_OFFSET);
|
||||
|
||||
// Null pad the handle
|
||||
|
||||
int pos = SHARE_OFFSET + 4;
|
||||
|
||||
while (pos < handle.length)
|
||||
handle[pos++] = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pack a share handle
|
||||
*
|
||||
* @param name String
|
||||
* @param rpc RpcPacket
|
||||
* @param hlen int
|
||||
*/
|
||||
public static final void packShareHandle(String name, RpcPacket rpc, int hlen)
|
||||
{
|
||||
|
||||
// Pack a share handle
|
||||
|
||||
rpc.packInt(hlen);
|
||||
|
||||
rpc.packByte(VERSION);
|
||||
rpc.packByte(TYPE_SHARE);
|
||||
|
||||
// Pack the hash code of the share name
|
||||
|
||||
rpc.packInt(name.hashCode());
|
||||
|
||||
// Null pad the handle
|
||||
|
||||
rpc.packNulls(hlen - 6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pack a directory handle
|
||||
*
|
||||
* @param shareId int
|
||||
* @param dirId int
|
||||
* @param handle byte[]
|
||||
*/
|
||||
public static final void packDirectoryHandle(int shareId, int dirId, byte[] handle)
|
||||
{
|
||||
|
||||
// Pack a directory handle
|
||||
|
||||
handle[0] = VERSION;
|
||||
handle[1] = TYPE_DIR;
|
||||
|
||||
DataPacker.putInt(shareId, handle, 2);
|
||||
DataPacker.putInt(dirId, handle, 6);
|
||||
|
||||
// Null pad the handle
|
||||
|
||||
for (int i = 10; i < handle.length; i++)
|
||||
handle[i] = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pack a directory handle
|
||||
*
|
||||
* @param shareId int
|
||||
* @param dirId int
|
||||
* @param rpc RpcPacket
|
||||
* @param hlen int
|
||||
*/
|
||||
public static final void packDirectoryHandle(int shareId, int dirId, RpcPacket rpc, int hlen)
|
||||
{
|
||||
|
||||
// Pack a directory handle
|
||||
|
||||
rpc.packInt(hlen);
|
||||
|
||||
rpc.packByte(VERSION);
|
||||
rpc.packByte(TYPE_DIR);
|
||||
|
||||
rpc.packInt(shareId);
|
||||
rpc.packInt(dirId);
|
||||
|
||||
// Null pad the handle
|
||||
|
||||
rpc.packNulls(hlen - 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pack a file handle
|
||||
*
|
||||
* @param shareId int
|
||||
* @param dirId int
|
||||
* @param fileId int
|
||||
* @param handle byte[]
|
||||
*/
|
||||
public static final void packFileHandle(int shareId, int dirId, int fileId, byte[] handle)
|
||||
{
|
||||
|
||||
// Pack a directory handle
|
||||
|
||||
handle[0] = VERSION;
|
||||
handle[1] = TYPE_FILE;
|
||||
|
||||
DataPacker.putInt(shareId, handle, 2);
|
||||
DataPacker.putInt(dirId, handle, 6);
|
||||
DataPacker.putInt(fileId, handle, 10);
|
||||
|
||||
// Null pad the handle
|
||||
|
||||
for (int i = 14; i < handle.length; i++)
|
||||
handle[i] = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pack a file handle
|
||||
*
|
||||
* @param shareId int
|
||||
* @param dirId int
|
||||
* @param fileId int
|
||||
* @param rpc RpcPacket
|
||||
* @param hlen int
|
||||
*/
|
||||
public static final void packFileHandle(int shareId, int dirId, int fileId, RpcPacket rpc, int hlen)
|
||||
{
|
||||
|
||||
// Pack a directory handle
|
||||
|
||||
rpc.packInt(hlen);
|
||||
|
||||
rpc.packByte(VERSION);
|
||||
rpc.packByte(TYPE_FILE);
|
||||
|
||||
rpc.packInt(shareId);
|
||||
rpc.packInt(dirId);
|
||||
rpc.packInt(fileId);
|
||||
|
||||
// Null pad the handle
|
||||
|
||||
rpc.packNulls(hlen - 14);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack a share id from a handle
|
||||
*
|
||||
* @param handle byte[]
|
||||
* @return int
|
||||
*/
|
||||
public static final int unpackShareId(byte[] handle)
|
||||
{
|
||||
|
||||
// Check if the handle is a share type handle
|
||||
|
||||
int shareId = -1;
|
||||
|
||||
if (handle[1] == TYPE_SHARE || handle[1] == TYPE_DIR || handle[1] == TYPE_FILE)
|
||||
{
|
||||
|
||||
// Unpack the share id
|
||||
|
||||
shareId = DataPacker.getInt(handle, 2);
|
||||
}
|
||||
|
||||
// Return the share id, or -1 if wrong handle type
|
||||
|
||||
return shareId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack a directory id from a handle
|
||||
*
|
||||
* @param handle byte[]
|
||||
* @return int
|
||||
*/
|
||||
public static final int unpackDirectoryId(byte[] handle)
|
||||
{
|
||||
|
||||
// Check if the handle is a directory or file type handle
|
||||
|
||||
int dirId = -1;
|
||||
|
||||
if (handle[1] == TYPE_DIR || handle[1] == TYPE_FILE)
|
||||
{
|
||||
|
||||
// Unpack the directory id
|
||||
|
||||
dirId = DataPacker.getInt(handle, 6);
|
||||
}
|
||||
|
||||
// Return the directory id, or -1 if wrong handle type
|
||||
|
||||
return dirId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack a file id from a handle
|
||||
*
|
||||
* @param handle byte[]
|
||||
* @return int
|
||||
*/
|
||||
public static final int unpackFileId(byte[] handle)
|
||||
{
|
||||
|
||||
// Check if the handle is a file type handle
|
||||
|
||||
int fileId = -1;
|
||||
|
||||
if (handle[1] == TYPE_FILE)
|
||||
{
|
||||
|
||||
// Unpack the file id
|
||||
|
||||
fileId = DataPacker.getInt(handle, 10);
|
||||
}
|
||||
|
||||
// Return the file id, or -1 if wrong handle type
|
||||
|
||||
return fileId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an NFS handle as a string
|
||||
*
|
||||
* @param handle byte[]
|
||||
* @return String
|
||||
*/
|
||||
public static final String asString(byte[] handle)
|
||||
{
|
||||
|
||||
// Check if the handle is a valid type
|
||||
|
||||
StringBuffer str = new StringBuffer();
|
||||
str.append("[");
|
||||
|
||||
switch (handle[1])
|
||||
{
|
||||
|
||||
// Share/mountpoint type handle
|
||||
|
||||
case TYPE_SHARE:
|
||||
str.append("Share:0x");
|
||||
str.append(Integer.toHexString(DataPacker.getInt(handle, 2)));
|
||||
break;
|
||||
|
||||
// Directory handle
|
||||
|
||||
case TYPE_DIR:
|
||||
str.append("Dir:share=0x");
|
||||
str.append(Integer.toHexString(DataPacker.getInt(handle, 2)));
|
||||
str.append(",dir=0x");
|
||||
str.append(Integer.toHexString(DataPacker.getInt(handle, 6)));
|
||||
break;
|
||||
|
||||
// File handle
|
||||
|
||||
case TYPE_FILE:
|
||||
str.append("File:share=0x");
|
||||
str.append(Integer.toHexString(DataPacker.getInt(handle, 2)));
|
||||
str.append(",dir=0x");
|
||||
str.append(Integer.toHexString(DataPacker.getInt(handle, 6)));
|
||||
str.append(",file=0x");
|
||||
str.append(Integer.toHexString(DataPacker.getInt(handle, 10)));
|
||||
break;
|
||||
}
|
||||
|
||||
// Return the handle string
|
||||
|
||||
str.append("]");
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a handle is valid
|
||||
*
|
||||
* @param handle byte[]
|
||||
* @return boolean
|
||||
*/
|
||||
public static final boolean isValid(byte[] handle)
|
||||
{
|
||||
|
||||
// Check if the version is valid
|
||||
|
||||
if (handle[0] < MIN_VERSION || handle[0] > MAX_VERSION)
|
||||
return false;
|
||||
|
||||
// Check if the handle type is valid
|
||||
|
||||
if (handle[1] == TYPE_SHARE || handle[1] == TYPE_DIR || handle[1] == TYPE_FILE)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
5104
source/java/org/alfresco/filesys/server/oncrpc/nfs/NFSServer.java
Normal file
5104
source/java/org/alfresco/filesys/server/oncrpc/nfs/NFSServer.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc.nfs;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* NFS Server Session Table Class
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class NFSSessionTable {
|
||||
|
||||
// Session list
|
||||
|
||||
private Hashtable<Object, NFSSrvSession> m_sessions;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public NFSSessionTable()
|
||||
{
|
||||
m_sessions = new Hashtable<Object, NFSSrvSession>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of sessions in the list
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int numberOfSessions()
|
||||
{
|
||||
return m_sessions.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a session to the list
|
||||
*
|
||||
* @param sess NFSSrvSession
|
||||
*/
|
||||
public final void addSession(NFSSrvSession sess)
|
||||
{
|
||||
m_sessions.put(sess.getAuthIdentifier(), sess);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the session using the authentication identifier
|
||||
*
|
||||
* @param authIdent Object
|
||||
* @return NFSSrvSession
|
||||
*/
|
||||
public final NFSSrvSession findSession(Object authIdent)
|
||||
{
|
||||
return (NFSSrvSession) m_sessions.get(authIdent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a session from the list
|
||||
*
|
||||
* @param sess NFSSrvSession
|
||||
* @return NFSSrvSession
|
||||
*/
|
||||
public final NFSSrvSession removeSession(NFSSrvSession sess)
|
||||
{
|
||||
return removeSession(sess.getAuthIdentifier());
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a session from the list
|
||||
*
|
||||
* @param authIdent Object
|
||||
* @return NFSSrvSession
|
||||
*/
|
||||
public final NFSSrvSession removeSession(Object authIdent)
|
||||
{
|
||||
|
||||
// Find the required session
|
||||
|
||||
NFSSrvSession sess = findSession(authIdent);
|
||||
|
||||
// Remove the session and return the removed session
|
||||
|
||||
m_sessions.remove(authIdent);
|
||||
return sess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerate the session ids
|
||||
*
|
||||
* @return Enumeration
|
||||
*/
|
||||
public final Enumeration<Object> enumerate()
|
||||
{
|
||||
return m_sessions.keys();
|
||||
}
|
||||
}
|
@@ -0,0 +1,471 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc.nfs;
|
||||
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
|
||||
import org.alfresco.filesys.server.NetworkServer;
|
||||
import org.alfresco.filesys.server.SrvSession;
|
||||
import org.alfresco.filesys.server.core.DeviceInterface;
|
||||
import org.alfresco.filesys.server.filesys.SearchContext;
|
||||
import org.alfresco.filesys.server.filesys.TreeConnection;
|
||||
import org.alfresco.filesys.server.filesys.TreeConnectionHash;
|
||||
import org.alfresco.filesys.server.oncrpc.Rpc;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* NFS Server Session Class
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class NFSSrvSession extends SrvSession {
|
||||
|
||||
// Debug logging
|
||||
|
||||
private static final Log logger = LogFactory.getLog("org.alfresco.nfs.protocol");
|
||||
|
||||
// Default and maximum number of search slots
|
||||
|
||||
private static final int DefaultSearches = 32;
|
||||
private static final int MaxSearches = 256;
|
||||
|
||||
// Remote address and port
|
||||
|
||||
private InetAddress m_remAddr;
|
||||
private int m_remPort;
|
||||
|
||||
// Session type (TCP or UDP)
|
||||
|
||||
private int m_type;
|
||||
|
||||
// Authentication identifier
|
||||
//
|
||||
// Identifies this session uniquely within the authentication type being used by the client
|
||||
|
||||
private Object m_authIdentifier;
|
||||
|
||||
// Active tree connections
|
||||
|
||||
private TreeConnectionHash m_connections;
|
||||
|
||||
// Cache of currently open files
|
||||
|
||||
private NetworkFileCache m_fileCache;
|
||||
|
||||
// Last time the session was accessed. Used to determine when to expire UDP sessions.
|
||||
|
||||
private long m_lastAccess;
|
||||
|
||||
// Active search list for this session
|
||||
|
||||
private SearchContext[] m_search;
|
||||
private int m_searchCount;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param srv NetworkServer
|
||||
* @param addr InetAddress
|
||||
* @param port int
|
||||
* @param type int
|
||||
*/
|
||||
public NFSSrvSession(NetworkServer srv, InetAddress addr, int port, int type)
|
||||
{
|
||||
super(-1, srv, "NFS", null);
|
||||
|
||||
// Save the remove address/port and type
|
||||
|
||||
m_remAddr = addr;
|
||||
m_remPort = port;
|
||||
m_type = type;
|
||||
|
||||
// Create a unique id for the session from the remote address, port and type
|
||||
|
||||
StringBuffer str = new StringBuffer();
|
||||
|
||||
str.append(type == Rpc.TCP ? "T" : "U");
|
||||
str.append(m_remAddr.getHostAddress());
|
||||
str.append(":");
|
||||
str.append(m_remPort);
|
||||
|
||||
setUniqueId(str.toString());
|
||||
|
||||
// Set the remote name
|
||||
|
||||
setRemoteName(m_remAddr.getHostAddress());
|
||||
|
||||
// Initialize the last access date/time
|
||||
|
||||
setLastAccess(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the session type
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int isType()
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the open file cache
|
||||
*
|
||||
* @return NetworkFileCache
|
||||
*/
|
||||
public final NetworkFileCache getFileCache()
|
||||
{
|
||||
if (m_fileCache == null)
|
||||
m_fileCache = new NetworkFileCache(getUniqueId());
|
||||
return m_fileCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the session has an authentication identifier
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public final boolean hasAuthIdentifier()
|
||||
{
|
||||
return m_authIdentifier != null ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the authentication identifier
|
||||
*
|
||||
* @return Object
|
||||
*/
|
||||
public final Object getAuthIdentifier()
|
||||
{
|
||||
return m_authIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the client network address
|
||||
*
|
||||
* @return InetAddress
|
||||
*/
|
||||
public InetAddress getRemoteAddress()
|
||||
{
|
||||
return m_remAddr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the remote port
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getRemotePort()
|
||||
{
|
||||
return m_remPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last access date/time for the session
|
||||
*
|
||||
* @return long
|
||||
*/
|
||||
public final long getLastAccess()
|
||||
{
|
||||
return m_lastAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the tree connection for the specified share hash
|
||||
*
|
||||
* @param shareHash int
|
||||
* @return TreeConnection
|
||||
*/
|
||||
public final TreeConnection findConnection(int shareHash)
|
||||
{
|
||||
if (m_connections == null)
|
||||
return null;
|
||||
return m_connections.findConnection(shareHash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new connection to the list of active tree connections for this session
|
||||
*
|
||||
* @param tree TreeConnection
|
||||
*/
|
||||
public final void addConnection(TreeConnection tree)
|
||||
{
|
||||
if (m_connections == null)
|
||||
m_connections = new TreeConnectionHash();
|
||||
m_connections.addConnection(tree);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a connection from the list of active tree connections for this session
|
||||
*
|
||||
* @param tree TreeConnection
|
||||
*/
|
||||
public final void removeConnection(TreeConnection tree)
|
||||
{
|
||||
if (m_connections == null)
|
||||
return;
|
||||
m_connections.deleteConnection(tree.getSharedDevice().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the authentication identifier
|
||||
*
|
||||
* @param authIdent Object
|
||||
*/
|
||||
public final void setAuthIdentifier(Object authIdent)
|
||||
{
|
||||
m_authIdentifier = authIdent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the last access date/time for the session
|
||||
*
|
||||
* @param dateTime long
|
||||
*/
|
||||
public final void setLastAccess(long dateTime)
|
||||
{
|
||||
m_lastAccess = dateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the last access date/time for the session
|
||||
*/
|
||||
public final void setLastAccess()
|
||||
{
|
||||
m_lastAccess = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the session, cleanup any resources.
|
||||
*/
|
||||
public void closeSession()
|
||||
{
|
||||
|
||||
// Cleanup open files, tree connections and searches
|
||||
|
||||
cleanupSession();
|
||||
|
||||
// Call the base class
|
||||
|
||||
super.closeSession();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a slot in the active searches list for a new search.
|
||||
*
|
||||
* @param search SearchContext
|
||||
* @return int Search slot index, or -1 if there are no more search slots available.
|
||||
*/
|
||||
protected synchronized final int allocateSearchSlot(SearchContext search)
|
||||
{
|
||||
|
||||
// Check if the search array has been allocated
|
||||
|
||||
if (m_search == null)
|
||||
m_search = new SearchContext[DefaultSearches];
|
||||
|
||||
// Find a free slot for the new search
|
||||
|
||||
int idx = 0;
|
||||
|
||||
while (idx < m_search.length && m_search[idx] != null)
|
||||
idx++;
|
||||
|
||||
// Check if we found a free slot
|
||||
|
||||
if (idx == m_search.length)
|
||||
{
|
||||
|
||||
// The search array needs to be extended, check if we reached the limit.
|
||||
|
||||
if (m_search.length >= MaxSearches)
|
||||
return -1;
|
||||
|
||||
// Extend the search array
|
||||
|
||||
SearchContext[] newSearch = new SearchContext[m_search.length * 2];
|
||||
System.arraycopy(m_search, 0, newSearch, 0, m_search.length);
|
||||
m_search = newSearch;
|
||||
}
|
||||
|
||||
// If the search context is valid then store in the allocated slot
|
||||
|
||||
if (search != null)
|
||||
m_search[idx] = search;
|
||||
|
||||
// Return the allocated search slot index
|
||||
|
||||
m_searchCount++;
|
||||
return idx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deallocate the specified search context/slot.
|
||||
*
|
||||
* @param ctxId int
|
||||
*/
|
||||
protected synchronized final void deallocateSearchSlot(int ctxId)
|
||||
{
|
||||
|
||||
// Check if the search array has been allocated and that the index is valid
|
||||
|
||||
if (m_search == null || ctxId >= m_search.length)
|
||||
return;
|
||||
|
||||
// Close the search
|
||||
|
||||
if (m_search[ctxId] != null)
|
||||
m_search[ctxId].closeSearch();
|
||||
|
||||
// Free the specified search context slot
|
||||
|
||||
m_searchCount--;
|
||||
m_search[ctxId] = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the NFS server that the session is associated with
|
||||
*
|
||||
* @return NFSServer
|
||||
*/
|
||||
public final NFSServer getNFSServer()
|
||||
{
|
||||
return (NFSServer) getServer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the search context for the specified search id.
|
||||
*
|
||||
* @return com.starla.smbsrv.SearchContext
|
||||
* @param srchId int
|
||||
*/
|
||||
protected final SearchContext getSearchContext(int srchId)
|
||||
{
|
||||
|
||||
// Check if the search array is valid and the search index is valid
|
||||
|
||||
if (m_search == null || srchId >= m_search.length)
|
||||
return null;
|
||||
|
||||
// Return the required search context
|
||||
|
||||
return m_search[srchId];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of active tree searches.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getSearchCount()
|
||||
{
|
||||
return m_searchCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the seach context in the specified slot.
|
||||
*
|
||||
* @param slot Slot to store the search context.
|
||||
* @param srch com.starla.smbsrv.SearchContext
|
||||
*/
|
||||
protected final void setSearchContext(int slot, SearchContext srch)
|
||||
{
|
||||
|
||||
// Check if the search slot id is valid
|
||||
|
||||
if (m_search == null || slot > m_search.length)
|
||||
return;
|
||||
|
||||
// Store the context
|
||||
|
||||
m_search[slot] = srch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup any resources owned by this session, close files, searches and change notification requests.
|
||||
*/
|
||||
protected final void cleanupSession()
|
||||
{
|
||||
|
||||
// Debug
|
||||
|
||||
if (logger.isDebugEnabled() && hasDebug(NFSServer.DBG_SESSION))
|
||||
logger.debug("NFS Cleanup session, searches=" + getSearchCount() + ", files="
|
||||
+ (m_fileCache != null ? m_fileCache.numberOfEntries() : 0) + ", treeConns="
|
||||
+ (m_connections != null ? m_connections.numberOfEntries() : 0));
|
||||
|
||||
// Check if there are any active searches
|
||||
|
||||
if (m_search != null)
|
||||
{
|
||||
|
||||
// Close all active searches
|
||||
|
||||
for (int idx = 0; idx < m_search.length; idx++)
|
||||
{
|
||||
|
||||
// Check if the current search slot is active
|
||||
|
||||
if (m_search[idx] != null)
|
||||
deallocateSearchSlot(idx);
|
||||
}
|
||||
|
||||
// Release the search context list, clear the search count
|
||||
|
||||
m_search = null;
|
||||
m_searchCount = 0;
|
||||
}
|
||||
|
||||
// Close any open files
|
||||
|
||||
if (m_fileCache != null)
|
||||
m_fileCache.closeAllFiles();
|
||||
|
||||
// Check if there are open tree connections
|
||||
|
||||
if (m_connections != null && m_connections.numberOfEntries() > 0)
|
||||
{
|
||||
|
||||
// Enumerate the active connections
|
||||
|
||||
Enumeration conns = m_connections.enumerateConnections();
|
||||
|
||||
while (conns.hasMoreElements())
|
||||
{
|
||||
|
||||
// Get the current tree connection
|
||||
|
||||
TreeConnection tree = (TreeConnection) conns.nextElement();
|
||||
|
||||
tree.closeConnection(this);
|
||||
|
||||
// Inform the driver that the connection has been closed
|
||||
|
||||
DeviceInterface devIface = tree.getInterface();
|
||||
if (devIface != null)
|
||||
devIface.treeClosed(this, tree);
|
||||
|
||||
// Release the connection list
|
||||
|
||||
m_connections = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,444 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc.nfs;
|
||||
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
|
||||
import org.alfresco.filesys.server.filesys.DiskInterface;
|
||||
import org.alfresco.filesys.server.filesys.NetworkFile;
|
||||
import org.alfresco.filesys.server.filesys.TreeConnection;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Network File Cache Class
|
||||
*
|
||||
* <p>Caches the network files that are currently being accessed by the NFS server.
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class NetworkFileCache {
|
||||
|
||||
// Debug logging
|
||||
|
||||
private static final Log logger = LogFactory.getLog(NetworkFileCache.class);
|
||||
|
||||
// Default file timeout
|
||||
|
||||
public static final long DefaultFileTimeout = 5000L; // 5 seconds
|
||||
|
||||
// Network file cache, key is the file id
|
||||
|
||||
private Hashtable<Integer, FileEntry> m_fileCache;
|
||||
|
||||
// File expiry thread
|
||||
|
||||
private FileExpiry m_expiryThread;
|
||||
|
||||
// File timeout
|
||||
|
||||
private long m_fileTmo = DefaultFileTimeout;
|
||||
|
||||
// Debug enable flag
|
||||
|
||||
private boolean m_debug = false;
|
||||
|
||||
/**
|
||||
* File Entry Class
|
||||
*/
|
||||
protected class FileEntry {
|
||||
|
||||
// Network file
|
||||
|
||||
private NetworkFile m_file;
|
||||
|
||||
// Disk share connection
|
||||
|
||||
private TreeConnection m_conn;
|
||||
|
||||
// File timeout
|
||||
|
||||
private long m_timeout;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param file
|
||||
* NetworkFile
|
||||
* @param conn
|
||||
* TreeConnection
|
||||
*/
|
||||
public FileEntry(NetworkFile file, TreeConnection conn) {
|
||||
m_file = file;
|
||||
m_conn = conn;
|
||||
updateTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the file timeout
|
||||
*
|
||||
* @return long
|
||||
*/
|
||||
public final long getTimeout() {
|
||||
return m_timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the network file
|
||||
*
|
||||
* @return NetworkFile
|
||||
*/
|
||||
public final NetworkFile getFile() {
|
||||
return m_file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the disk share connection
|
||||
*
|
||||
* @return TreeConnection
|
||||
*/
|
||||
public final TreeConnection getConnection() {
|
||||
return m_conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the file timeout
|
||||
*/
|
||||
public final void updateTimeout() {
|
||||
m_timeout = System.currentTimeMillis() + m_fileTmo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the file timeout
|
||||
*
|
||||
* @param tmo
|
||||
* long
|
||||
*/
|
||||
public final void updateTimeout(long tmo) {
|
||||
m_timeout = tmo;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* File Expiry Thread Class
|
||||
*/
|
||||
protected class FileExpiry implements Runnable {
|
||||
|
||||
// Expiry thread
|
||||
|
||||
private Thread m_thread;
|
||||
|
||||
// Wakeup interval
|
||||
|
||||
private long m_wakeup;
|
||||
|
||||
// Shutdown flag
|
||||
|
||||
private boolean m_shutdown;
|
||||
|
||||
/**
|
||||
* Class Constructor
|
||||
*
|
||||
* @param wakeup
|
||||
* long
|
||||
* @param name
|
||||
* String
|
||||
*/
|
||||
public FileExpiry(long wakeup, String name) {
|
||||
|
||||
// Set the wakeup interval
|
||||
|
||||
m_wakeup = wakeup;
|
||||
|
||||
// Create and start the file expiry thread
|
||||
|
||||
m_thread = new Thread(this);
|
||||
m_thread.setDaemon(true);
|
||||
m_thread.setName("NFSFileExpiry_" + name);
|
||||
m_thread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Main thread method
|
||||
*/
|
||||
public void run() {
|
||||
|
||||
// Loop until shutdown
|
||||
|
||||
while (m_shutdown == false) {
|
||||
|
||||
// Sleep for a while
|
||||
|
||||
try {
|
||||
Thread.sleep(m_wakeup);
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
|
||||
// Get the current system time
|
||||
|
||||
long timeNow = System.currentTimeMillis();
|
||||
|
||||
// Check for expired files
|
||||
|
||||
synchronized (m_fileCache) {
|
||||
|
||||
// Enumerate the cache entries
|
||||
|
||||
Enumeration enm = m_fileCache.keys();
|
||||
|
||||
while (enm.hasMoreElements()) {
|
||||
|
||||
// Get the current key
|
||||
|
||||
Integer fileId = (Integer) enm.nextElement();
|
||||
|
||||
// Get the file entry and check if it has expired
|
||||
|
||||
FileEntry fentry = (FileEntry) m_fileCache.get(fileId);
|
||||
|
||||
if (fentry != null && fentry.getTimeout() < timeNow) {
|
||||
|
||||
// Get the network file
|
||||
|
||||
NetworkFile netFile = fentry.getFile();
|
||||
|
||||
// Check if the file has an I/O request pending, if
|
||||
// so then reset the file expiry time
|
||||
// for the file
|
||||
|
||||
if (netFile.hasIOPending()) {
|
||||
|
||||
// Update the expiry time for the file entry
|
||||
|
||||
fentry.updateTimeout();
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("NFSFileExpiry: I/O pending file=" + fentry.getFile().getFullName() + ", fid=" + fileId);
|
||||
} else {
|
||||
|
||||
// File entry has expired, remove it from the
|
||||
// cache
|
||||
|
||||
m_fileCache.remove(fileId);
|
||||
|
||||
// Close the file via the disk interface
|
||||
|
||||
try {
|
||||
|
||||
// Get the disk interface
|
||||
|
||||
DiskInterface disk = (DiskInterface) fentry
|
||||
.getConnection().getInterface();
|
||||
|
||||
// Close the file
|
||||
|
||||
disk.closeFile(null,
|
||||
fentry.getConnection(), netFile);
|
||||
} catch (IOException ex) {
|
||||
}
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("NFSFileExpiry: Closed file=" + fentry.getFile().getFullName() + ", fid=" + fileId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request the file expiry thread to shutdown
|
||||
*/
|
||||
public final void requestShutdown() {
|
||||
|
||||
// Set the shutdown flag
|
||||
|
||||
m_shutdown = true;
|
||||
|
||||
// Wakeup the thread
|
||||
|
||||
try {
|
||||
m_thread.interrupt();
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
|
||||
// Wait for the expiry thread to complete
|
||||
|
||||
try {
|
||||
m_thread.join(DefaultFileTimeout);
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param name
|
||||
* String
|
||||
*/
|
||||
public NetworkFileCache(String name) {
|
||||
|
||||
// Create the file cache
|
||||
|
||||
m_fileCache = new Hashtable<Integer, FileEntry>();
|
||||
|
||||
// Start the file expiry thread
|
||||
|
||||
m_expiryThread = new FileExpiry(DefaultFileTimeout / 4, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if debug output is enabled
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public final boolean hasDebug() {
|
||||
return m_debug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a file to the cache
|
||||
*
|
||||
* @param file
|
||||
* NetworkFile
|
||||
* @param conn
|
||||
* TreeConnection
|
||||
*/
|
||||
public synchronized final void addFile(NetworkFile file, TreeConnection conn) {
|
||||
synchronized (m_fileCache) {
|
||||
m_fileCache.put(new Integer(file.getFileId()), new FileEntry(file,
|
||||
conn));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a file from the cache
|
||||
*
|
||||
* @param id
|
||||
*/
|
||||
public synchronized final void removeFile(int id) {
|
||||
|
||||
// Create the search key
|
||||
|
||||
Integer fileId = new Integer(id);
|
||||
|
||||
synchronized (m_fileCache) {
|
||||
m_fileCache.remove(fileId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a file via the file id
|
||||
*
|
||||
* @param id
|
||||
* int
|
||||
* @return NetworkFile
|
||||
*/
|
||||
public synchronized final NetworkFile findFile(int id) {
|
||||
|
||||
// Create the search key
|
||||
|
||||
Integer fileId = new Integer(id);
|
||||
FileEntry fentry = null;
|
||||
|
||||
synchronized (m_fileCache) {
|
||||
fentry = (FileEntry) m_fileCache.get(fileId);
|
||||
}
|
||||
|
||||
// Return the file, or null if not found
|
||||
|
||||
if (fentry != null) {
|
||||
|
||||
// Update the file timeout and return the file
|
||||
|
||||
fentry.updateTimeout();
|
||||
return fentry.getFile();
|
||||
}
|
||||
|
||||
// Invalid file id
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the count of entries in the cache
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int numberOfEntries() {
|
||||
return m_fileCache.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the expiry cache, close and remove all files from the cache and
|
||||
* stop the expiry thread.
|
||||
*/
|
||||
public final void closeAllFiles() {
|
||||
|
||||
// Enumerate the cache entries
|
||||
|
||||
Enumeration keys = m_fileCache.keys();
|
||||
|
||||
while (keys.hasMoreElements()) {
|
||||
|
||||
// Get the current key and lookup the matching value
|
||||
|
||||
Integer key = (Integer) keys.nextElement();
|
||||
FileEntry entry = (FileEntry) m_fileCache.get(key);
|
||||
|
||||
// Expire the file entry
|
||||
|
||||
entry.updateTimeout(0L);
|
||||
}
|
||||
|
||||
// Shutdown the expiry thread, this should close the files
|
||||
|
||||
m_expiryThread.requestShutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the cache entries to the debug device
|
||||
*/
|
||||
public final void dumpCache() {
|
||||
|
||||
// Dump the count of entries in the cache
|
||||
|
||||
logger.debug("NetworkFileCache entries=" + numberOfEntries());
|
||||
|
||||
// Enumerate the cache entries
|
||||
|
||||
Enumeration keys = m_fileCache.keys();
|
||||
|
||||
while (keys.hasMoreElements()) {
|
||||
|
||||
// Get the current key and lookup the matching value
|
||||
|
||||
Integer key = (Integer) keys.nextElement();
|
||||
FileEntry entry = (FileEntry) m_fileCache.get(key);
|
||||
|
||||
// Dump the entry details
|
||||
|
||||
logger.debug("fid=" + key + ": " + entry);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc.nfs;
|
||||
|
||||
import org.alfresco.filesys.server.filesys.SearchContext;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Search Cache Class
|
||||
*
|
||||
* <p>Holds the details of the active searches for the NFS server
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class SearchCache {
|
||||
|
||||
// Debug logging
|
||||
|
||||
private static final Log logger = LogFactory.getLog(SearchCache.class);
|
||||
|
||||
// Maximum number of active searches
|
||||
|
||||
public static final int MaximumSearches = 255;
|
||||
|
||||
// Default search timeout
|
||||
|
||||
public static final long DefaultSearchTimeout = 30000L; // 30 seconds
|
||||
|
||||
// Array of active searches, last allocated index
|
||||
|
||||
private SearchEntry[] m_searches;
|
||||
private int m_lastIdx;
|
||||
|
||||
// Search timeout
|
||||
|
||||
private long m_searchTmo = DefaultSearchTimeout;
|
||||
|
||||
// Debug enable flag
|
||||
|
||||
private boolean m_debug = true;
|
||||
|
||||
/**
|
||||
* Search Entry Class
|
||||
*/
|
||||
protected class SearchEntry
|
||||
{
|
||||
|
||||
// Search context
|
||||
|
||||
private SearchContext m_search;
|
||||
|
||||
// Search timeout
|
||||
|
||||
private long m_timeout;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param search SearchContext
|
||||
*/
|
||||
public SearchEntry(SearchContext search)
|
||||
{
|
||||
m_search = search;
|
||||
updateTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the search timeout
|
||||
*
|
||||
* @return long
|
||||
*/
|
||||
public final long getTimeout()
|
||||
{
|
||||
return m_timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the search context
|
||||
*
|
||||
* @return SearchContext
|
||||
*/
|
||||
public final SearchContext getSearch()
|
||||
{
|
||||
return m_search;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the search timeout
|
||||
*/
|
||||
public final void updateTimeout()
|
||||
{
|
||||
m_timeout = System.currentTimeMillis() + m_searchTmo;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Search Expiry Thread Class
|
||||
*/
|
||||
protected class SearchExpiry implements Runnable
|
||||
{
|
||||
|
||||
// Expiry thread
|
||||
|
||||
private Thread m_thread;
|
||||
|
||||
// Wakeup interval
|
||||
|
||||
private long m_wakeup;
|
||||
|
||||
/**
|
||||
* Class Constructor
|
||||
*
|
||||
* @param wakeup long
|
||||
*/
|
||||
public SearchExpiry(long wakeup)
|
||||
{
|
||||
|
||||
// Set the wakeup interval
|
||||
|
||||
m_wakeup = wakeup;
|
||||
|
||||
// Create and start the search expiry thread
|
||||
|
||||
m_thread = new Thread(this);
|
||||
m_thread.setDaemon(true);
|
||||
m_thread.setName("NFSSearchExpiry");
|
||||
m_thread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Main thread method
|
||||
*/
|
||||
public void run()
|
||||
{
|
||||
|
||||
// Loop until shutdown
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
||||
// Sleep for a while
|
||||
|
||||
try
|
||||
{
|
||||
Thread.sleep(m_wakeup);
|
||||
} catch (InterruptedException ex)
|
||||
{
|
||||
}
|
||||
|
||||
// Get the current system time
|
||||
|
||||
long timeNow = System.currentTimeMillis();
|
||||
|
||||
// Check for expired searches
|
||||
|
||||
synchronized (m_searches)
|
||||
{
|
||||
|
||||
// Check all allocated slots
|
||||
|
||||
for (int i = 0; i < m_searches.length; i++)
|
||||
{
|
||||
|
||||
// Check if the current slot has a valid entry
|
||||
|
||||
if (m_searches[i] != null && m_searches[i].getTimeout() < timeNow)
|
||||
{
|
||||
|
||||
// Remove the current search entry
|
||||
|
||||
SearchEntry entry = m_searches[i];
|
||||
m_searches[i] = null;
|
||||
|
||||
// Close the search
|
||||
|
||||
entry.getSearch().closeSearch();
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("NFSSearchExpiry: Closed search=" + entry.getSearch().getSearchString()
|
||||
+ ", id=" + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public SearchCache()
|
||||
{
|
||||
|
||||
// Create the active search list
|
||||
|
||||
m_searches = new SearchEntry[MaximumSearches];
|
||||
|
||||
// Start the search expiry thread
|
||||
|
||||
new SearchExpiry(DefaultSearchTimeout / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if debug output is enabled
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public final boolean hasDebug()
|
||||
{
|
||||
return m_debug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a search slot
|
||||
*
|
||||
* @param search SearchContext
|
||||
* @return int
|
||||
*/
|
||||
public final int allocateSearchId(SearchContext search)
|
||||
{
|
||||
|
||||
synchronized (m_searches)
|
||||
{
|
||||
|
||||
// Search for a free slot in the search list
|
||||
|
||||
int cnt = 0;
|
||||
|
||||
while (cnt < MaximumSearches)
|
||||
{
|
||||
|
||||
// Check if the index has wrapped
|
||||
|
||||
if (m_lastIdx >= MaximumSearches)
|
||||
m_lastIdx = 0;
|
||||
|
||||
// Check if the current slot is empty
|
||||
|
||||
if (m_searches[m_lastIdx] == null)
|
||||
{
|
||||
|
||||
// Use this slot
|
||||
|
||||
SearchEntry entry = new SearchEntry(search);
|
||||
m_searches[m_lastIdx] = entry;
|
||||
return m_lastIdx++;
|
||||
} else
|
||||
m_lastIdx++;
|
||||
|
||||
// Update the slot count
|
||||
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
// No empty search slot found
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release a search slot
|
||||
*
|
||||
* @param id int
|
||||
*/
|
||||
public final void releaseSearchId(int id)
|
||||
{
|
||||
|
||||
// Range check the id
|
||||
|
||||
if (id < 0 || id >= MaximumSearches)
|
||||
return;
|
||||
|
||||
// Delete the search entry
|
||||
|
||||
synchronized (m_searches)
|
||||
{
|
||||
m_searches[id] = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the required search context
|
||||
*
|
||||
* @param id int
|
||||
* @return SearchContext
|
||||
*/
|
||||
public final SearchContext getSearch(int id)
|
||||
{
|
||||
|
||||
// Range check the id
|
||||
|
||||
if (id < 0 || id >= MaximumSearches)
|
||||
return null;
|
||||
|
||||
// Get the search entry
|
||||
|
||||
SearchEntry entry = null;
|
||||
|
||||
synchronized (m_searches)
|
||||
{
|
||||
entry = m_searches[id];
|
||||
}
|
||||
|
||||
// Return the search context, if valid
|
||||
|
||||
if (entry != null)
|
||||
{
|
||||
|
||||
// Update the search timeout and return the search
|
||||
|
||||
entry.updateTimeout();
|
||||
return entry.getSearch();
|
||||
}
|
||||
|
||||
// Invalid search
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the active search list
|
||||
*/
|
||||
public final void dumpSearches()
|
||||
{
|
||||
|
||||
synchronized (m_searches)
|
||||
{
|
||||
|
||||
// Find all active searches in the list
|
||||
|
||||
for (int i = 0; i < m_searches.length; i++)
|
||||
{
|
||||
|
||||
// Check if the current search slot is active
|
||||
|
||||
if (m_searches[i] != null)
|
||||
{
|
||||
|
||||
// Get the search details
|
||||
|
||||
SearchEntry entry = m_searches[i];
|
||||
|
||||
logger.debug("" + i + ": " + entry.getSearch().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc.nfs;
|
||||
|
||||
/**
|
||||
* Share Details Class
|
||||
*
|
||||
* <p>Contains the file id cache, active search cache and tree connection details
|
||||
* of a shared filesystem.
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class ShareDetails {
|
||||
|
||||
// Share name
|
||||
|
||||
private String m_name;
|
||||
|
||||
// File id to path conversion cache
|
||||
|
||||
private FileIdCache m_idCache;
|
||||
|
||||
// Flag to indicate if the filesystem driver for this share supports file id
|
||||
// lookups
|
||||
// via the FileIdInterface
|
||||
|
||||
private boolean m_fileIdLookup;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param name String
|
||||
* @param fileIdSupport boolean
|
||||
*/
|
||||
public ShareDetails(String name, boolean fileIdSupport)
|
||||
{
|
||||
|
||||
// Save the share name
|
||||
|
||||
m_name = name;
|
||||
|
||||
// Set the file id support flag
|
||||
|
||||
m_fileIdLookup = fileIdSupport;
|
||||
|
||||
// Create the file id and search caches
|
||||
|
||||
m_idCache = new FileIdCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the share name
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public final String getName()
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the file id cache
|
||||
*
|
||||
* @return FileIdCache
|
||||
*/
|
||||
public final FileIdCache getFileIdCache()
|
||||
{
|
||||
return m_idCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the filesystem driver for this share has file id support
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public final boolean hasFileIdSupport()
|
||||
{
|
||||
return m_fileIdLookup;
|
||||
}
|
||||
}
|
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc.nfs;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Share Details Hash Class
|
||||
*
|
||||
* <p>Hashtable of ShareDetails for the available disk shared devices. ShareDetails are indexed using the
|
||||
* hash of the share name to allow mounts to be persistent across server restarts.
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class ShareDetailsHash {
|
||||
|
||||
// Share name hash to share details
|
||||
|
||||
private Hashtable<Integer, ShareDetails> m_details;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public ShareDetailsHash()
|
||||
{
|
||||
m_details = new Hashtable<Integer, ShareDetails>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add share details to the list of available shares
|
||||
*
|
||||
* @param details ShareDetails
|
||||
*/
|
||||
public final void addDetails(ShareDetails details)
|
||||
{
|
||||
m_details.put(new Integer(details.getName().hashCode()), details);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete share details from the list
|
||||
*
|
||||
* @param shareName String
|
||||
* @return ShareDetails
|
||||
*/
|
||||
public final ShareDetails deleteDetails(String shareName)
|
||||
{
|
||||
return (ShareDetails) m_details.get(new Integer(shareName.hashCode()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find share details for the specified share name
|
||||
*
|
||||
* @param shareName String
|
||||
* @return ShareDetails
|
||||
*/
|
||||
public final ShareDetails findDetails(String shareName)
|
||||
{
|
||||
|
||||
// Get the share details for the associated share name
|
||||
|
||||
ShareDetails details = (ShareDetails) m_details.get(new Integer(shareName.hashCode()));
|
||||
|
||||
// Return the share details
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find share details for the specified share name hash code
|
||||
*
|
||||
* @param hashCode int
|
||||
* @return ShareDetails
|
||||
*/
|
||||
public final ShareDetails findDetails(int hashCode)
|
||||
{
|
||||
|
||||
// Get the share details for the associated share name
|
||||
|
||||
ShareDetails details = (ShareDetails) m_details.get(new Integer(hashCode));
|
||||
|
||||
// Return the share details
|
||||
|
||||
return details;
|
||||
}
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc.nfs;
|
||||
|
||||
/**
|
||||
* Stale Handle Exception Class
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class StaleHandleException extends Exception {
|
||||
|
||||
// Object version id
|
||||
|
||||
private static final long serialVersionUID = -8607694363687774475L;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public StaleHandleException()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param msg String
|
||||
*/
|
||||
public StaleHandleException(String msg)
|
||||
{
|
||||
super(msg);
|
||||
}
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc.portmap;
|
||||
|
||||
/**
|
||||
* PortMapper RPC Service Constants Class
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class PortMapper {
|
||||
|
||||
// Default port mapper port
|
||||
|
||||
public static final int DefaultPort = 111;
|
||||
|
||||
// Program and version id
|
||||
|
||||
public static final int ProgramId = 100000;
|
||||
public static final int VersionId = 2;
|
||||
|
||||
// RPC procedure ids
|
||||
|
||||
public static final int ProcNull = 0;
|
||||
public static final int ProcSet = 1;
|
||||
public static final int ProcUnSet = 2;
|
||||
public static final int ProcGetPort = 3;
|
||||
public static final int ProcDump = 4;
|
||||
public static final int ProcMax = 4;
|
||||
|
||||
// RPC procedure names
|
||||
|
||||
private static final String[] _procNames = { "Null", "Set", "UnSet",
|
||||
"GetPort", "Dump" };
|
||||
|
||||
/**
|
||||
* Return a procedure id as a name
|
||||
*
|
||||
* @param id
|
||||
* int
|
||||
* @return String
|
||||
*/
|
||||
public final static String getProcedureName(int id) {
|
||||
if (id < 0 || id > ProcMax)
|
||||
return null;
|
||||
return _procNames[id];
|
||||
}
|
||||
}
|
@@ -0,0 +1,551 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.server.oncrpc.portmap;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import org.alfresco.filesys.server.NetworkServer;
|
||||
import org.alfresco.filesys.server.ServerListener;
|
||||
import org.alfresco.filesys.server.config.ServerConfiguration;
|
||||
import org.alfresco.filesys.server.oncrpc.PortMapping;
|
||||
import org.alfresco.filesys.server.oncrpc.Rpc;
|
||||
import org.alfresco.filesys.server.oncrpc.RpcPacket;
|
||||
import org.alfresco.filesys.server.oncrpc.RpcProcessor;
|
||||
import org.alfresco.filesys.server.oncrpc.TcpRpcSessionHandler;
|
||||
import org.alfresco.filesys.server.oncrpc.UdpRpcDatagramHandler;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Port Mapper Server Class
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class PortMapperServer extends NetworkServer implements RpcProcessor {
|
||||
|
||||
// Debug logging
|
||||
|
||||
protected static final Log logger = LogFactory.getLog("org.alfresco.nfs.protocol");
|
||||
|
||||
// Constants
|
||||
//
|
||||
// Default port mapper port
|
||||
|
||||
public final static int DefaultPort = 111;
|
||||
|
||||
// Maximum request size to accept
|
||||
|
||||
public final static int MaxRequestSize = 1024;
|
||||
|
||||
// Incoming datagram handler for UDP requests
|
||||
|
||||
private UdpRpcDatagramHandler m_udpHandler;
|
||||
|
||||
// Incoming session handler for TCP requests
|
||||
|
||||
private TcpRpcSessionHandler m_tcpHandler;
|
||||
|
||||
// Portmapper port
|
||||
|
||||
private int m_port;
|
||||
|
||||
// Table of active port mappings
|
||||
|
||||
private Hashtable<Integer, PortMapping> m_mappings;
|
||||
private Hashtable<Integer, PortMapping> m_noVerMappings;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param config
|
||||
* ServerConfiguration
|
||||
*/
|
||||
public PortMapperServer(ServerConfiguration config) {
|
||||
super("Portmap", config);
|
||||
|
||||
// Enable/disable debug output
|
||||
|
||||
setDebug(config.hasPortMapperDebug());
|
||||
|
||||
// Set the port to use
|
||||
|
||||
if (config.getPortMapperPort() != 0)
|
||||
setPort(config.getPortMapperPort());
|
||||
else
|
||||
setPort(DefaultPort);
|
||||
|
||||
// Create the mappings tables
|
||||
|
||||
m_mappings = new Hashtable<Integer, PortMapping>();
|
||||
m_noVerMappings = new Hashtable<Integer, PortMapping>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the server port
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getPort() {
|
||||
return m_port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the portmapper server
|
||||
*/
|
||||
public void startServer() {
|
||||
|
||||
try {
|
||||
|
||||
// Create the UDP RPC handler to accept incoming requests
|
||||
|
||||
m_udpHandler = new UdpRpcDatagramHandler("PortMap", "Port", this, this, null, getPort(), MaxRequestSize);
|
||||
m_udpHandler.initializeSessionHandler(this);
|
||||
|
||||
// Start the UDP request listener is a seperate thread
|
||||
|
||||
Thread udpThread = new Thread(m_udpHandler);
|
||||
udpThread.setName("PortMap_UDP");
|
||||
udpThread.start();
|
||||
|
||||
// Create the TCP RPC handler to accept incoming requests
|
||||
|
||||
m_tcpHandler = new TcpRpcSessionHandler("PortMap", "Port", this, this, null, getPort(), MaxRequestSize);
|
||||
m_tcpHandler.initializeSessionHandler(this);
|
||||
|
||||
// Start the UDP request listener is a seperate thread
|
||||
|
||||
Thread tcpThread = new Thread(m_tcpHandler);
|
||||
tcpThread.setName("PortMap_TCP");
|
||||
tcpThread.start();
|
||||
|
||||
// Add port mapper entries for the portmapper service
|
||||
|
||||
PortMapping portMap = new PortMapping(PortMapper.ProgramId, PortMapper.VersionId, Rpc.UDP, getPort());
|
||||
addPortMapping(portMap);
|
||||
|
||||
portMap = new PortMapping(PortMapper.ProgramId, PortMapper.VersionId, Rpc.TCP, getPort());
|
||||
addPortMapping(portMap);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.debug(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown the server
|
||||
*
|
||||
* @param immediate
|
||||
* boolean
|
||||
*/
|
||||
public void shutdownServer(boolean immediate) {
|
||||
|
||||
// Stop the RPC handlers
|
||||
|
||||
if (m_udpHandler != null) {
|
||||
m_udpHandler.closeSessionHandler(this);
|
||||
m_udpHandler = null;
|
||||
}
|
||||
|
||||
if (m_tcpHandler != null) {
|
||||
m_tcpHandler.closeSessionHandler(this);
|
||||
m_tcpHandler = null;
|
||||
}
|
||||
|
||||
// Fire a shutdown notification event
|
||||
|
||||
fireServerEvent(ServerListener.ServerShutdown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the server port
|
||||
*
|
||||
* @param port
|
||||
* int
|
||||
*/
|
||||
public final void setPort(int port) {
|
||||
m_port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an RPC request
|
||||
*
|
||||
* @param rpc
|
||||
* RpcPacket
|
||||
* @return RpcPacket
|
||||
* @throws IOException
|
||||
*/
|
||||
public RpcPacket processRpc(RpcPacket rpc) throws IOException {
|
||||
|
||||
// Validate the request
|
||||
|
||||
if (rpc.getProgramId() != PortMapper.ProgramId) {
|
||||
|
||||
// Request is not for us
|
||||
|
||||
rpc.buildAcceptErrorResponse(Rpc.StsProgUnavail);
|
||||
return rpc;
|
||||
} else if (rpc.getProgramVersion() != PortMapper.VersionId) {
|
||||
|
||||
// Request is not for this version of portmapper
|
||||
|
||||
rpc.buildProgramMismatchResponse(PortMapper.VersionId,
|
||||
PortMapper.VersionId);
|
||||
return rpc;
|
||||
}
|
||||
|
||||
// Position the RPC buffer pointer at the start of the call parameters
|
||||
|
||||
rpc.positionAtParameters();
|
||||
|
||||
// Process the RPC request
|
||||
|
||||
RpcPacket response = null;
|
||||
|
||||
switch (rpc.getProcedureId()) {
|
||||
|
||||
// Null request
|
||||
|
||||
case PortMapper.ProcNull:
|
||||
response = procNull(rpc);
|
||||
break;
|
||||
|
||||
// Set a port
|
||||
|
||||
case PortMapper.ProcSet:
|
||||
response = procSet(rpc);
|
||||
break;
|
||||
|
||||
// Release a port
|
||||
|
||||
case PortMapper.ProcUnSet:
|
||||
response = procUnSet(rpc);
|
||||
break;
|
||||
|
||||
// Get the port for a service
|
||||
|
||||
case PortMapper.ProcGetPort:
|
||||
response = procGetPort(rpc);
|
||||
break;
|
||||
|
||||
// Dump ports request
|
||||
|
||||
case PortMapper.ProcDump:
|
||||
response = procDump(rpc);
|
||||
break;
|
||||
}
|
||||
|
||||
// Return the RPC response
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the null request
|
||||
*
|
||||
* @param rpc
|
||||
* RpcPacket
|
||||
* @return RpcPacket
|
||||
*/
|
||||
private final RpcPacket procNull(RpcPacket rpc) {
|
||||
|
||||
// Build the response
|
||||
|
||||
rpc.buildResponseHeader();
|
||||
return rpc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the set request
|
||||
*
|
||||
* @param rpc
|
||||
* RpcPacket
|
||||
* @return RpcPacket
|
||||
*/
|
||||
private final RpcPacket procSet(RpcPacket rpc) {
|
||||
|
||||
// Get the call parameters
|
||||
|
||||
int progId = rpc.unpackInt();
|
||||
int verId = rpc.unpackInt();
|
||||
int proto = rpc.unpackInt();
|
||||
int port = rpc.unpackInt();
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("[PortMap] Set port program=" + Rpc.getServiceName(progId) + ", version=" + verId
|
||||
+ ", protocol=" + (proto == Rpc.TCP ? "TCP" : "UDP") + ", port=" + port);
|
||||
|
||||
// Check if the port is already mapped
|
||||
|
||||
PortMapping portMap = findPortMapping(progId, verId, proto);
|
||||
int portAdded = Rpc.False;
|
||||
|
||||
if (portMap == null) {
|
||||
|
||||
// Add a mapping for the new service
|
||||
|
||||
portMap = new PortMapping(progId, verId, proto, port);
|
||||
if (addPortMapping(portMap) == true)
|
||||
portAdded = Rpc.True;
|
||||
}
|
||||
|
||||
// Check if the service is on the same port as the current port mapping,
|
||||
// and it is not
|
||||
// an attempt to set the port mapper service port.
|
||||
|
||||
else if (progId != PortMapper.ProgramId && portMap.getPort() == port) {
|
||||
|
||||
// Settings are the same as the existing service settings so accept
|
||||
// it
|
||||
|
||||
portAdded = Rpc.True;
|
||||
}
|
||||
|
||||
// Build the response header
|
||||
|
||||
rpc.buildResponseHeader();
|
||||
|
||||
// Pack a boolean indicating if the port was added, or not
|
||||
|
||||
rpc.packInt(portAdded);
|
||||
rpc.setLength();
|
||||
|
||||
// Return the response
|
||||
|
||||
return rpc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the unset request
|
||||
*
|
||||
* @param rpc
|
||||
* RpcPacket
|
||||
* @return RpcPacket
|
||||
*/
|
||||
private final RpcPacket procUnSet(RpcPacket rpc) {
|
||||
|
||||
// Get the call parameters
|
||||
|
||||
int progId = rpc.unpackInt();
|
||||
int verId = rpc.unpackInt();
|
||||
int proto = rpc.unpackInt();
|
||||
int port = rpc.unpackInt();
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("[PortMap] UnSet port program=" + Rpc.getServiceName(progId) + ", version=" + verId
|
||||
+ ", protocol=" + (proto == Rpc.TCP ? "TCP" : "UDP") + ", port=" + port);
|
||||
|
||||
// Check if the port is mapped, and it is not an attempt to remove a
|
||||
// portmapper portt
|
||||
|
||||
PortMapping portMap = findPortMapping(progId, verId, proto);
|
||||
int portRemoved = Rpc.False;
|
||||
|
||||
if (portMap != null && progId != PortMapper.ProgramId) {
|
||||
|
||||
// Remove the port mapping
|
||||
|
||||
if (removePortMapping(portMap) == true)
|
||||
portRemoved = Rpc.True;
|
||||
}
|
||||
|
||||
// Build the response header
|
||||
|
||||
rpc.buildResponseHeader();
|
||||
|
||||
// Pack a boolean indicating if the port was removed, or not
|
||||
|
||||
rpc.packInt(portRemoved);
|
||||
rpc.setLength();
|
||||
|
||||
// Return the response
|
||||
|
||||
return rpc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the get port request
|
||||
*
|
||||
* @param rpc
|
||||
* RpcPacket
|
||||
* @return RpcPacket
|
||||
*/
|
||||
private final RpcPacket procGetPort(RpcPacket rpc) {
|
||||
|
||||
// Get the call parameters
|
||||
|
||||
int progId = rpc.unpackInt();
|
||||
int verId = rpc.unpackInt();
|
||||
int proto = rpc.unpackInt();
|
||||
|
||||
// Find the required port mapping
|
||||
|
||||
PortMapping portMap = findPortMapping(progId, verId, proto);
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("[PortMap] Get port program=" + Rpc.getServiceName(progId) + ", version=" + verId
|
||||
+ ", protocol=" + (proto == Rpc.TCP ? "TCP" : "UDP") + ", port=" + (portMap != null ? portMap.getPort() : 0));
|
||||
|
||||
// Build the response header
|
||||
|
||||
rpc.buildResponseHeader();
|
||||
|
||||
// Pack the port number of the requested RPC service, or zero if not
|
||||
// found
|
||||
|
||||
rpc.packInt(portMap != null ? portMap.getPort() : 0);
|
||||
rpc.setLength();
|
||||
|
||||
// Return the response
|
||||
|
||||
return rpc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the dump request
|
||||
*
|
||||
* @param rpc
|
||||
* RpcPacket
|
||||
* @return RpcPacket
|
||||
*/
|
||||
private final RpcPacket procDump(RpcPacket rpc) {
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("[PortMap] Dump ports request from " + rpc.getClientDetails());
|
||||
|
||||
// Build the response
|
||||
|
||||
rpc.buildResponseHeader();
|
||||
|
||||
// Pack the active port mappings structures
|
||||
|
||||
Enumeration enm = m_mappings.elements();
|
||||
|
||||
while (enm.hasMoreElements()) {
|
||||
|
||||
// Get the current port mapping
|
||||
|
||||
PortMapping portMap = (PortMapping) enm.nextElement();
|
||||
|
||||
// Pack the port mapping structure
|
||||
|
||||
rpc.packInt(Rpc.True);
|
||||
rpc.packPortMapping(portMap);
|
||||
}
|
||||
|
||||
// Pack the end of list structure, set the response length
|
||||
|
||||
rpc.packInt(Rpc.False);
|
||||
rpc.setLength();
|
||||
|
||||
// Return the response
|
||||
|
||||
return rpc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a port mapping to the active list
|
||||
*
|
||||
* @param portMap
|
||||
* PortMapping
|
||||
* @return boolean
|
||||
*/
|
||||
private final boolean addPortMapping(PortMapping portMap) {
|
||||
|
||||
// Check if there is an existing port mapping that matches the new port
|
||||
|
||||
Integer key = new Integer(portMap.hashCode());
|
||||
if (m_mappings.get(key) != null)
|
||||
return false;
|
||||
|
||||
// Add the port mapping
|
||||
|
||||
m_mappings.put(key, portMap);
|
||||
|
||||
// Add a port mapping with a version id of zero
|
||||
|
||||
key = new Integer(PortMapping.generateHashCode(portMap.getProgramId(), 0, portMap.getProtocol()));
|
||||
m_noVerMappings.put(key, portMap);
|
||||
|
||||
// Indicate that the mapping was added
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a port mapping from the active list
|
||||
*
|
||||
* @param portMap
|
||||
* PortMapping
|
||||
* @return boolean
|
||||
*/
|
||||
private final boolean removePortMapping(PortMapping portMap) {
|
||||
|
||||
// Remove the port mapping from the active lists
|
||||
|
||||
Integer key = new Integer(portMap.hashCode());
|
||||
Object removedObj = m_mappings.remove(key);
|
||||
|
||||
key = new Integer(PortMapping.generateHashCode(portMap.getProgramId(), 0, portMap.getProtocol()));
|
||||
m_noVerMappings.remove(key);
|
||||
|
||||
// Return a status indicating if the mapping was removed
|
||||
|
||||
return removedObj != null ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for a port mapping
|
||||
*
|
||||
* @param progId
|
||||
* int
|
||||
* @param verId
|
||||
* int
|
||||
* @param proto
|
||||
* int
|
||||
* @return PortMapping
|
||||
*/
|
||||
private final PortMapping findPortMapping(int progId, int verId, int proto) {
|
||||
|
||||
// Create a key for the RPC service
|
||||
|
||||
Integer key = new Integer(PortMapping.generateHashCode(progId, verId,
|
||||
proto));
|
||||
|
||||
// Search for the required port mapping, including the version id
|
||||
|
||||
PortMapping portMap = (PortMapping) m_mappings.get(key);
|
||||
if (portMap == null && verId == 0) {
|
||||
|
||||
// Search for the port mapping without the version id
|
||||
|
||||
portMap = (PortMapping) m_noVerMappings.get(key);
|
||||
}
|
||||
|
||||
// Return the port mapping, or null if not found
|
||||
|
||||
return portMap;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user