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:
Gary Spencer
2007-01-05 15:07:18 +00:00
parent 91ad3dde7d
commit 225f69de01
53 changed files with 16444 additions and 46 deletions

View File

@@ -0,0 +1,280 @@
/*
* Copyright (C) 2006 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;
import java.io.IOException;
import java.io.PrintStream;
import java.net.SocketException;
import java.util.Vector;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.filesys.server.NetworkServer;
import org.alfresco.filesys.server.config.ServerConfiguration;
import org.alfresco.filesys.server.oncrpc.mount.MountServer;
import org.alfresco.filesys.server.oncrpc.portmap.PortMapperServer;
import org.alfresco.util.AbstractLifecycleBean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* NFS Server Class
*
* <p>Create and start the various server components required to run the NFS server.
*
* @author GKSpencer
*/
public class NFSServer extends AbstractLifecycleBean
{
// Debug logging
private static final Log logger = LogFactory.getLog("org.alfresco.nfs.server");
// Server configuration
private ServerConfiguration m_filesysConfig;
// List of NFS server components
private Vector<NetworkServer> m_serverList = new Vector<NetworkServer>();
/**
* Class constructor
*
* @param serverConfig ServerConfiguration
*/
public NFSServer(ServerConfiguration serverConfig)
{
m_filesysConfig = serverConfig;
}
/**
* Return the server configuration
*
* @return ServerConfiguration
*/
public final ServerConfiguration getConfiguration()
{
return m_filesysConfig;
}
/**
* Check if the server is started/enabled
*
* @return Returns true if the server started up without any errors
*/
public boolean isStarted()
{
return (m_filesysConfig != null && m_filesysConfig.isNFSServerEnabled());
}
/**
* Start the NFS server components
*
* @exception SocketException If a network error occurs
* @exception IOException If an I/O error occurs
*/
public final void startServer() throws SocketException, IOException
{
try
{
// Create the NFS, mount and portmapper servers, if enabled
if (m_filesysConfig.isNFSServerEnabled())
{
// Create the portmapper server, if enabled
if (m_filesysConfig.hasNFSPortMapper())
m_serverList.add(new PortMapperServer(m_filesysConfig));
// Create the mount and main NFS servers
m_serverList.add(new MountServer(m_filesysConfig));
m_serverList.add(new org.alfresco.filesys.server.oncrpc.nfs.NFSServer(m_filesysConfig));
// Add the servers to the configuration
for (NetworkServer server : m_serverList)
{
m_filesysConfig.addServer(server);
}
}
// Start the server(s)
for (NetworkServer server : m_serverList)
{
if (logger.isInfoEnabled())
logger.info("Starting server " + server.getProtocolName() + " ...");
// Start the server
server.startServer();
}
}
catch (Throwable e)
{
m_filesysConfig = null;
throw new AlfrescoRuntimeException("Failed to start NFS Server", e);
}
}
/**
* Stop the NFS server components
*/
public final void stopServer()
{
if (m_filesysConfig == null)
{
// initialisation failed
return;
}
// Shutdown the NFS server components, in reverse order
for ( int i = m_serverList.size() - 1; i >= 0; i--)
{
// Get the current server from the list
NetworkServer server = m_serverList.get( i);
if (logger.isInfoEnabled())
logger.info("Shutting server " + server.getProtocolName() + " ...");
// Stop the server
server.shutdownServer(false);
// Remove the server from the global list
getConfiguration().removeServer(server.getProtocolName());
}
// Clear the server list and configuration
m_serverList.clear();
m_filesysConfig = null;
}
/**
* Runs the NFS server directly
*
* @param args String[]
*/
public static void main(String[] args)
{
PrintStream out = System.out;
out.println("NFS Server Test");
out.println("----------------");
try
{
// Create the configuration service in the same way that Spring creates it
ApplicationContext ctx = new ClassPathXmlApplicationContext("alfresco/application-context.xml");
// Get the NFS server bean
NFSServer server = (NFSServer) ctx.getBean("nfsServer");
if (server == null)
{
throw new AlfrescoRuntimeException("Server bean 'nfsServer' not defined");
}
// Stop the FTP server, if running
server.getConfiguration().setFTPServerEnabled(false);
NetworkServer srv = server.getConfiguration().findServer("FTP");
if ( srv != null)
srv.shutdownServer(true);
// Stop the CIFS server, if running
server.getConfiguration().setSMBServerEnabled(false);
srv = server.getConfiguration().findServer("SMB");
if ( srv != null)
srv.shutdownServer(true);
// Only wait for shutdown if the NFS server is enabled
if ( server.getConfiguration().isNFSServerEnabled())
{
// NFS server should have automatically started
// Wait for shutdown via the console
out.println("Enter 'x' to shutdown ...");
boolean shutdown = false;
// Wait while the server runs, user may stop the server by typing a key
while (shutdown == false)
{
// Wait for the user to enter the shutdown key
int ch = System.in.read();
if (ch == 'x' || ch == 'X')
shutdown = true;
synchronized (server)
{
server.wait(20);
}
}
// Stop the server
server.stopServer();
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
System.exit(1);
}
@Override
protected void onBootstrap(ApplicationEvent event)
{
try
{
startServer();
}
catch (SocketException e)
{
throw new AlfrescoRuntimeException("Failed to start NFS server", e);
}
catch (IOException e)
{
throw new AlfrescoRuntimeException("Failed to start NFS server", e);
}
}
@Override
protected void onShutdown(ApplicationEvent event)
{
stopServer();
}
}

View File

@@ -0,0 +1,414 @@
/*
* 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;
import java.io.*;
import java.net.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Datagram Session Handler Class
*
* <p>Implementation of a session handler that uses a Java datagram socket to listen for incoming requests.
*
* @author GKSpencer
*/
public abstract class DatagramSessionHandler implements SessionHandlerInterface, Runnable {
// Debug logging
protected static final Log logger = LogFactory.getLog(DatagramSessionHandler.class);
// Server that the handler is associated with
private NetworkServer m_server;
// Address/port to use
private int m_port;
private InetAddress m_bindAddr;
// Datagram socket to listen for incoming requests
private DatagramSocket m_srvSock;
// Maximum datagram size
private int m_maxDgramSize;
// Session id
private int m_sessId;
// Session handler name, protocol name
private String m_name;
private String m_protocol;
// Shutdown request flag
private boolean m_shutdown;
/**
* Class constructor
*
* @param name String
* @param protocol String
* @param server NetworkServer
* @param addr InetAddress
* @param port int
*/
protected DatagramSessionHandler(String name, String protocol, NetworkServer server, InetAddress addr, int port)
{
m_name = name;
m_protocol = protocol;
m_server = server;
m_bindAddr = addr;
m_port = port;
}
/**
* Return the maximum datagram size allowed
*
* @return int
*/
public final int getMaximumDatagramSize()
{
return m_maxDgramSize;
}
/**
* Return the session handler name
*
* @return String
*/
public final String getHandlerName()
{
return m_name;
}
/**
* Return the short protocol name
*
* @return String
*/
public final String getProtocolName()
{
return m_protocol;
}
/**
* Check if the server should bind to a specific network address
*
* @return boolean
*/
public final boolean hasBindAddress()
{
return m_bindAddr != null ? true : false;
}
/**
* Return the network address that the server should bind to
*
* @return InetAddress
*/
public final InetAddress getBindAddres()
{
return m_bindAddr;
}
/**
* Return the port that the server should bind to
*
* @return int
*/
public final int getPort()
{
return m_port;
}
/**
* Clear the shutdown flag
*/
protected final void clearShutdown()
{
m_shutdown = false;
}
/**
* Determine if the shutdown flag has been set
*
* @return boolean
*/
protected final boolean hasShutdown()
{
return m_shutdown;
}
/**
* Get the next available session id
*
* @return int
*/
protected synchronized int getNextSessionId()
{
return m_sessId++;
}
/**
* Set the local port that the datagram handler is using
*
* @param port int
*/
protected final void setPort(int port)
{
m_port = port;
}
/**
* Return the datagrma socket
*
* @return DatagramSocket
*/
protected final DatagramSocket getDatagramSocket()
{
return m_srvSock;
}
/**
* Initialize the session handler
*
* @param server NetworkServer
*/
public void initializeSessionHandler(NetworkServer server)
throws IOException
{
// Open the server socket
if (hasBindAddress())
m_srvSock = new DatagramSocket(getPort(), getBindAddres());
else
m_srvSock = new DatagramSocket(getPort());
// Set the datagram receive buffer size
if (m_srvSock.getReceiveBufferSize() < getMaximumDatagramSize())
m_srvSock.setReceiveBufferSize(getMaximumDatagramSize());
// Set the allocated port
if (getPort() == 0)
setPort(m_srvSock.getLocalPort());
// DEBUG
if (logger.isDebugEnabled())
{
String bindAddr = hasBindAddress() ? getBindAddres().getHostAddress() : "ALL";
logger.debug("[" + getProtocolName() + "] Binding " + getHandlerName() + " session handler to address : " + bindAddr);
}
}
/**
* Close the session handler
*
* @param server NetworkServer
*/
public void closeSessionHandler(NetworkServer server)
{
// Request the main listener thread shutdown
m_shutdown = true;
// Close the server socket to release any pending listen
if (m_srvSock != null)
m_srvSock.close();
}
/**
* Set the maximum datagram size
*
* @param maxSize int
*/
protected final void setMaximumDatagramSize(int maxSize)
{
m_maxDgramSize = maxSize;
}
/**
* Process a received datagram packet
*
* @param pkt DatagramPacket
* @return boolean Return true to reuse the DatagramPacket, else false to allocate a new packet
* @exception IOException
*/
protected abstract boolean processDatagram(DatagramPacket pkt)
throws IOException;
/**
* Allocate a buffer for the datagram receive
*
* @param bufSize int
* @return byte[]
*/
protected byte[] allocateBuffer(int bufSize)
{
// Allocate a buffer for the datagram
return new byte[bufSize];
}
/**
* Send a datagram
*
* @param pkt DatagramPacket
* @exception IOException
*/
protected void sendDatagram(DatagramPacket pkt)
throws IOException
{
// Check if the datagram socket is valid
if (m_srvSock == null)
throw new IOException("Datagram socket is null");
// Default implementation sends the datagram immediately via the datagram socket
m_srvSock.send(pkt);
}
/**
* Socket listener thread
*/
public void run()
{
try
{
// Set the thread name
Thread.currentThread().setName(getProtocolName() + "_" + getHandlerName());
// Clear the shutdown flag
clearShutdown();
// Debug
if (logger.isDebugEnabled())
logger.debug("[" + getProtocolName() + "] Waiting for datagrams ...");
// Wait for incoming connection requests
byte[] buf = null;
DatagramPacket pkt = null;
boolean reusePkt = false;
while (hasShutdown() == false)
{
// Allocate the datagram buffer and packet
if (reusePkt == false)
{
// Allocate a new datagram packet and buffer
buf = allocateBuffer(getMaximumDatagramSize());
if (pkt == null)
{
// Allocate the datagram packet
pkt = new DatagramPacket(buf, buf.length);
} else
{
// Re-use the existing datagram packet
pkt.setData(buf, 0, buf.length);
}
} else
{
// Re-use the existing datagram packet and buffer.
//
// Reset to use our buffer as the datagram packet may have been reused to send a response.
pkt.setData(buf, 0, buf.length);
}
// Wait for an incoming datagram
m_srvSock.receive(pkt);
try
{
// Process the datagram packet
reusePkt = processDatagram(pkt);
} catch (Exception ex)
{
// Debug
if (logger.isDebugEnabled())
logger.debug("[" + getProtocolName() + "] Error processing datagram, " + ex.toString());
}
}
} catch (SocketException ex)
{
// Do not report an error if the server has shutdown, closing the server socket
// causes an exception to be thrown.
if (hasShutdown() == false)
{
logger.debug("[" + getProtocolName() + "] Socket error : " + ex.toString());
logger.debug(ex);
}
} catch (Exception ex)
{
// Do not report an error if the server has shutdown, closing the server socket
// causes an exception to be thrown.
if (hasShutdown() == false)
{
logger.debug("[" + getProtocolName() + "] Server error : " + ex.toString());
logger.debug(ex);
}
}
// Debug
if (logger.isDebugEnabled())
logger.debug("[" + getProtocolName() + "] " + getHandlerName() + " session handler closed");
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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;
import java.io.*;
/**
* Packet Handler Interface
*
* <p>Implemented by classes that read/write request packets to a network connection.
*
* @author GKSpencer
*/
public interface PacketHandlerInterface {
/**
* Return the protocol name
*
* @return String
*/
public String getProtocolName();
/**
* Return the number of bytes available for reading without blocking
*
* @return int
* @exception IOException
*/
public int availableBytes()
throws IOException;
/**
* Read a packet of data
*
* @param pkt byte[]
* @param offset int
* @param maxLen int
* @return int
* @exception IOException
*/
public int readPacket(byte[] pkt, int offset, int maxLen)
throws IOException;
/**
* Write a packet of data
*
* @param pkt byte[]
* @param offset int
* @param len int
* @exception IOException
*/
public void writePacket(byte[] pkt, int offset, int len)
throws IOException;
/**
* Close the packet handler
*/
public void closePacketHandler();
}

View File

@@ -0,0 +1,98 @@
/*
* 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;
import java.util.*;
/**
* Packet Handler List Class
*
* @author GKSpencer
*/
public class PacketHandlerList {
// List of session handlers
private Vector<PacketHandlerInterface> m_handlers;
/**
* Default constructor
*/
public PacketHandlerList() {
m_handlers = new Vector<PacketHandlerInterface>();
}
/**
* Add a handler to the list
*
* @param handler PacketHandlerInterface
*/
public final void addHandler(PacketHandlerInterface handler) {
m_handlers.addElement(handler);
}
/**
* Return the number of handlers in the list
*
* @return int
*/
public final int numberOfHandlers() {
return m_handlers.size();
}
/**
* Return the specified handler
*
* @param idx int
* @return PacketHandlerInterface
*/
public final PacketHandlerInterface getHandlerAt(int idx) {
// Range check the index
if (idx < 0 || idx >= m_handlers.size())
return null;
return (PacketHandlerInterface) m_handlers.elementAt(idx);
}
/**
* Remove a handler from the list
*
* @param idx int
* @return PacketHandlerInterface
*/
public final PacketHandlerInterface remoteHandler(int idx) {
// Range check the index
if (idx < 0 || idx >= m_handlers.size())
return null;
// Remove the handler, and return it
PacketHandlerInterface handler = (PacketHandlerInterface) m_handlers.elementAt(idx);
m_handlers.removeElementAt(idx);
return handler;
}
/**
* Remove all handlers from the list
*/
public final void removeAllHandlers() {
m_handlers.removeAllElements();
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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;
import java.io.*;
/**
* Session Handler Interface
*
* <p>Implemented by classes that wait for an incoming session request.
*
* @author GKSpencer
*/
public interface SessionHandlerInterface
{
/**
* Return the protocol name
*
* @return String
*/
public String getHandlerName();
/**
* Initialize the session handler
*
* @param server
* NetworkServer
* @exception IOException
*/
public void initializeSessionHandler(NetworkServer server)
throws IOException;
/**
* Close the session handler
*/
public void closeSessionHandler(NetworkServer server);
}

View File

@@ -0,0 +1,164 @@
/*
* 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;
import java.util.*;
/**
* Session Handler List Class
*
* @author GKSpencer
*/
public class SessionHandlerList {
// List of session handlers
private Vector<SessionHandlerInterface> m_handlers;
/**
* Default constructor
*/
public SessionHandlerList()
{
m_handlers = new Vector<SessionHandlerInterface>();
}
/**
* Add a handler to the list
*
* @param handler SessionHandlerInterface
*/
public final void addHandler(SessionHandlerInterface handler)
{
m_handlers.addElement(handler);
}
/**
* Return the number of handlers in the list
*
* @return int
*/
public final int numberOfHandlers()
{
return m_handlers.size();
}
/**
* Return the specified handler
*
* @param idx int
* @return SessionHandlerInterface
*/
public final SessionHandlerInterface getHandlerAt(int idx)
{
// Range check the index
if (idx < 0 || idx >= m_handlers.size())
return null;
return (SessionHandlerInterface) m_handlers.elementAt(idx);
}
/**
* Find the required handler by name
*
* @param name String
* @return SessionHandlerInterface
*/
public final SessionHandlerInterface findHandler(String name)
{
// Search for the required handler
for (int i = 0; i < m_handlers.size(); i++)
{
// Get the current handler
SessionHandlerInterface handler = (SessionHandlerInterface) m_handlers.elementAt(i);
if (handler.getHandlerName().equals(name))
return handler;
}
// Handler not found
return null;
}
/**
* Remove a handler from the list
*
* @param idx int
* @return SessionHandlerInterface
*/
public final SessionHandlerInterface remoteHandler(int idx)
{
// Range check the index
if (idx < 0 || idx >= m_handlers.size())
return null;
// Remove the handler, and return it
SessionHandlerInterface handler = (SessionHandlerInterface) m_handlers.elementAt(idx);
m_handlers.removeElementAt(idx);
return handler;
}
/**
* Remove a handler from the list
*
* @param name String
* @return SessionHandlerInterface
*/
public final SessionHandlerInterface remoteHandler(String name)
{
// Search for the required handler
for (int i = 0; i < m_handlers.size(); i++)
{
// Get the current handler
SessionHandlerInterface handler = (SessionHandlerInterface) m_handlers.elementAt(i);
if (handler.getHandlerName().equals(name))
{
// Remove the handler from the list
m_handlers.removeElementAt(i);
return handler;
}
}
// Handler not found
return null;
}
/**
* Remove all handlers from the list
*/
public final void removeAllHandlers()
{
m_handlers.removeAllElements();
}
}

View File

@@ -0,0 +1,155 @@
/*
* 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;
import java.io.*;
import java.net.*;
/**
* Java Socket Based Packet Handler Class
*
* @author GKSpencer
*/
public abstract class SocketPacketHandler implements PacketHandlerInterface {
// Socket to read/write to/from
private Socket m_socket;
// Input/output streams for receiving/sending data
private DataInputStream m_in;
private DataOutputStream m_out;
/**
* Class constructor
*
* @param socket Socket
* @param protocol String
* @exception IOException
*/
protected SocketPacketHandler(Socket socket) throws IOException {
m_socket = socket;
// Open the input/output streams
m_in = new DataInputStream(m_socket.getInputStream());
m_out = new DataOutputStream(m_socket.getOutputStream());
}
/**
* Return the protocol name
*
* @return String
*/
public abstract String getProtocolName();
/**
* Return the number of bytes available for reading without blocking
*
* @return int
* @exception IOException
*/
public int availableBytes() throws IOException {
if (m_in != null)
return m_in.available();
return 0;
}
/**
* Read a packet of data
*
* @param pkt byte[]
* @param offset int
* @param maxLen int
* @return int
* @exception IOException
*/
public int readPacket(byte[] pkt, int offset, int maxLen) throws IOException {
// Read a packet of data
if (m_in != null)
return m_in.read(pkt, offset, maxLen);
return 0;
}
/**
* Write a packet of data
*
* @param pkt byte[]
* @param offset int
* @param len int
* @exception IOException
*/
public void writePacket(byte[] pkt, int offset, int len) throws IOException {
// Output the raw packet
if (m_out != null) {
synchronized (m_out) {
m_out.write(pkt, offset, len);
}
}
}
/**
* Close the packet handler
*/
public void closePacketHandler() {
// Close the socket
if (m_socket != null) {
try {
m_socket.close();
} catch (Exception ex) {
}
m_socket = null;
}
// Close the input stream
if (m_in != null) {
try {
m_in.close();
} catch (Exception ex) {
}
m_in = null;
}
// Close the output stream
if (m_out != null) {
try {
m_out.close();
} catch (Exception ex) {
}
m_out = null;
}
}
/**
* Return the socket
*
* @return Socket
*/
protected final Socket getSocket() {
return m_socket;
}
}

View File

@@ -0,0 +1,329 @@
/*
* 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;
import java.io.*;
import java.net.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Socket Session Handler Class
*
* <p>Implementation of a session handler that uses a Java socket to listen for incoming session requests.
*
* @author GKSpencer
*/
public abstract class SocketSessionHandler implements SessionHandlerInterface, Runnable {
// Debug logging
protected static final Log logger = LogFactory.getLog(SocketSessionHandler.class);
// Constants
//
// Default socket listen back log limit
public static final int ListenBacklog = 10;
// Server that the handler is associated with
private NetworkServer m_server;
// Address/port to use
private int m_port;
private InetAddress m_bindAddr;
// Socket listen back log limit
private int m_backLog = ListenBacklog;
// Server socket to listen for incoming connections
private ServerSocket m_srvSock;
// Session id
private int m_sessId;
// Session handler name, protocol name
private String m_name;
private String m_protocol;
// Shutdown request flag
private boolean m_shutdown;
// Debug enable
private boolean m_debug;
/**
* Class constructor
*
* @param name String
* @param protocol String
* @param server NetworkServer
* @param addr InetAddress
* @param port int
*/
public SocketSessionHandler(String name, String protocol, NetworkServer server, InetAddress addr, int port) {
m_name = name;
m_protocol = protocol;
m_server = server;
m_bindAddr = addr;
m_port = port;
}
/**
* Return the session handler name
*
* @return String
*/
public final String getHandlerName() {
return m_name;
}
/**
* Return the short protocol name
*
* @return String
*/
public final String getProtocolName() {
return m_protocol;
}
/**
* Check if the server should bind to a specific network address
*
* @return boolean
*/
public final boolean hasBindAddress() {
return m_bindAddr != null ? true : false;
}
/**
* Return the network address that the server should bind to
*
* @return InetAddress
*/
public final InetAddress getBindAddress() {
return m_bindAddr;
}
/**
* Return the port that the server should bind to
*
* @return int
*/
public final int getPort() {
return m_port;
}
/**
* Return the socket listen backlog limit
*
* @return int
*/
public final int getListenBacklog() {
return m_backLog;
}
/**
* Determine if debug output is enabled
*
* @return boolean
*/
public final boolean hasDebug() {
return m_debug;
}
/**
* Clear the shutdown flag
*/
protected final void clearShutdown() {
m_shutdown = false;
}
/**
* Determine if the shutdown flag has been set
*
* @return boolean
*/
protected final boolean hasShutdown() {
return m_shutdown;
}
/**
* Get the next available session id
*
* @return int
*/
protected synchronized int getNextSessionId() {
return m_sessId++;
}
/**
* Enable/disable debug output
*
* @param dbg boolean
*/
public final void setDebug(boolean dbg) {
m_debug = dbg;
}
/**
* Set the local port that the session handler is using
*
* @param port int
*/
protected final void setPort(int port) {
m_port = port;
}
/**
* Initialize the session handler
*
* @param server NetworkServer
*/
public void initializeSessionHandler(NetworkServer server) throws IOException {
// Open the server socket
if (hasBindAddress())
m_srvSock = new ServerSocket(getPort(), getListenBacklog(), getBindAddress());
else
m_srvSock = new ServerSocket(getPort(), getListenBacklog());
// Set the allocated port
if (getPort() == 0)
setPort(m_srvSock.getLocalPort());
// DEBUG
if (logger.isDebugEnabled()) {
String bindAddr = hasBindAddress() ? getBindAddress().getHostAddress() : "ALL";
logger.debug("[" + getProtocolName() + "] Binding " + getHandlerName() + " session handler to address : " + bindAddr);
}
}
/**
* Close the session handler
*
* @param server NetworkServer
*/
public void closeSessionHandler(NetworkServer server) {
// Request the main listener thread shutdown
m_shutdown = true;
try {
// Close the server socket to release any pending listen
if (m_srvSock != null)
m_srvSock.close();
} catch (SocketException ex) {
} catch (Exception ex) {
}
}
/**
* Accept a new connection on the specified socket
*
* @param sock Socket
*/
protected abstract void acceptConnection(Socket sock);
/**
* Socket listener thread
*/
public void run() {
try {
// Clear the shutdown flag
clearShutdown();
// Wait for incoming connection requests
while (hasShutdown() == false) {
// Debug
if (logger.isDebugEnabled())
logger.debug("[" + getProtocolName() + "] Waiting for session request ...");
// Wait for a connection
Socket sessSock = m_srvSock.accept();
// Debug
if (logger.isDebugEnabled())
logger.debug("[" + getProtocolName() + "] Session request received from "
+ sessSock.getInetAddress().getHostAddress());
try {
// Process the new connection request
acceptConnection(sessSock);
}
catch (Exception ex) {
// Debug
if (logger.isDebugEnabled())
logger.debug("[" + getProtocolName() + "] Failed to create session, " + ex.toString());
}
}
}
catch (SocketException ex) {
// Do not report an error if the server has shutdown, closing the server socket
// causes an exception to be thrown.
if (hasShutdown() == false) {
logger.debug("[" + getProtocolName() + "] Socket error : " + ex.toString());
logger.debug(ex);
}
}
catch (Exception ex) {
// Do not report an error if the server has shutdown, closing the server socket
// causes an exception to be thrown.
if (hasShutdown() == false) {
logger.debug("[" + getProtocolName() + "] Server error : " + ex.toString());
logger.debug(ex);
}
}
// Debug
if (logger.isDebugEnabled())
logger.debug("[" + getProtocolName() + "] " + getHandlerName() + " session handler closed");
}
}

View File

@@ -27,6 +27,7 @@ import org.alfresco.filesys.server.auth.AuthContext;
import org.alfresco.filesys.server.auth.ClientInfo;
import org.alfresco.filesys.server.core.SharedDevice;
import org.alfresco.filesys.server.core.SharedDeviceList;
import org.alfresco.filesys.server.filesys.FilesysTransaction;
import org.alfresco.service.transaction.TransactionService;
/**
@@ -64,7 +65,6 @@ public abstract class SrvSession
// Debug flags for this session
private int m_debug;
private String m_dbgPrefix;
// Session shutdown flag
@@ -92,8 +92,10 @@ public abstract class SrvSession
// Active transaction and read/write flag
private UserTransaction m_transaction;
private boolean m_readOnlyTrans;
private ThreadLocal<FilesysTransaction> m_tx = new ThreadLocal<FilesysTransaction>();
// UserTransaction m_transaction;
// private boolean m_readOnlyTrans;
// Request and transaction counts
@@ -373,16 +375,6 @@ public abstract class SrvSession
m_debug = flgs;
}
/**
* Set the debug output prefix for this session
*
* @param prefix String
*/
public final void setDebugPrefix(String prefix)
{
m_dbgPrefix = prefix;
}
/**
* Set the logged on/validated status for the session
*
@@ -507,22 +499,34 @@ public abstract class SrvSession
{
boolean created = false;
// Get the filesystem transaction
FilesysTransaction filesysTx = m_tx.get();
if ( filesysTx == null)
{
filesysTx = new FilesysTransaction();
m_tx.set( filesysTx);
}
// If there is an active transaction check that it is the required type
if ( m_transaction != null)
if ( filesysTx.hasTransaction())
{
// Get the active transaction
UserTransaction tx = filesysTx.getTransaction();
// Check if the current transaction is marked for rollback
try
{
if ( m_transaction.getStatus() == Status.STATUS_MARKED_ROLLBACK ||
m_transaction.getStatus() == Status.STATUS_ROLLEDBACK ||
m_transaction.getStatus() == Status.STATUS_ROLLING_BACK)
if ( tx.getStatus() == Status.STATUS_MARKED_ROLLBACK ||
tx.getStatus() == Status.STATUS_ROLLEDBACK ||
tx.getStatus() == Status.STATUS_ROLLING_BACK)
{
// Rollback the current transaction
m_transaction.rollback();
tx.rollback();
}
}
catch ( SystemException ex)
@@ -531,13 +535,13 @@ public abstract class SrvSession
// Check if the transaction is a write transaction, if write has been requested
if ( readOnly == false && m_readOnlyTrans == true)
if ( readOnly == false && filesysTx.isReadOnly() == true)
{
// Commit the read-only transaction
try
{
m_transaction.commit();
tx.commit();
m_transConvCount++;
}
catch ( Exception ex)
@@ -548,24 +552,25 @@ public abstract class SrvSession
{
// Clear the active transaction
m_transaction = null;
filesysTx.clearTransaction();
}
}
}
// Create the transaction
if ( m_transaction == null)
if ( filesysTx.hasTransaction() == false)
{
try
{
m_transaction = transService.getUserTransaction(readOnly);
m_transaction.begin();
UserTransaction userTrans = transService.getUserTransaction(readOnly);
userTrans.begin();
created = true;
// Store the transaction
m_readOnlyTrans = readOnly;
filesysTx.setTransaction( userTrans, readOnly);
m_transCount++;
}
catch (Exception ex)
@@ -585,25 +590,33 @@ public abstract class SrvSession
public final void endTransaction()
throws AlfrescoRuntimeException
{
// Get the filesystem transaction
FilesysTransaction filesysTx = m_tx.get();
// Check if there is an active transaction
if ( m_transaction != null)
if ( filesysTx != null && filesysTx.hasTransaction())
{
// Get the active transaction
UserTransaction tx = filesysTx.getTransaction();
try
{
// Commit or rollback the transaction
if ( m_transaction.getStatus() == Status.STATUS_MARKED_ROLLBACK)
if ( tx.getStatus() == Status.STATUS_MARKED_ROLLBACK)
{
// Transaction is marked for rollback
m_transaction.rollback();
tx.rollback();
}
else
{
// Commit the transaction
m_transaction.commit();
tx.commit();
}
}
catch ( Exception ex)
@@ -614,7 +627,7 @@ public abstract class SrvSession
{
// Clear the current transaction
m_transaction = null;
filesysTx.clearTransaction();
}
}
@@ -626,7 +639,12 @@ public abstract class SrvSession
*/
public final boolean hasUserTransaction()
{
return m_transaction != null ? true : false;
// Get the filesystem transaction
FilesysTransaction filesysTx = m_tx.get();
if ( filesysTx != null)
return filesysTx.hasTransaction();
return false;
}
/**
@@ -636,8 +654,17 @@ public abstract class SrvSession
*/
public final UserTransaction getUserTransaction()
{
UserTransaction trans = m_transaction;
m_transaction = null;
return trans;
// Get the filesystem transaction
UserTransaction userTrans = null;
FilesysTransaction filesysTx = m_tx.get();
if ( filesysTx != null)
{
userTrans = filesysTx.getTransaction();
filesysTx.clearTransaction();
}
return userTrans;
}
}

View File

@@ -0,0 +1,380 @@
/*
* Copyright (C) 2005-2006 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.auth;
import java.util.HashMap;
import java.util.List;
import org.alfresco.config.ConfigElement;
import org.alfresco.filesys.server.SrvSession;
import org.alfresco.filesys.server.config.InvalidConfigurationException;
import org.alfresco.filesys.server.config.ServerConfiguration;
import org.alfresco.filesys.server.oncrpc.AuthType;
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.RpcPacket;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.service.transaction.TransactionService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Alfresco RPC Authenticator Class
*
* <p>Provides authentication support for the NFS server.
*
* @author gkspencer
*/
public class AlfrescoRpcAuthenticator 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 };
// UID/GID to username conversions
private HashMap<Integer, String> m_idMap;
// Authentication component and services
private AuthenticationComponent m_authComponent;
private TransactionService m_transService;
/**
* 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;
if (authType == 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();
// DEBUG
if ( logger.isDebugEnabled())
logger.debug( "RpcAuth: Type=Unix uid=" + uid + ", gid=" + gid);
// Check if the Unix authentication session table is valid
sessKey = new Long((((long) rpc.getClientAddress().hashCode()) << 32) + (gid << 16) + uid);
}
else if ( authType == AuthType.Null)
{
// Set the session key for the null authentication
sessKey = new Integer(rpc.getClientAddress().hashCode());
// DEBUG
if ( logger.isDebugEnabled())
logger.debug( "RpcAuth: Type=Null client=" + rpc.getClientAddress());
}
// 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 = null;
// Get the authentication type
int authType = rpc.getCredentialsType();
// Unpack the client details from the RPC request
if ( authType == AuthType.Unix) {
// Unpack the credentials data
rpc.positionAtCredentialsData();
rpc.skipBytes(4); // stamp id
String clientAddr = rpc.unpackString();
int uid = rpc.unpackInt();
int gid = rpc.unpackInt();
// Check for an additional groups list
int grpLen = rpc.unpackInt();
int[] groups = null;
if (grpLen > 0) {
groups = new int[grpLen];
rpc.unpackIntArray(groups);
}
// Get the user name mapping for the uid/gid and authenticate
Integer idKey = new Integer((gid << 16) + uid);
String userName = m_idMap.get( idKey);
// DEBUG
if ( logger.isDebugEnabled())
logger.debug( "RpcClientInfo: username=" + userName + ", uid=" + uid + ", gid=" + gid);
// Create the client information if there is a valid mapping
if ( userName != null)
{
// Create the client information and fill in relevant fields
cInfo = new ClientInfo( userName, null);
cInfo.setNFSAuthenticationType( authType);
cInfo.setClientAddress( clientAddr);
cInfo.setUid( uid);
cInfo.setGid( gid);
cInfo.setGroupsList(groups);
}
// DEBUG
if (logger.isDebugEnabled())
logger.debug("RpcAuth: Client info, type=" + AuthType.getTypeAsString(authType) + ", name="
+ clientAddr + ", uid=" + uid + ", gid=" + gid + ", groups=" + grpLen);
}
else if ( authType == AuthType.Null)
{
// Create the client information
cInfo = new ClientInfo( "", null);
cInfo.setClientAddress(rpc.getClientAddress().getHostAddress());
// DEBUG
if (logger.isDebugEnabled())
logger.debug("RpcAuth: Client info, type=" + AuthType.getTypeAsString(authType) + ", addr="
+ rpc.getClientAddress().getHostAddress());
}
// Return the client information
return cInfo;
}
/**
* Set the current authenticated user context for this thread
*
* @param sess SrvSession
* @param client ClientInfo
*/
public void setCurrentUser( SrvSession sess, ClientInfo client)
{
// Start a transaction
sess.beginReadTransaction( m_transService);
// Check the account type and setup the authentication context
if ( client == null || client.isNullSession() || client.hasAuthenticationToken() == false)
{
// Clear the authentication, null user should not be allowed to do any service calls
m_authComponent.clearCurrentSecurityContext();
}
else if ( client.isGuest() == false)
{
// Check if the authentication token has been set for the client
if ( client.hasAuthenticationToken() == false)
{
// Set the current user and retrieve the authentication token
m_authComponent.setCurrentUser( client.getUserName());
client.setAuthenticationToken( m_authComponent.getCurrentAuthentication());
}
else
{
// Set the authentication context for the request
m_authComponent.setCurrentAuthentication( client.getAuthenticationToken());
}
}
else
{
// Enable guest access for the request
m_authComponent.setGuestUserAsCurrentUser();
}
}
/**
* Initialize the RPC authenticator
*
* @param config ServerConfiguration
* @param params NameValueList
* @throws InvalidConfigurationException
*/
public void initialize(ServerConfiguration config, ConfigElement params)
throws InvalidConfigurationException {
// Get the configured authentication component and transaction service
m_authComponent = config.getAuthenticationComponent();
m_transService = config.getTransactionService();
// Check for the user mappings
ConfigElement userMappings = params.getChild("userMappings");
if ( userMappings != null)
{
// Allocate the id mappings table
m_idMap = new HashMap<Integer, String>();
// Get the user map elements
List<ConfigElement> userMaps = userMappings.getChildren();
// Process the user list
for ( ConfigElement userElem : userMaps)
{
// Validate the element type
if ( userElem.getName().equalsIgnoreCase( "user"))
{
// Get the user name, user id and group id
String userName = userElem.getAttribute("name");
String uidStr = userElem.getAttribute("uid");
String gidStr = userElem.getAttribute("gid");
if ( userName == null || userName.length() == 0)
throw new InvalidConfigurationException("Empty user name, or name not specified");
if ( uidStr == null || uidStr.length() == 0)
throw new InvalidConfigurationException("Invalid uid, or uid not specified, for user " + userName);
if ( gidStr == null || gidStr.length() == 0)
throw new InvalidConfigurationException("Invalid gid, or gid not specified, for user " + userName);
// Parse the uid/gid
int uid = -1;
int gid = -1;
try
{
uid = Integer.parseInt( uidStr);
}
catch ( NumberFormatException ex)
{
throw new InvalidConfigurationException("Invalid uid value, " + uidStr + " for user " + userName);
}
try
{
gid = Integer.parseInt( gidStr);
}
catch ( NumberFormatException ex)
{
throw new InvalidConfigurationException("Invalid gid value, " + gidStr + " for user " + userName);
}
// Check if the mapping already exists
Integer idKey = new Integer(( gid << 16) + uid);
if ( m_idMap.containsKey( idKey) == false)
{
// Add the username uid/gid mapping
m_idMap.put( idKey, userName);
// DEBUG
if ( logger.isDebugEnabled())
logger.debug("Added RPC user mapping for user " + userName + " uid=" + uid + ", gid=" + gid);
}
else if ( logger.isDebugEnabled())
{
// DEBUG
logger.debug("Ignored duplicate mapping for uid=" + uid + ", gid=" + gid);
}
}
else
throw new InvalidConfigurationException( "Invalid user mapping, " + userElem.getName());
}
}
// Make sure there are some user mappings
if ( m_idMap == null || m_idMap.size() == 0)
throw new InvalidConfigurationException("No user mappings for RPC authenticator");
}
}

View File

@@ -58,6 +58,7 @@ import org.alfresco.filesys.netbios.RFCNetBIOSProtocol;
import org.alfresco.filesys.netbios.win32.Win32NetBIOS;
import org.alfresco.filesys.server.NetworkServer;
import org.alfresco.filesys.server.NetworkServerList;
import org.alfresco.filesys.server.auth.AlfrescoRpcAuthenticator;
import org.alfresco.filesys.server.auth.CifsAuthenticator;
import org.alfresco.filesys.server.auth.acl.ACLParseException;
import org.alfresco.filesys.server.auth.acl.AccessControl;
@@ -75,8 +76,8 @@ import org.alfresco.filesys.server.core.SharedDeviceList;
import org.alfresco.filesys.server.filesys.DefaultShareMapper;
import org.alfresco.filesys.server.filesys.DiskInterface;
import org.alfresco.filesys.server.filesys.DiskSharedDevice;
//import org.alfresco.filesys.server.oncrpc.DefaultRpcAuthenticator;
//import org.alfresco.filesys.server.oncrpc.RpcAuthenticator;
import org.alfresco.filesys.server.oncrpc.DefaultRpcAuthenticator;
import org.alfresco.filesys.server.oncrpc.RpcAuthenticator;
import org.alfresco.filesys.smb.ServerType;
import org.alfresco.filesys.smb.TcpipSMB;
import org.alfresco.filesys.smb.server.repo.ContentContext;
@@ -357,7 +358,7 @@ public class ServerConfiguration extends AbstractLifecycleBean
// RPC authenticator implementation
// private RpcAuthenticator m_rpcAuthenticator;
private RpcAuthenticator m_rpcAuthenticator;
// --------------------------------------------------------------------------------
// Global server configuration
@@ -1699,7 +1700,6 @@ public class ServerConfiguration extends AbstractLifecycleBean
*/
private final void processNFSServerConfig(Config config)
{
/**
// If the configuration section is not valid then NFS is disabled
if ( config == null)
@@ -1856,8 +1856,26 @@ public class ServerConfiguration extends AbstractLifecycleBean
// Create the RPC authenticator
m_rpcAuthenticator = new DefaultRpcAuthenticator();
**/
elem = config.getConfigElement("rpcAuthenticator");
if ( elem != null)
{
// Create the RPC authenticator
m_rpcAuthenticator = new AlfrescoRpcAuthenticator();
try
{
// Initialize the RPC authenticator
m_rpcAuthenticator.initialize( this, elem);
}
catch (InvalidConfigurationException ex)
{
throw new AlfrescoRuntimeException( ex.getMessage());
}
}
else
throw new AlfrescoRuntimeException("RPC authenticator configuration missing, require user mappings");
}
/**
@@ -2181,8 +2199,8 @@ public class ServerConfiguration extends AbstractLifecycleBean
{
// Check if the appropriate authentication component type is configured
if ( ntlmMode != NTLMMode.NONE)
throw new AlfrescoRuntimeException("Wrong authentication setup for passthru authenticator (can only be used with LDAP/JAAS auth component)");
// if ( ntlmMode != NTLMMode.NONE)
// throw new AlfrescoRuntimeException("Wrong authentication setup for passthru authenticator (can only be used with LDAP/JAAS auth component)");
// Load the passthru authenticator dynamically
@@ -3845,12 +3863,11 @@ public class ServerConfiguration extends AbstractLifecycleBean
*
* @return RpcAuthenticator
*/
/**
public final RpcAuthenticator getRpcAuthenticator()
{
return m_rpcAuthenticator;
}
**/
/**
* Close the server configuration, used to close various components that are shared between protocol
* handlers.

View File

@@ -0,0 +1,112 @@
/*
* Copyright (C) 2005-2006 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.filesys;
import javax.transaction.UserTransaction;
/**
* Filesystem Transaction Class
*
* <p>Holds the details of a transaction used during a batch of filesystem driver requests.
*
* @author gkspencer
*/
public class FilesysTransaction {
// Transaction
private UserTransaction m_transaction;
// Flag to indicate read-only or writeable transaction
private boolean m_readOnly;
/**
* Default constructor
*/
public FilesysTransaction()
{
}
/**
* Check if the transaction is valid
*
* @return boolean
*/
public final boolean hasTransaction()
{
return m_transaction != null ? true : false;
}
/**
* Check if the transaction is read-only
*
* @return boolean
*/
public final boolean isReadOnly()
{
return m_readOnly;
}
/**
* Return the active transaction
*
* @return UserTransaction
*/
public final UserTransaction getTransaction()
{
return m_transaction;
}
/**
* Set the transaction
*
* @param trans UserTransaction
* @param readOnly boolean
*/
public final void setTransaction( UserTransaction trans, boolean readOnly)
{
m_transaction = trans;
m_readOnly = readOnly;
}
/**
* Clear the transaction
*/
public final void clearTransaction()
{
m_transaction = null;
m_readOnly = true;
}
/**
* Return the transaction details as a string
*
* @return String
*/
public String toString()
{
StringBuilder str = new StringBuilder();
str.append( "[");
str.append( m_transaction);
str.append( isReadOnly() ? ",Read" : ",Write");
str.append( "]");
return str.toString();
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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.filesys;
import org.alfresco.filesys.server.SrvSession;
/**
* Symbolic Link Interface
*
* <p>Optional interface that a filesystem driver can implement to indicate that symbolic links are supported.
*/
public interface SymbolicLinkInterface {
/**
* Read the link data for a symbolic link
*
* @param sess SrvSession
* @param tree TreeConnection
* @param path String
* @return String
* @exception AccessDeniedException
*/
public String readSymbolicLink( SrvSession sess, TreeConnection tree, String path)
throws AccessDeniedException;
}

View 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];
}
}

View File

@@ -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)
{
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View 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();
}
}

View 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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View 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();
}
}

View File

@@ -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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}

View File

@@ -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];
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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));
}
}

View 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;
}
}

View File

@@ -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;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -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();
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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());
}
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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];
}
}

View File

@@ -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;
}
}