/***************************************************
*
* cismet GmbH, Saarbruecken, Germany
*
* ... and it just works.
*
****************************************************/
/*
* RMServer.java
*
* Created on 23. November 2006, 15:49
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package Sirius.server.registry.rmplugin;
import Sirius.server.registry.rmplugin.exception.UnableToDeregisterException;
import Sirius.server.registry.rmplugin.exception.UnableToSendMessageException;
import Sirius.server.registry.rmplugin.interfaces.RMRegistryServer;
import Sirius.server.registry.rmplugin.util.RMInfo;
import Sirius.server.registry.rmplugin.util.RMUser;
import org.apache.log4j.Logger;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;
import de.cismet.rmplugin.interfaces.RMessenger;
/**
* This Class ist the implementation of the RMRegestry Server. This server provides functionality such as sending
* messages to users, test rm object if there alive, determine online users etc.
*
* @author Sebastian
* @version 0.6
*/
public class RMRegistryServerImpl implements RMRegistryServer {
//~ Static fields/initializers ---------------------------------------------
/** the static logger variable. */
private static final transient Logger LOG = Logger.getLogger(RMRegistryServerImpl.class);
//~ Instance fields --------------------------------------------------------
/** the amount of active clients. */
private Hashtable<String, Vector<RMInfo>> activeClients = new Hashtable<String, Vector<RMInfo>>();
/** the update thread. */
private Thread updateThread;
/** the total count of last send messages. */
private int total = 0;
/** the RMI Registry. */
private Registry reg;
//~ Methods ----------------------------------------------------------------
/**
* This Constructor initializes RMI Registry with the given port. In this registry the RMRegistry is bind, which
* enables clients to register and deregister RMPlugin instances. On the otherside it is possible to send messages
* to active users.
*
* @param port The port which registry should be used or if no one exist where to create
*
* @throws RemoteException java.lang.Exception This method forwards any uncatched exception
*/
public void startRMRegistryServer(final int port) throws RemoteException {
System.out.println("<RMREG> Initializing Remote Messenger Registry"); // NOI18N
LOG.info("<RMREG> Initializing Remote Messenger Registry"); // NOI18N
final RMRegistryServer ser = (RMRegistryServer)UnicastRemoteObject.exportObject(this, port);
reg = LocateRegistry.getRegistry(port);
System.out.println("<RMREG> Bind RMRegistryServer on RMIRegistry as RMRegistryServer"); // NOI18N
LOG.info("<RMREG> Bind RMRegistryServer on RMIRegistry as RMRegistryServer"); // NOI18N
reg.rebind("RMRegistryServer", ser); // NOI18N
System.out.println("<RMREG> ----------RMRegistryServer STARTED!!!----------\n"); // NOI18N
}
/**
* This method is responsible for unbinding the RMRegistry Object.
*
* @throws Exception java.lang.Exception Any Exception thrown in this method will be forwarded to the next higher
* instance
*/
public void stopRMRegistryServer() throws Exception {
String message = "<RMREG> Shutting down the Remote Messenger Registry"; // NOI18N
System.out.println(message);
if (LOG.isInfoEnabled()) {
LOG.info(message);
}
message = "<RMREG> Unbind RMRegistryServer on RMIRegistry"; // NOI18N
System.out.println(message);
if (LOG.isInfoEnabled()) {
LOG.info(message);
}
try {
reg.unbind("RMRegistryServer"); // NOI18N
} catch (final NotBoundException e) {
LOG.warn("RMRegistryServer not available (anymore), probably already unbound", e); // NOI18N
}
message = "<RMREG> ----------RMRegistryServer STOPPED!!!----------\n"; // NOI18N
System.out.println(message);
if (LOG.isInfoEnabled()) {
LOG.info(message);
}
}
/**
* Checks if a user is availabe, available means in this context if you could send a message to this user and use
* the other offered services.
*
* @param target The target is a string identifier build up out of three components seperated by @'s. The
* identifier contains username, group and domain. for example heinz@ketschup@lecker where heinz
* indicates the user, ketschup the group and lecker the domain.
*
* @return return either true if the user is availabe or false if its not
*/
@Override
public boolean available(final String target) {
if (LOG.isDebugEnabled()) {
LOG.debug("is addressee " + target + " available ?"); // NOI18N
}
final Enumeration<String> keys = activeClients.keys();
String targetUser = null;
while (keys.hasMoreElements()) {
final String current = keys.nextElement();
if (current.contains(target) && (activeClients.get(current) != null)) {
targetUser = current;
break;
}
}
if (targetUser != null) {
if (LOG.isDebugEnabled()) {
LOG.debug("addressee " + target + " available"); // NOI18N
}
return true;
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("addressee " + target + " nicht available"); // NOI18N
}
return false;
}
}
/**
* Actually the same method as available(String target) but with the difference that not only if one target user is
* online this account is available. A target is only available if the ipAddresses are equal.
*
* @param target The target is a string identifier build up out of three components seperated by @'s. The
* identifier contains username, group and domain. for example heinz@ketschup@lecker where heinz
* indicates the user, ketschup the group and lecker the domain.
* @param ipAddress the ipAddress of the user
*
* @return return either true if the user is availabe or false if its not
*/
@Override
public boolean available(final String target, final String ipAddress) {
if (LOG.isDebugEnabled()) {
LOG.debug("is addressee " + target + " available ?"); // NOI18N
}
final Enumeration<String> keys = activeClients.keys();
String targetUser = null;
while (keys.hasMoreElements()) {
final String current = keys.nextElement();
if (current.contains(target) && (activeClients.get(current) != null)) {
final Vector<RMInfo> tmp = activeClients.get(current);
final Iterator<RMInfo> it = tmp.iterator();
while (it.hasNext()) {
final RMInfo info = it.next();
if (info.getIP().equals(ipAddress)) {
targetUser = current;
break;
}
}
break;
}
}
if (targetUser != null) {
if (LOG.isDebugEnabled()) {
LOG.debug("addressee " + target + " available"); // NOI18N
}
return true;
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("addressee " + target + " not available"); // NOI18N
}
return false;
}
}
/**
* This method is the heart of RMRegistry implementation and provides the main functionallity. This method sends a
* message with a title to the given target. Target means in this context all user which contains the target string
* complete.
*
* @param target The target is a string identifier build up out of three components seperated by @'s. The
* identifier contains username, group and domain. for example heinz@ketschup@lecker where heinz
* indicates the user, ketschup the group and lecker the domain.
* @param message the message which should be send to the user
* @param title the a short title of the message
*
* @return returns the number messages actually send to users
*
* @throws UnableToSendMessageException Sirius.server.registry.rmplugin.exception.UnableToSendMessageException
* This Exception is thrown if any attemp to send a message to an user
* identified by the target string fails due to extern circumstances for
* example a remote Exception.
*/
// Wenn ein senden fehl schl\u00E4gt dann --> werden die restlichen nicht gesendet
@Override
public int sendMessage(final String target, final String message, final String title)
throws UnableToSendMessageException {
boolean flagSendProblem = false;
Exception ex = null;
total = 0;
if (LOG.isDebugEnabled()) {
LOG.debug("send message to " + target); // NOI18N
}
final Enumeration<String> keys = activeClients.keys();
final String targetUser = null;
while (keys.hasMoreElements()) {
final String current = keys.nextElement();
if (current.contains(target)) {
final Vector<RMInfo> tmp = activeClients.get(current);
if (tmp != null) {
final Iterator<RMInfo> it = tmp.iterator();
while (it.hasNext()) {
final RMInfo info = it.next();
if (info != null) {
if (LOG.isDebugEnabled()) {
LOG.debug("RMInfo Objekt is not null"); // NOI18N
}
try {
if (LOG.isDebugEnabled()) {
LOG.debug("RMIAddress:" + info.getRmiAddress().toString()); // NOI18N
}
final RMessenger messenger = (RMessenger)Naming.lookup(info.getRmiAddress().toString());
messenger.sendMessage(message, title);
if (LOG.isDebugEnabled()) {
LOG.debug("Message successfully sent"); // NOI18N
}
total++;
} catch (Exception e) {
// if(total>0)total--;
LOG.error("Error while transmitting the message to " + target, e); // NOI18N
// throw new UnableToSendMessageException("Sending Message to target "+target+" fails,
// one or more messages are maybe not delivered"+e.toString());
ex = e;
flagSendProblem = true;
}
}
}
}
}
}
if (flagSendProblem) {
throw new UnableToSendMessageException(
"Sending Message to target " // NOI18N
+ target
+ " fails, one or more messages are maybe not delivered\n Exception:\n" // NOI18N
+ ex.toString()
+ "\n Total sended: " // NOI18N
+ total,
total);
}
return total;
}
/**
* Actually the same as the Method sendMessage(String,String) but with this method it is possible to further select
* the user via the ipAddress. The message is only send to a user exact on that maschine.
*
* @param target The target is a string identifier build up out of three components seperated by @'s. The
* identifier contains username, group and domain. for example heinz@ketschup@lecker where heinz
* indicates the user, ketschup the group and lecker the domain.
* @param ipAddress the ipAddress to which the message should be send
* @param message the message which should be send to the user
* @param title the a short title of the message
*
* @return returns the number messages actually send to users
*
* @throws UnableToSendMessageException Sirius.server.registry.rmplugin.exception.UnableToSendMessageException
* This Exception is thrown if any attemp to send a message to an user
* identified by the target string fails due to extern circumstances for
* example a remote Exception.
*/
@Override
public int sendMessage(final String target, final String ipAddress, final String message, final String title)
throws UnableToSendMessageException {
boolean flagSendProblem = false;
Exception ex = null;
total = 0;
if (LOG.isDebugEnabled()) {
LOG.debug("Send message to " + target); // NOI18N
}
final Enumeration<String> keys = activeClients.keys();
final String targetUser = null;
while (keys.hasMoreElements()) {
final String current = keys.nextElement();
if (current.contains(target)) {
final Vector<RMInfo> tmp = activeClients.get(current);
if (tmp != null) {
final Iterator<RMInfo> it = tmp.iterator();
while (it.hasNext()) {
final RMInfo info = it.next();
if ((info != null) && info.getIP().equals(ipAddress)) {
if (LOG.isDebugEnabled()) {
LOG.debug("RMInfo object is not null"); // NOI18N
}
try {
if (LOG.isDebugEnabled()) {
LOG.debug("RMIAddress:" + info.getRmiAddress().toString()); // NOI18N
}
final RMessenger messenger = (RMessenger)Naming.lookup(info.getRmiAddress().toString());
messenger.sendMessage(message, title);
if (LOG.isDebugEnabled()) {
LOG.debug("Message successfully sent"); // NOI18N
}
total++;
} catch (Exception e) {
// if(total>0)total--;
LOG.error("Error while transmitting the message to " + target, e); // NOI18N
ex = e;
flagSendProblem = true;
}
}
}
}
}
}
if (flagSendProblem) {
throw new UnableToSendMessageException(
"Sending Message to target " // NOI18N
+ target
+ " fails, one or more messages are maybe not delivered\n Exception:\n" // NOI18N
+ ex.toString()
+ "\n Total sended: " // NOI18N
+ total,
total);
}
return total;
}
/**
* This Method registers an RMPlugin in the RMRegistry. In this process a mapping is created between the full
* qualified identifier (username@group@domain) and the delivered RMInfo Object which contains all necessary
* information to connect to the RMPlugin. This information more specific the mapping will be saved in a hashmap.
*
* @param info This object contains all the information of the object who is asking for registration.
*/
@Override
public void register(final RMInfo info) {
final String key = info.getKey();
if (LOG.isDebugEnabled()) {
LOG.debug(key + " has registered at the RMRegistryServer"); // NOI18N
}
// activeUsers.put(key,info);
Vector<RMInfo> tmp = activeClients.get(info.getKey());
if (tmp != null) {
synchronized (tmp) {
final int index = tmp.indexOf(info);
if (index != -1) {
tmp.remove(index);
tmp.add(info);
} else {
tmp.add(info);
}
}
} else {
synchronized (activeClients) {
tmp = activeClients.get(info.getKey());
if (tmp != null) {
final int index = tmp.indexOf(info);
if (index != -1) {
tmp.remove(index);
tmp.add(info);
} else {
tmp.add(info);
}
} else {
tmp = new Vector<RMInfo>();
tmp.add(info);
activeClients.put(key, tmp);
}
}
}
}
/**
* This method is the pendant to the register method and deregisters a rmplugin by deleting the mapping between
* qualified name and RMInfo object.
*
* @param info The object which should be removed from the RMRegistry
*
* @throws UnableToDeregisterException Sirius.server.registry.rmplugin.exception.UnableToDeregisterException This
* Exception is thrown if the object which should be deregister does not exist
* in the RMRegistry
*/
@Override
public void deregister(final RMInfo info) throws UnableToDeregisterException {
final String key = info.getKey();
if (LOG.isDebugEnabled()) {
LOG.debug(key + " tries to sign out from the RMRegistryServer"); // NOI18N
// activeUsers.remove(key);
}
final Vector<RMInfo> tmp = activeClients.get(info.getKey());
if (tmp != null) {
synchronized (tmp) {
final int index = tmp.indexOf(info);
if (LOG.isDebugEnabled()) {
LOG.debug("Index of the object is:" + index); // NOI18N
}
if (index != -1) {
tmp.remove(index);
if (LOG.isDebugEnabled()) {
LOG.debug(key + " is RMRegistryServer signed out"); // NOI18N
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug(key + " no such user in RMRegistryServer"); // NOI18N
}
throw new UnableToDeregisterException("User is not in registry"); // NOI18N
}
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug(key + " no such user in RMRegistryServer"); // NOI18N
}
throw new UnableToDeregisterException("User is not in registry"); // NOI18N
}
}
//ungetestede weitergabe \u00FCberpr\u00FCft nicht ob das rmi objekt noch ansprechbar ist
/**
* This Method delivers all domain in which are currently user registrated.
*
* @return a ArrayList which contains all domains
*/
@Override
public ArrayList<String> getAllActiveDomains() {
final ArrayList activeDomains = new ArrayList();
final Enumeration<String> keys = activeClients.keys();
while (keys.hasMoreElements()) {
final String current = keys.nextElement();
final String currentDomain = extractDomainName(current);
if ((currentDomain != null) && !activeDomains.contains(currentDomain)) {
activeDomains.add(currentDomain);
}
}
return activeDomains;
}
/**
* This Method returns all groups within a specific domain. A group is returned if at least one member is currently
* online
*
* @param domain Only the groups specified by this domain will be checked
*
* @return an Arraylist containing the groups which at least have one active user
*/
@Override
public ArrayList<String> getAllActiveGroups(final String domain) {
final ArrayList activeGroups = new ArrayList();
final Enumeration<String> keys = activeClients.keys();
while (keys.hasMoreElements()) {
final String current = keys.nextElement();
final String currentDomain = extractDomainName(current);
if ((currentDomain != null) && currentDomain.equals(domain)) {
final String currentGroup = extractGroupName(current);
if ((currentGroup != null) && !activeGroups.contains(currentGroup)) {
activeGroups.add(currentGroup);
}
}
}
return activeGroups;
}
/**
* This Method returns all active user specified by a group and a domain.
*
* @param group the group which should be checked
* @param domain the domain which should be checked
*
* @return an ArrayList containing all active users
*/
@Override
public ArrayList<RMUser> getAllActiveUsers(final String group, final String domain) {
final ArrayList<RMUser> activeUsers = new ArrayList();
final Enumeration<String> keys = activeClients.keys();
while (keys.hasMoreElements()) {
final String current = keys.nextElement();
final String currentDomain = extractDomainName(current);
final String currentGroup = extractGroupName(current);
if (((currentDomain != null) && (currentGroup != null))
&& (currentDomain.equals(domain) && currentGroup.equals(group))) {
final String currentUser = extractUserName(current);
if ((currentUser != null) && !activeUsers.contains(currentUser)) {
final Vector<RMInfo> tmp = activeClients.get(current);
if (tmp != null) {
final Iterator<RMInfo> it = tmp.iterator();
while (it.hasNext()) {
final RMInfo user = it.next();
activeUsers.add(
new RMUser(
user.getUserName(),
user.getUserGroup(),
user.getUserDomain(),
user.getOnlineTimeInMillis(),
user.getIP()));
}
}
}
}
}
return activeUsers;
}
/**
* This method returns every user registered in the RMRegistry in an ArrayList. The objects in the list are
* containing the username and the onlineTime.
*
* @return all current active User
*/
@Override
public ArrayList<RMUser> getAllUsers() {
final ArrayList activeUsers = new ArrayList();
final Enumeration<String> keys = activeClients.keys();
while (keys.hasMoreElements()) {
final String current = keys.nextElement();
final Vector<RMInfo> tmp = activeClients.get(current);
if (tmp != null) {
final Iterator<RMInfo> it = tmp.iterator();
while (it.hasNext()) {
final RMInfo user = it.next();
activeUsers.add(
new RMUser(
user.getUserName(),
user.getUserGroup(),
user.getUserDomain(),
user.getOnlineTimeInMillis(),
user.getIP()));
}
}
}
return activeUsers;
}
/**
* this method is only for internal use and extracts the domainname out of a qualified name.
*
* @param string the qualified name wich contains a domain
*
* @return the extracted domain
*/
private String extractDomainName(final String string) {
final String substring;
try {
final int start = string.lastIndexOf("@"); // NOI18N
substring = string.substring(start + 1, string.length());
} catch (Exception e) {
LOG.error("Error while extracting the domain from:" + string); // NOI18N
return null;
}
return substring;
}
/**
* This method is only for internal use and extracts the groupname out of a qualified name.
*
* @param string the qualified name wich contains a group
*
* @return the extracted groupname
*/
private String extractGroupName(final String string) {
final String substring;
try {
final int start = string.indexOf("@"); // NOI18N
final int end = string.lastIndexOf("@"); // NOI18N
substring = string.substring(start + 1, end);
} catch (Exception e) {
LOG.error("Error while extracting the group from:" + string); // NOI18N
return null;
}
return substring;
}
/**
* This method is only for internal use and extracts the username out of a qualified name.
*
* @param string the qualified name wich contains a username
*
* @return the extracted username
*/
private String extractUserName(final String string) {
final String substring;
try {
final int end = string.indexOf("@"); // NOI18N
substring = string.substring(0, end);
} catch (Exception e) {
LOG.error("Error while extracting the group from:" + string); // NOI18N
return null;
}
return substring;
}
/**
* This method updates the registry that means every dead rmObject (if there is any problem by using the remote
* methods) will be removed from the RMRegistry.
*/
@Override
public synchronized void updateRegistry() {
if (updateThread != null) {
if (!updateThread.isAlive()) {
if (LOG.isDebugEnabled()) {
LOG.debug("Starting update thread"); // NOI18N
}
updateThread.run();
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("Update thread already running"); // NOI18N
}
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("Creating new update thread"); // NOI18N
}
updateThread = new Thread() {
@Override
public void run() {
update();
}
};
updateThread.run();
}
}
/**
* This method is practically called from a thread out of the updateRegistry() method.
*/
private void update() {
if (LOG.isDebugEnabled()) {
LOG.debug("Updating Registry..."); // NOI18N
}
final Enumeration<String> keys = activeClients.keys();
while (keys.hasMoreElements()) {
final String current = keys.nextElement();
final Vector<RMInfo> userList = activeClients.get(current);
if (userList != null) {
final Iterator<RMInfo> it = userList.iterator();
while (it.hasNext()) {
final RMInfo user = it.next();
if (user != null) {
try {
final RMessenger messenger = (RMessenger)Naming.lookup(user.getRmiAddress().toString());
messenger.test();
} catch (Exception e) {
if (LOG.isDebugEnabled()) {
LOG.debug(
"Exception during Registry Update: Entry --> " // NOI18N
+ user.getRmiAddress().toString()
+ " removing entry", // NOI18N
e);
}
activeClients.remove(user.getKey());
}
}
}
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("Updating Registry finished"); // NOI18N
}
}
/**
* This method logs the current content of the registry to the log4j logger in the way qualified name --> rmiAdress.
*/
@Override
public void logCurrentRegistry() {
if (LOG.isDebugEnabled()) {
LOG.debug("logging Registry..."); // NOI18N
}
final Enumeration<String> keys = activeClients.keys();
if (LOG.isDebugEnabled()) {
LOG.debug("Registry:"); // NOI18N
}
while (keys.hasMoreElements()) {
final String current = keys.nextElement();
final Vector<RMInfo> userList = activeClients.get(current);
if (userList != null) {
final Iterator<RMInfo> it = userList.iterator();
while (it.hasNext()) {
final RMInfo user = it.next();
if (user != null) {
if (LOG.isDebugEnabled()) {
LOG.debug(
current
+ " : " // NOI18N
+ user.getRmiAddress()
+ " --> online since " // NOI18N
+ user.getOnlineTimeAsText());
}
}
}
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("Logging Registry finished"); // NOI18N
}
}
/**
* Getter for the actual count of users online.
*
* @return the count of online users
*/
protected int getUserCount() {
int count = 0;
final Enumeration<String> keys = activeClients.keys();
while (keys.hasMoreElements()) {
final String current = keys.nextElement();
final Vector<RMInfo> userList = activeClients.get(current);
if (userList != null) {
final Iterator<RMInfo> it = userList.iterator();
while (it.hasNext()) {
final RMInfo user = it.next();
if (user != null) {
count++;
}
}
}
}
return count;
}
/**
* Returns how many messages are send at last invocation of sendMessage().
*
* @return returns the total amount of last send messages
*/
protected int getTotalCountOfLastSendMessages() {
return total;
}
}