Source Code Cross Referenced for MultiUserChatServerImpl.java in  » Net » openfire » org » jivesoftware » openfire » muc » spi » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Net » openfire » org.jivesoftware.openfire.muc.spi 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /**
0002:         * $RCSfile: MultiUserChatServerImpl.java,v $
0003:         * $Revision: 3036 $
0004:         * $Date: 2005-11-07 15:15:00 -0300 (Mon, 07 Nov 2005) $
0005:         *
0006:         * Copyright (C) 2004 Jive Software. All rights reserved.
0007:         *
0008:         * This software is published under the terms of the GNU Public License (GPL),
0009:         * a copy of which is included in this distribution.
0010:         */package org.jivesoftware.openfire.muc.spi;
0011:
0012:        import org.dom4j.DocumentHelper;
0013:        import org.dom4j.Element;
0014:        import org.jivesoftware.openfire.*;
0015:        import org.jivesoftware.openfire.auth.UnauthorizedException;
0016:        import org.jivesoftware.openfire.cluster.ClusterEventListener;
0017:        import org.jivesoftware.openfire.cluster.ClusterManager;
0018:        import org.jivesoftware.openfire.container.BasicModule;
0019:        import org.jivesoftware.openfire.disco.*;
0020:        import org.jivesoftware.openfire.forms.DataForm;
0021:        import org.jivesoftware.openfire.forms.FormField;
0022:        import org.jivesoftware.openfire.forms.spi.XDataFormImpl;
0023:        import org.jivesoftware.openfire.forms.spi.XFormFieldImpl;
0024:        import org.jivesoftware.openfire.muc.*;
0025:        import org.jivesoftware.openfire.muc.cluster.*;
0026:        import org.jivesoftware.openfire.resultsetmanager.ResultSet;
0027:        import org.jivesoftware.openfire.stats.Statistic;
0028:        import org.jivesoftware.openfire.stats.StatisticsManager;
0029:        import org.jivesoftware.util.*;
0030:        import org.jivesoftware.util.cache.CacheFactory;
0031:        import org.xmpp.component.ComponentManager;
0032:        import org.xmpp.packet.*;
0033:
0034:        import java.util.*;
0035:        import java.util.concurrent.ConcurrentHashMap;
0036:        import java.util.concurrent.ConcurrentLinkedQueue;
0037:        import java.util.concurrent.CopyOnWriteArrayList;
0038:        import java.util.concurrent.LinkedBlockingQueue;
0039:        import java.util.concurrent.atomic.AtomicInteger;
0040:        import java.util.concurrent.atomic.AtomicLong;
0041:
0042:        /**
0043:         * Implements the chat server as a cached memory resident chat server. The server is also
0044:         * responsible for responding Multi-User Chat disco requests as well as removing inactive users from
0045:         * the rooms after a period of time and to maintain a log of the conversation in the rooms that 
0046:         * require to log their conversations. The conversations log is saved to the database using a 
0047:         * separate process<p>
0048:         *
0049:         * Temporary rooms are held in memory as long as they have occupants. They will be destroyed after
0050:         * the last occupant left the room. On the other hand, persistent rooms are always present in memory
0051:         * even after the last occupant left the room. In order to keep memory clean of persistent rooms that
0052:         * have been forgotten or abandoned this class includes a clean up process. The clean up process
0053:         * will remove from memory rooms that haven't had occupants for a while. Moreover, forgotten or
0054:         * abandoned rooms won't be loaded into memory when the Multi-User Chat service starts up.
0055:         *
0056:         * @author Gaston Dombiak
0057:         */
0058:        public class MultiUserChatServerImpl extends BasicModule implements 
0059:                MultiUserChatServer, ServerItemsProvider, DiscoInfoProvider,
0060:                DiscoItemsProvider, RoutableChannelHandler,
0061:                ClusterEventListener {
0062:
0063:            private static final FastDateFormat dateFormatter = FastDateFormat
0064:                    .getInstance(JiveConstants.XMPP_DELAY_DATETIME_FORMAT,
0065:                            TimeZone.getTimeZone("UTC"));
0066:
0067:            /**
0068:             * Statistics keys
0069:             */
0070:            private static final String roomsStatKey = "muc_rooms";
0071:            private static final String occupantsStatKey = "muc_occupants";
0072:            private static final String usersStatKey = "muc_users";
0073:            private static final String incomingStatKey = "muc_incoming";
0074:            private static final String outgoingStatKey = "muc_outgoing";
0075:            private static final String trafficStatGroup = "muc_traffic";
0076:
0077:            /**
0078:             * The time to elapse between clearing of idle chat users.
0079:             */
0080:            private int user_timeout = 300000;
0081:            /**
0082:             * The number of milliseconds a user must be idle before he/she gets kicked from all the rooms.
0083:             */
0084:            private int user_idle = -1;
0085:            /**
0086:             * Task that kicks idle users from the rooms.
0087:             */
0088:            private UserTimeoutTask userTimeoutTask;
0089:            /**
0090:             * The time to elapse between logging the room conversations.
0091:             */
0092:            private int log_timeout = 300000;
0093:            /**
0094:             * The number of messages to log on each run of the logging process.
0095:             */
0096:            private int log_batch_size = 50;
0097:            /**
0098:             * Task that flushes room conversation logs to the database.
0099:             */
0100:            private LogConversationTask logConversationTask;
0101:            /**
0102:             * the chat service's hostname
0103:             */
0104:            private String chatServiceName = null;
0105:
0106:            /**
0107:             * chatrooms managed by this manager, table: key room name (String); value ChatRoom
0108:             */
0109:            private Map<String, LocalMUCRoom> rooms = new ConcurrentHashMap<String, LocalMUCRoom>();
0110:
0111:            /**
0112:             * Chat users managed by this manager. This includes only users connected to this JVM.
0113:             * That means that when running inside of a cluster each node will have its own manager
0114:             * that in turn will keep its own list of locally connected.
0115:             *
0116:             * table: key user jid (XMPPAddress); value ChatUser
0117:             */
0118:            private Map<JID, LocalMUCUser> users = new ConcurrentHashMap<JID, LocalMUCUser>();
0119:            private HistoryStrategy historyStrategy;
0120:
0121:            private RoutingTable routingTable = null;
0122:            /**
0123:             * The packet router for the server.
0124:             */
0125:            private PacketRouter router = null;
0126:            /**
0127:             * The handler of packets with namespace jabber:iq:register for the server.
0128:             */
0129:            private IQMUCRegisterHandler registerHandler = null;
0130:
0131:            /**
0132:             * The handler of search requests ('jabber:iq:search' namespace).
0133:             */
0134:            private IQMUCSearchHandler searchHandler = null;
0135:
0136:            /**
0137:             * The total time all agents took to chat *
0138:             */
0139:            public long totalChatTime;
0140:
0141:            /**
0142:             * Timer to monitor chatroom participants. If they've been idle for too long, probe for
0143:             * presence.
0144:             */
0145:            private Timer timer = new Timer("MUC cleanup");
0146:
0147:            /**
0148:             * Flag that indicates if the service should provide information about locked rooms when
0149:             * handling service discovery requests.
0150:             * Note: Setting this flag in false is not compliant with the spec. A user may try to join a
0151:             * locked room thinking that the room doesn't exist because the user didn't discover it before.
0152:             */
0153:            private boolean allowToDiscoverLockedRooms = true;
0154:
0155:            /**
0156:             * Returns the permission policy for creating rooms. A true value means that not anyone can
0157:             * create a room, only the JIDs listed in <code>allowedToCreate</code> are allowed to create
0158:             * rooms.
0159:             */
0160:            private boolean roomCreationRestricted = false;
0161:
0162:            /**
0163:             * Bare jids of users that are allowed to create MUC rooms. An empty list means that anyone can 
0164:             * create a room. 
0165:             */
0166:            private List<String> allowedToCreate = new CopyOnWriteArrayList<String>();
0167:
0168:            /**
0169:             * Bare jids of users that are system administrators of the MUC service. A sysadmin has the same
0170:             * permissions as a room owner.
0171:             */
0172:            private List<String> sysadmins = new CopyOnWriteArrayList<String>();
0173:
0174:            /**
0175:             * Queue that holds the messages to log for the rooms that need to log their conversations.
0176:             */
0177:            private Queue<ConversationLogEntry> logQueue = new LinkedBlockingQueue<ConversationLogEntry>();
0178:
0179:            /**
0180:             * Max number of hours that a persistent room may be empty before the service removes the
0181:             * room from memory. Unloaded rooms will exist in the database and may be loaded by a user
0182:             * request. Default time limit is: 30 days.
0183:             */
0184:            private long emptyLimit = 30 * 24;
0185:            /**
0186:             * Task that removes rooms from memory that have been without activity for a period of time. A
0187:             * room is considered without activity when no occupants are present in the room for a while.
0188:             */
0189:            private CleanupTask cleanupTask;
0190:            /**
0191:             * The time to elapse between each rooms cleanup. Default frequency is 60 minutes.
0192:             */
0193:            private static final long CLEANUP_FREQUENCY = 60 * 60 * 1000;
0194:
0195:            /**
0196:             * Total number of received messages in all rooms since the last reset. The counter
0197:             * is reset each time the Statistic makes a sampling.
0198:             */
0199:            private AtomicInteger inMessages = new AtomicInteger(0);
0200:            /**
0201:             * Total number of broadcasted messages in all rooms since the last reset. The counter
0202:             * is reset each time the Statistic makes a sampling.
0203:             */
0204:            private AtomicLong outMessages = new AtomicLong(0);
0205:
0206:            /**
0207:             * Flag that indicates if MUC service is enabled.
0208:             */
0209:            private boolean serviceEnabled = true;
0210:
0211:            Collection<MUCEventListener> listeners = new ConcurrentLinkedQueue<MUCEventListener>();
0212:
0213:            /**
0214:             * Create a new group chat server.
0215:             */
0216:            public MultiUserChatServerImpl() {
0217:                super ("Basic multi user chat server");
0218:                historyStrategy = new HistoryStrategy(null);
0219:            }
0220:
0221:            public String getDescription() {
0222:                return null;
0223:            }
0224:
0225:            public void process(Packet packet) throws UnauthorizedException,
0226:                    PacketException {
0227:                // TODO Remove this method when moving MUC as a component and removing module code
0228:                processPacket(packet);
0229:            }
0230:
0231:            public void processPacket(Packet packet) {
0232:                if (!isServiceEnabled()) {
0233:                    return;
0234:                }
0235:                // The MUC service will receive all the packets whose domain matches the domain of the MUC
0236:                // service. This means that, for instance, a disco request should be responded by the
0237:                // service itself instead of relying on the server to handle the request.
0238:                try {
0239:                    // Check if the packet is a disco request or a packet with namespace iq:register
0240:                    if (packet instanceof  IQ) {
0241:                        if (process((IQ) packet)) {
0242:                            return;
0243:                        }
0244:                    }
0245:                    // The packet is a normal packet that should possibly be sent to the room
0246:                    JID receipient = packet.getTo();
0247:                    String roomName = receipient != null ? receipient.getNode()
0248:                            : null;
0249:                    getChatUser(packet.getFrom(), roomName).process(packet);
0250:                } catch (Exception e) {
0251:                    Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
0252:                }
0253:            }
0254:
0255:            /**
0256:             * Returns true if the IQ packet was processed. This method should only process disco packets
0257:             * as well as jabber:iq:register packets sent to the MUC service.
0258:             *
0259:             * @param iq the IQ packet to process.
0260:             * @return true if the IQ packet was processed.
0261:             */
0262:            private boolean process(IQ iq) {
0263:                Element childElement = iq.getChildElement();
0264:                String namespace = null;
0265:                // Ignore IQs of type ERROR
0266:                if (IQ.Type.error == iq.getType()) {
0267:                    return false;
0268:                }
0269:                if (iq.getTo().getResource() != null) {
0270:                    // Ignore IQ packets sent to room occupants
0271:                    return false;
0272:                }
0273:                if (childElement != null) {
0274:                    namespace = childElement.getNamespaceURI();
0275:                }
0276:                if ("jabber:iq:register".equals(namespace)) {
0277:                    IQ reply = registerHandler.handleIQ(iq);
0278:                    router.route(reply);
0279:                } else if ("jabber:iq:search".equals(namespace)) {
0280:                    IQ reply = searchHandler.handleIQ(iq);
0281:                    router.route(reply);
0282:                } else if ("http://jabber.org/protocol/disco#info"
0283:                        .equals(namespace)) {
0284:                    // TODO MUC should have an IQDiscoInfoHandler of its own when MUC becomes
0285:                    // a component
0286:                    IQ reply = XMPPServer.getInstance().getIQDiscoInfoHandler()
0287:                            .handleIQ(iq);
0288:                    router.route(reply);
0289:                } else if ("http://jabber.org/protocol/disco#items"
0290:                        .equals(namespace)) {
0291:                    // TODO MUC should have an IQDiscoItemsHandler of its own when MUC becomes
0292:                    // a component
0293:                    IQ reply = XMPPServer.getInstance()
0294:                            .getIQDiscoItemsHandler().handleIQ(iq);
0295:                    router.route(reply);
0296:                } else {
0297:                    return false;
0298:                }
0299:                return true;
0300:            }
0301:
0302:            public void initialize(JID jid, ComponentManager componentManager) {
0303:
0304:            }
0305:
0306:            public void shutdown() {
0307:
0308:            }
0309:
0310:            public String getServiceDomain() {
0311:                return chatServiceName + "."
0312:                        + XMPPServer.getInstance().getServerInfo().getName();
0313:            }
0314:
0315:            public JID getAddress() {
0316:                return new JID(null, getServiceDomain(), null, true);
0317:            }
0318:
0319:            /**
0320:             * Probes the presence of any user who's last packet was sent more than 5 minute ago.
0321:             */
0322:            private class UserTimeoutTask extends TimerTask {
0323:                /**
0324:                 * Remove any user that has been idle for longer than the user timeout time.
0325:                 */
0326:                public void run() {
0327:                    checkForTimedOutUsers();
0328:                }
0329:            }
0330:
0331:            private void checkForTimedOutUsers() {
0332:                final long deadline = System.currentTimeMillis() - user_idle;
0333:                for (LocalMUCUser user : users.values()) {
0334:                    try {
0335:                        // If user is not present in any room then remove the user from
0336:                        // the list of users
0337:                        if (!user.isJoined()) {
0338:                            removeUser(user.getAddress());
0339:                            continue;
0340:                        }
0341:                        // Do nothing if this feature is disabled (i.e USER_IDLE equals -1)
0342:                        if (user_idle == -1) {
0343:                            return;
0344:                        }
0345:                        if (user.getLastPacketTime() < deadline) {
0346:                            // Kick the user from all the rooms that he/she had previuosly joined
0347:                            MUCRoom room;
0348:                            Presence kickedPresence;
0349:                            for (LocalMUCRole role : user.getRoles()) {
0350:                                room = role.getChatRoom();
0351:                                try {
0352:                                    kickedPresence = room.kickOccupant(user
0353:                                            .getAddress(), null, null);
0354:                                    // Send the updated presence to the room occupants
0355:                                    room.send(kickedPresence);
0356:                                } catch (NotAllowedException e) {
0357:                                    // Do nothing since we cannot kick owners or admins
0358:                                }
0359:                            }
0360:                        }
0361:                    } catch (Throwable e) {
0362:                        Log.error(
0363:                                LocaleUtils.getLocalizedString("admin.error"),
0364:                                e);
0365:                    }
0366:                }
0367:            }
0368:
0369:            /**
0370:             * Logs the conversation of the rooms that have this feature enabled.
0371:             */
0372:            private class LogConversationTask extends TimerTask {
0373:                public void run() {
0374:                    try {
0375:                        logConversation();
0376:                    } catch (Throwable e) {
0377:                        Log.error(
0378:                                LocaleUtils.getLocalizedString("admin.error"),
0379:                                e);
0380:                    }
0381:                }
0382:            }
0383:
0384:            private void logConversation() {
0385:                ConversationLogEntry entry;
0386:                boolean success;
0387:                for (int index = 0; index <= log_batch_size
0388:                        && !logQueue.isEmpty(); index++) {
0389:                    entry = logQueue.poll();
0390:                    if (entry != null) {
0391:                        success = MUCPersistenceManager
0392:                                .saveConversationLogEntry(entry);
0393:                        if (!success) {
0394:                            logQueue.add(entry);
0395:                        }
0396:                    }
0397:                }
0398:            }
0399:
0400:            /**
0401:             * Logs all the remaining conversation log entries to the database. Use this method to force
0402:             * saving all the conversation log entries before the service becomes unavailable.
0403:             */
0404:            private void logAllConversation() {
0405:                ConversationLogEntry entry;
0406:                while (!logQueue.isEmpty()) {
0407:                    entry = logQueue.poll();
0408:                    if (entry != null) {
0409:                        MUCPersistenceManager.saveConversationLogEntry(entry);
0410:                    }
0411:                }
0412:            }
0413:
0414:            /**
0415:             * Removes from memory rooms that have been without activity for a period of time. A room is
0416:             * considered without activity when no occupants are present in the room for a while.
0417:             */
0418:            private class CleanupTask extends TimerTask {
0419:                public void run() {
0420:                    if (ClusterManager.isClusteringStarted()
0421:                            && !ClusterManager.isSeniorClusterMember()) {
0422:                        // Do nothing if we are in a cluster and this JVM is not the senior cluster member
0423:                        return;
0424:                    }
0425:                    try {
0426:                        cleanupRooms();
0427:                    } catch (Throwable e) {
0428:                        Log.error(
0429:                                LocaleUtils.getLocalizedString("admin.error"),
0430:                                e);
0431:                    }
0432:                }
0433:            }
0434:
0435:            private void cleanupRooms() {
0436:                for (MUCRoom room : rooms.values()) {
0437:                    if (room.getEmptyDate() != null
0438:                            && room.getEmptyDate().before(getCleanupDate())) {
0439:                        removeChatRoom(room.getName());
0440:                    }
0441:                }
0442:            }
0443:
0444:            public MUCRoom getChatRoom(String roomName, JID userjid)
0445:                    throws NotAllowedException {
0446:                LocalMUCRoom room;
0447:                boolean loaded = false;
0448:                boolean created = false;
0449:                synchronized (roomName.intern()) {
0450:                    room = rooms.get(roomName);
0451:                    if (room == null) {
0452:                        room = new LocalMUCRoom(this , roomName, router);
0453:                        // If the room is persistent load the configuration values from the DB
0454:                        try {
0455:                            // Try to load the room's configuration from the database (if the room is
0456:                            // persistent but was added to the DB after the server was started up or the
0457:                            // room may be an old room that was not present in memory)
0458:                            MUCPersistenceManager
0459:                                    .loadFromDB((LocalMUCRoom) room);
0460:                            loaded = true;
0461:                        } catch (IllegalArgumentException e) {
0462:                            // The room does not exist so check for creation permissions
0463:                            // Room creation is always allowed for sysadmin
0464:                            if (isRoomCreationRestricted()
0465:                                    && !sysadmins.contains(userjid.toBareJID())) {
0466:                                // The room creation is only allowed for certain JIDs
0467:                                if (!allowedToCreate.contains(userjid
0468:                                        .toBareJID())) {
0469:                                    // The user is not in the list of allowed JIDs to create a room so raise
0470:                                    // an exception
0471:                                    throw new NotAllowedException();
0472:                                }
0473:                            }
0474:                            room.addFirstOwner(userjid.toBareJID());
0475:                            created = true;
0476:                        }
0477:                        rooms.put(roomName, room);
0478:                    }
0479:                }
0480:                if (created) {
0481:                    // Fire event that a new room has been created
0482:                    for (MUCEventListener listener : listeners) {
0483:                        listener.roomCreated(room.getRole().getRoleAddress());
0484:                    }
0485:                }
0486:                if (loaded || created) {
0487:                    // Notify other cluster nodes that a new room is available
0488:                    CacheFactory.doClusterTask(new RoomAvailableEvent(room));
0489:                    for (MUCRole role : room.getOccupants()) {
0490:                        if (role instanceof  LocalMUCRole) {
0491:                            CacheFactory.doClusterTask(new OccupantAddedEvent(
0492:                                    room, role));
0493:                        }
0494:                    }
0495:                }
0496:                return room;
0497:            }
0498:
0499:            public MUCRoom getChatRoom(String roomName) {
0500:                boolean loaded = false;
0501:                LocalMUCRoom room = rooms.get(roomName);
0502:                if (room == null) {
0503:                    // Check if the room exists in the database and was not present in memory
0504:                    synchronized (roomName.intern()) {
0505:                        room = rooms.get(roomName);
0506:                        if (room == null) {
0507:                            room = new LocalMUCRoom(this , roomName, router);
0508:                            // If the room is persistent load the configuration values from the DB
0509:                            try {
0510:                                // Try to load the room's configuration from the database (if the room is
0511:                                // persistent but was added to the DB after the server was started up or the
0512:                                // room may be an old room that was not present in memory)
0513:                                MUCPersistenceManager
0514:                                        .loadFromDB((LocalMUCRoom) room);
0515:                                loaded = true;
0516:                                rooms.put(roomName, room);
0517:                            } catch (IllegalArgumentException e) {
0518:                                // The room does not exist so do nothing
0519:                                room = null;
0520:                            }
0521:                        }
0522:                    }
0523:                }
0524:                if (loaded) {
0525:                    // Notify other cluster nodes that a new room is available
0526:                    CacheFactory.doClusterTask(new RoomAvailableEvent(room));
0527:                }
0528:                return room;
0529:            }
0530:
0531:            public List<MUCRoom> getChatRooms() {
0532:                return new ArrayList<MUCRoom>(rooms.values());
0533:            }
0534:
0535:            public boolean hasChatRoom(String roomName) {
0536:                return getChatRoom(roomName) != null;
0537:            }
0538:
0539:            public void removeChatRoom(String roomName) {
0540:                removeChatRoom(roomName, true);
0541:            }
0542:
0543:            /**
0544:             * Notification message indicating that the specified chat room was
0545:             * removed from some other cluster member.
0546:             *
0547:             * @param roomName the name of the room removed from the cluster.
0548:             */
0549:            public void chatRoomRemoved(String roomName) {
0550:                removeChatRoom(roomName, false);
0551:            }
0552:
0553:            /**
0554:             * Notification message indicating that a chat room has been created
0555:             * in another cluster member.
0556:             *
0557:             * @param room the created room in another cluster node.
0558:             */
0559:            public void chatRoomAdded(LocalMUCRoom room) {
0560:                rooms.put(room.getName(), room);
0561:            }
0562:
0563:            private void removeChatRoom(String roomName, boolean notify) {
0564:                MUCRoom room = rooms.remove(roomName);
0565:                if (room != null) {
0566:                    totalChatTime += room.getChatLength();
0567:                    if (notify) {
0568:                        // Notify other cluster nodes that a room has been removed
0569:                        CacheFactory.doClusterTask(new RoomRemovedEvent(
0570:                                roomName));
0571:                    }
0572:                }
0573:            }
0574:
0575:            public String getServiceName() {
0576:                return chatServiceName;
0577:            }
0578:
0579:            public HistoryStrategy getHistoryStrategy() {
0580:                return historyStrategy;
0581:            }
0582:
0583:            /**
0584:             * Removes a user from all chat rooms.
0585:             *
0586:             * @param jabberID The user's normal jid, not the chat nickname jid.
0587:             */
0588:            private void removeUser(JID jabberID) {
0589:                LocalMUCUser user = users.remove(jabberID);
0590:                if (user != null) {
0591:                    for (LocalMUCRole role : user.getRoles()) {
0592:                        try {
0593:                            role.getChatRoom().leaveRoom(role);
0594:                        } catch (Exception e) {
0595:                            Log.error(e);
0596:                        }
0597:                    }
0598:                }
0599:            }
0600:
0601:            /**
0602:             * Obtain a chat user by XMPPAddress. Only returns users that are connected to this JVM.
0603:             *
0604:             * @param userjid The XMPPAddress of the user.
0605:             * @param roomName name of the room to receive the packet.
0606:             * @return The chatuser corresponding to that XMPPAddress.
0607:             */
0608:            private MUCUser getChatUser(JID userjid, String roomName) {
0609:                if (router == null) {
0610:                    throw new IllegalStateException("Not initialized");
0611:                }
0612:                LocalMUCUser user;
0613:                synchronized (userjid.toString().intern()) {
0614:                    user = users.get(userjid);
0615:                    if (user == null) {
0616:                        if (roomName != null) {
0617:                            // Check if the JID belong to a user hosted in another cluster node
0618:                            LocalMUCRoom localMUCRoom = rooms.get(roomName);
0619:                            if (localMUCRoom != null) {
0620:                                MUCRole occupant = localMUCRoom
0621:                                        .getOccupantByFullJID(userjid);
0622:                                if (occupant != null && !occupant.isLocal()) {
0623:                                    return new RemoteMUCUser(userjid,
0624:                                            localMUCRoom);
0625:                                }
0626:                            }
0627:                        }
0628:                        user = new LocalMUCUser(this , router, userjid);
0629:                        users.put(userjid, user);
0630:                    }
0631:                }
0632:                return user;
0633:            }
0634:
0635:            public Collection<MUCRole> getMUCRoles(JID user) {
0636:                List<MUCRole> userRoles = new ArrayList<MUCRole>();
0637:                for (LocalMUCRoom room : rooms.values()) {
0638:                    MUCRole role = room.getOccupantByFullJID(user);
0639:                    if (role != null) {
0640:                        userRoles.add(role);
0641:                    }
0642:                }
0643:                return userRoles;
0644:            }
0645:
0646:            public void setServiceName(String name) {
0647:                JiveGlobals.setProperty("xmpp.muc.service", name);
0648:            }
0649:
0650:            /**
0651:             * Returns the limit date after which rooms without activity will be removed from memory.
0652:             *
0653:             * @return the limit date after which rooms without activity will be removed from memory.
0654:             */
0655:            private Date getCleanupDate() {
0656:                return new Date(System.currentTimeMillis()
0657:                        - (emptyLimit * 3600000));
0658:            }
0659:
0660:            public void setKickIdleUsersTimeout(int timeout) {
0661:                if (this .user_timeout == timeout) {
0662:                    return;
0663:                }
0664:                // Cancel the existing task because the timeout has changed
0665:                if (userTimeoutTask != null) {
0666:                    userTimeoutTask.cancel();
0667:                }
0668:                this .user_timeout = timeout;
0669:                // Create a new task and schedule it with the new timeout
0670:                userTimeoutTask = new UserTimeoutTask();
0671:                timer.schedule(userTimeoutTask, user_timeout, user_timeout);
0672:                // Set the new property value
0673:                JiveGlobals.setProperty("xmpp.muc.tasks.user.timeout", Integer
0674:                        .toString(timeout));
0675:            }
0676:
0677:            public int getKickIdleUsersTimeout() {
0678:                return user_timeout;
0679:            }
0680:
0681:            public void setUserIdleTime(int idleTime) {
0682:                if (this .user_idle == idleTime) {
0683:                    return;
0684:                }
0685:                this .user_idle = idleTime;
0686:                // Set the new property value
0687:                JiveGlobals.setProperty("xmpp.muc.tasks.user.idle", Integer
0688:                        .toString(idleTime));
0689:            }
0690:
0691:            public int getUserIdleTime() {
0692:                return user_idle;
0693:            }
0694:
0695:            public void setLogConversationsTimeout(int timeout) {
0696:                if (this .log_timeout == timeout) {
0697:                    return;
0698:                }
0699:                // Cancel the existing task because the timeout has changed
0700:                if (logConversationTask != null) {
0701:                    logConversationTask.cancel();
0702:                }
0703:                this .log_timeout = timeout;
0704:                // Create a new task and schedule it with the new timeout
0705:                logConversationTask = new LogConversationTask();
0706:                timer.schedule(logConversationTask, log_timeout, log_timeout);
0707:                // Set the new property value
0708:                JiveGlobals.setProperty("xmpp.muc.tasks.log.timeout", Integer
0709:                        .toString(timeout));
0710:            }
0711:
0712:            public int getLogConversationsTimeout() {
0713:                return log_timeout;
0714:            }
0715:
0716:            public void setLogConversationBatchSize(int size) {
0717:                if (this .log_batch_size == size) {
0718:                    return;
0719:                }
0720:                this .log_batch_size = size;
0721:                // Set the new property value
0722:                JiveGlobals.setProperty("xmpp.muc.tasks.log.batchsize", Integer
0723:                        .toString(size));
0724:            }
0725:
0726:            public int getLogConversationBatchSize() {
0727:                return log_batch_size;
0728:            }
0729:
0730:            public Collection<String> getUsersAllowedToCreate() {
0731:                return allowedToCreate;
0732:            }
0733:
0734:            public Collection<String> getSysadmins() {
0735:                return sysadmins;
0736:            }
0737:
0738:            public void addSysadmin(String userJID) {
0739:                sysadmins.add(userJID.trim().toLowerCase());
0740:                // CopyOnWriteArray does not allow sorting, so do sorting in temp list.
0741:                ArrayList<String> tempList = new ArrayList<String>(sysadmins);
0742:                Collections.sort(tempList);
0743:                sysadmins = new CopyOnWriteArrayList<String>(tempList);
0744:                // Update the config.
0745:                String[] jids = new String[sysadmins.size()];
0746:                jids = sysadmins.toArray(jids);
0747:                JiveGlobals.setProperty("xmpp.muc.sysadmin.jid",
0748:                        fromArray(jids));
0749:            }
0750:
0751:            public void removeSysadmin(String userJID) {
0752:                sysadmins.remove(userJID.trim().toLowerCase());
0753:                // Update the config.
0754:                String[] jids = new String[sysadmins.size()];
0755:                jids = sysadmins.toArray(jids);
0756:                JiveGlobals.setProperty("xmpp.muc.sysadmin.jid",
0757:                        fromArray(jids));
0758:            }
0759:
0760:            /**
0761:             * Returns the flag that indicates if the service should provide information about locked rooms
0762:             * when handling service discovery requests.
0763:             *
0764:             * @return true if the service should provide information about locked rooms.
0765:             */
0766:            public boolean isAllowToDiscoverLockedRooms() {
0767:                return allowToDiscoverLockedRooms;
0768:            }
0769:
0770:            /**
0771:             * Sets the flag that indicates if the service should provide information about locked rooms
0772:             * when handling service discovery requests.
0773:             * Note: Setting this flag in false is not compliant with the spec. A user may try to join a
0774:             * locked room thinking that the room doesn't exist because the user didn't discover it before.
0775:             *
0776:             * @param allowToDiscoverLockedRooms if the service should provide information about locked
0777:             *        rooms.
0778:             */
0779:            public void setAllowToDiscoverLockedRooms(
0780:                    boolean allowToDiscoverLockedRooms) {
0781:                this .allowToDiscoverLockedRooms = allowToDiscoverLockedRooms;
0782:                JiveGlobals.setProperty("xmpp.muc.discover.locked", Boolean
0783:                        .toString(allowToDiscoverLockedRooms));
0784:            }
0785:
0786:            public boolean isRoomCreationRestricted() {
0787:                return roomCreationRestricted;
0788:            }
0789:
0790:            public void setRoomCreationRestricted(boolean roomCreationRestricted) {
0791:                this .roomCreationRestricted = roomCreationRestricted;
0792:                JiveGlobals.setProperty("xmpp.muc.create.anyone", Boolean
0793:                        .toString(roomCreationRestricted));
0794:            }
0795:
0796:            public void addUserAllowedToCreate(String userJID) {
0797:                // Update the list of allowed JIDs to create MUC rooms. Since we are updating the instance
0798:                // variable there is no need to restart the service
0799:                allowedToCreate.add(userJID.trim().toLowerCase());
0800:                // CopyOnWriteArray does not allow sorting, so do sorting in temp list.
0801:                ArrayList<String> tempList = new ArrayList<String>(
0802:                        allowedToCreate);
0803:                Collections.sort(tempList);
0804:                allowedToCreate = new CopyOnWriteArrayList<String>(tempList);
0805:                // Update the config.
0806:                String[] jids = new String[allowedToCreate.size()];
0807:                jids = allowedToCreate.toArray(jids);
0808:                JiveGlobals.setProperty("xmpp.muc.create.jid", fromArray(jids));
0809:            }
0810:
0811:            public void removeUserAllowedToCreate(String userJID) {
0812:                // Update the list of allowed JIDs to create MUC rooms. Since we are updating the instance
0813:                // variable there is no need to restart the service
0814:                allowedToCreate.remove(userJID.trim().toLowerCase());
0815:                // Update the config.
0816:                String[] jids = new String[allowedToCreate.size()];
0817:                jids = allowedToCreate.toArray(jids);
0818:                JiveGlobals.setProperty("xmpp.muc.create.jid", fromArray(jids));
0819:            }
0820:
0821:            public void initialize(XMPPServer server) {
0822:                super .initialize(server);
0823:
0824:                serviceEnabled = JiveGlobals.getBooleanProperty(
0825:                        "xmpp.muc.enabled", true);
0826:                chatServiceName = JiveGlobals.getProperty("xmpp.muc.service");
0827:                // Trigger the strategy to load itself from the context
0828:                historyStrategy.setContext("xmpp.muc.history");
0829:                // Load the list of JIDs that are sysadmins of the MUC service
0830:                String property = JiveGlobals
0831:                        .getProperty("xmpp.muc.sysadmin.jid");
0832:                String[] jids;
0833:                if (property != null) {
0834:                    jids = property.split(",");
0835:                    for (String jid : jids) {
0836:                        sysadmins.add(jid.trim().toLowerCase());
0837:                    }
0838:                }
0839:                allowToDiscoverLockedRooms = Boolean.parseBoolean(JiveGlobals
0840:                        .getProperty("xmpp.muc.discover.locked", "true"));
0841:                roomCreationRestricted = Boolean.parseBoolean(JiveGlobals
0842:                        .getProperty("xmpp.muc.create.anyone", "false"));
0843:                // Load the list of JIDs that are allowed to create a MUC room
0844:                property = JiveGlobals.getProperty("xmpp.muc.create.jid");
0845:                if (property != null) {
0846:                    jids = property.split(",");
0847:                    for (String jid : jids) {
0848:                        allowedToCreate.add(jid.trim().toLowerCase());
0849:                    }
0850:                }
0851:                String value = JiveGlobals
0852:                        .getProperty("xmpp.muc.tasks.user.timeout");
0853:                if (value != null) {
0854:                    try {
0855:                        user_timeout = Integer.parseInt(value);
0856:                    } catch (NumberFormatException e) {
0857:                        Log
0858:                                .error(
0859:                                        "Wrong number format of property xmpp.muc.tasks.user.timeout",
0860:                                        e);
0861:                    }
0862:                }
0863:                value = JiveGlobals.getProperty("xmpp.muc.tasks.user.idle");
0864:                if (value != null) {
0865:                    try {
0866:                        user_idle = Integer.parseInt(value);
0867:                    } catch (NumberFormatException e) {
0868:                        Log
0869:                                .error(
0870:                                        "Wrong number format of property xmpp.muc.tasks.user.idle",
0871:                                        e);
0872:                    }
0873:                }
0874:                value = JiveGlobals.getProperty("xmpp.muc.tasks.log.timeout");
0875:                if (value != null) {
0876:                    try {
0877:                        log_timeout = Integer.parseInt(value);
0878:                    } catch (NumberFormatException e) {
0879:                        Log
0880:                                .error(
0881:                                        "Wrong number format of property xmpp.muc.tasks.log.timeout",
0882:                                        e);
0883:                    }
0884:                }
0885:                value = JiveGlobals.getProperty("xmpp.muc.tasks.log.batchsize");
0886:                if (value != null) {
0887:                    try {
0888:                        log_batch_size = Integer.parseInt(value);
0889:                    } catch (NumberFormatException e) {
0890:                        Log
0891:                                .error(
0892:                                        "Wrong number format of property xmpp.muc.tasks.log.batchsize",
0893:                                        e);
0894:                    }
0895:                }
0896:                value = JiveGlobals.getProperty("xmpp.muc.unload.empty_days");
0897:                if (value != null) {
0898:                    try {
0899:                        emptyLimit = Integer.parseInt(value) * 24;
0900:                    } catch (NumberFormatException e) {
0901:                        Log
0902:                                .error(
0903:                                        "Wrong number format of property xmpp.muc.unload.empty_days",
0904:                                        e);
0905:                    }
0906:                }
0907:                if (chatServiceName == null) {
0908:                    chatServiceName = "conference";
0909:                }
0910:                // Run through the users every 5 minutes after a 5 minutes server startup delay (default
0911:                // values)
0912:                userTimeoutTask = new UserTimeoutTask();
0913:                timer.schedule(userTimeoutTask, user_timeout, user_timeout);
0914:                // Log the room conversations every 5 minutes after a 5 minutes server startup delay
0915:                // (default values)
0916:                logConversationTask = new LogConversationTask();
0917:                timer.schedule(logConversationTask, log_timeout, log_timeout);
0918:                // Remove unused rooms from memory
0919:                cleanupTask = new CleanupTask();
0920:                timer.schedule(cleanupTask, CLEANUP_FREQUENCY,
0921:                        CLEANUP_FREQUENCY);
0922:
0923:                routingTable = server.getRoutingTable();
0924:                router = server.getPacketRouter();
0925:                // Configure the handler of iq:register packets
0926:                registerHandler = new IQMUCRegisterHandler(this );
0927:                // Configure the handler of jabber:iq:search packets
0928:                searchHandler = new IQMUCSearchHandler(this );
0929:                // Listen to cluster events
0930:                ClusterManager.addListener(this );
0931:            }
0932:
0933:            public void start() {
0934:                super .start();
0935:                // Add the route to this service
0936:                routingTable.addComponentRoute(getAddress(), this );
0937:                ArrayList<String> params = new ArrayList<String>();
0938:                params.clear();
0939:                params.add(getServiceDomain());
0940:                Log.info(LocaleUtils.getLocalizedString("startup.starting.muc",
0941:                        params));
0942:                // Load all the persistent rooms to memory
0943:                for (LocalMUCRoom room : MUCPersistenceManager.loadRoomsFromDB(
0944:                        this , this .getCleanupDate(), router)) {
0945:                    rooms.put(room.getName().toLowerCase(), room);
0946:                }
0947:                // Add statistics
0948:                addTotalRoomStats();
0949:                addTotalOccupantsStats();
0950:                addTotalConnectedUsers();
0951:                addNumberIncomingMessages();
0952:                addNumberOutgoingMessages();
0953:            }
0954:
0955:            public void stop() {
0956:                super .stop();
0957:                // Remove the route to this service
0958:                routingTable.removeComponentRoute(getAddress());
0959:                timer.cancel();
0960:                logAllConversation();
0961:                // Remove the statistics.
0962:                StatisticsManager.getInstance().removeStatistic(roomsStatKey);
0963:                StatisticsManager.getInstance().removeStatistic(
0964:                        occupantsStatKey);
0965:                StatisticsManager.getInstance().removeStatistic(usersStatKey);
0966:                StatisticsManager.getInstance()
0967:                        .removeStatistic(incomingStatKey);
0968:                StatisticsManager.getInstance()
0969:                        .removeStatistic(outgoingStatKey);
0970:
0971:            }
0972:
0973:            public void enableService(boolean enabled, boolean persistent) {
0974:                if (isServiceEnabled() == enabled) {
0975:                    // Do nothing if the service status has not changed
0976:                    return;
0977:                }
0978:                XMPPServer server = XMPPServer.getInstance();
0979:                if (!enabled) {
0980:                    // Disable disco information
0981:                    server.getIQDiscoItemsHandler().removeServerItemsProvider(
0982:                            this );
0983:                    // Stop the service/module
0984:                    stop();
0985:                }
0986:                if (persistent) {
0987:                    JiveGlobals.setProperty("xmpp.muc.enabled", Boolean
0988:                            .toString(enabled));
0989:                }
0990:                serviceEnabled = enabled;
0991:                if (enabled) {
0992:                    // Start the service/module
0993:                    start();
0994:                    // Enable disco information
0995:                    server.getIQDiscoItemsHandler()
0996:                            .addServerItemsProvider(this );
0997:                }
0998:            }
0999:
1000:            public boolean isServiceEnabled() {
1001:                return serviceEnabled;
1002:            }
1003:
1004:            public long getTotalChatTime() {
1005:                return totalChatTime;
1006:            }
1007:
1008:            /**
1009:             * Retuns the number of existing rooms in the server (i.e. persistent or not,
1010:             * in memory or not).
1011:             *
1012:             * @return the number of existing rooms in the server.
1013:             */
1014:            public int getNumberChatRooms() {
1015:                return rooms.size();
1016:            }
1017:
1018:            /**
1019:             * Retuns the total number of occupants in all rooms in the server.
1020:             *
1021:             * @param onlyLocal true if only users connected to this JVM will be considered. Otherwise count cluster wise.
1022:             * @return the number of existing rooms in the server.
1023:             */
1024:            public int getNumberConnectedUsers(boolean onlyLocal) {
1025:                int total = 0;
1026:                for (LocalMUCUser user : users.values()) {
1027:                    if (user.isJoined()) {
1028:                        total = total + 1;
1029:                    }
1030:                }
1031:                // Add users from remote cluster nodes
1032:                if (!onlyLocal) {
1033:                    Collection<Object> results = CacheFactory
1034:                            .doSynchronousClusterTask(
1035:                                    new GetNumberConnectedUsers(), false);
1036:                    for (Object result : results) {
1037:                        if (result == null) {
1038:                            continue;
1039:                        }
1040:                        total = total + (Integer) result;
1041:                    }
1042:                }
1043:                return total;
1044:            }
1045:
1046:            /**
1047:             * Retuns the total number of users that have joined in all rooms in the server.
1048:             *
1049:             * @return the number of existing rooms in the server.
1050:             */
1051:            public int getNumberRoomOccupants() {
1052:                int total = 0;
1053:                for (MUCRoom room : rooms.values()) {
1054:                    total = total + room.getOccupantsCount();
1055:                }
1056:                return total;
1057:            }
1058:
1059:            public void logConversation(MUCRoom room, Message message,
1060:                    JID sender) {
1061:                // Only log messages that have a subject or body. Otherwise ignore it.
1062:                if (message.getSubject() != null || message.getBody() != null) {
1063:                    logQueue.add(new ConversationLogEntry(new Date(), room,
1064:                            message, sender));
1065:                }
1066:            }
1067:
1068:            public void messageBroadcastedTo(int numOccupants) {
1069:                // Increment counter of received messages that where broadcasted by one
1070:                inMessages.incrementAndGet();
1071:                // Increment counter of outgoing messages with the number of room occupants
1072:                // that received the message
1073:                outMessages.addAndGet(numOccupants);
1074:            }
1075:
1076:            public void joinedCluster() {
1077:                if (isServiceEnabled()) {
1078:                    if (!ClusterManager.isSeniorClusterMember()) {
1079:                        // Get transient rooms and persistent rooms with occupants from senior
1080:                        // cluster member and merge with local ones. If room configuration was
1081:                        // changed in both places then latest configuration will be kept
1082:                        List<RoomInfo> result = (List<RoomInfo>) CacheFactory
1083:                                .doSynchronousClusterTask(
1084:                                        new SeniorMemberRoomsRequest(),
1085:                                        ClusterManager.getSeniorClusterMember()
1086:                                                .toByteArray());
1087:                        if (result != null) {
1088:                            for (RoomInfo roomInfo : result) {
1089:                                LocalMUCRoom remoteRoom = roomInfo.getRoom();
1090:                                LocalMUCRoom localRoom = rooms.get(remoteRoom
1091:                                        .getName());
1092:                                if (localRoom == null) {
1093:                                    // Create local room with remote information
1094:                                    localRoom = remoteRoom;
1095:                                    rooms.put(remoteRoom.getName(), localRoom);
1096:                                } else {
1097:                                    // Update local room with remote information
1098:                                    localRoom.updateConfiguration(remoteRoom);
1099:                                }
1100:                                // Add remote occupants to local room
1101:                                // TODO Handle conflict of nicknames
1102:                                for (OccupantAddedEvent event : roomInfo
1103:                                        .getOccupants()) {
1104:                                    event.setSendPresence(true);
1105:                                    event.run();
1106:                                }
1107:                            }
1108:                        }
1109:                    }
1110:                }
1111:            }
1112:
1113:            public void joinedCluster(byte[] nodeID) {
1114:                if (isServiceEnabled()) {
1115:                    List<RoomInfo> result = (List<RoomInfo>) CacheFactory
1116:                            .doSynchronousClusterTask(
1117:                                    new GetNewMemberRoomsRequest(), nodeID);
1118:                    if (result != null) {
1119:                        for (RoomInfo roomInfo : result) {
1120:                            LocalMUCRoom remoteRoom = roomInfo.getRoom();
1121:                            LocalMUCRoom localRoom = rooms.get(remoteRoom
1122:                                    .getName());
1123:                            if (localRoom == null) {
1124:                                // Create local room with remote information
1125:                                localRoom = remoteRoom;
1126:                                rooms.put(remoteRoom.getName(), localRoom);
1127:                            }
1128:                            // Add remote occupants to local room
1129:                            for (OccupantAddedEvent event : roomInfo
1130:                                    .getOccupants()) {
1131:                                event.setSendPresence(true);
1132:                                event.run();
1133:                            }
1134:                        }
1135:                    }
1136:                }
1137:            }
1138:
1139:            public void leftCluster() {
1140:                // Do nothing. An unavailable presence will be created for occupants hosted in other cluster nodes.
1141:            }
1142:
1143:            public void leftCluster(byte[] nodeID) {
1144:                // Do nothing. An unavailable presence will be created for occupants hosted in the leaving cluster node.
1145:            }
1146:
1147:            public void markedAsSeniorClusterMember() {
1148:                // Do nothing
1149:            }
1150:
1151:            public Iterator<DiscoServerItem> getItems() {
1152:                // Check if the service is disabled. Info is not available when
1153:                // disabled.
1154:                if (!isServiceEnabled()) {
1155:                    return null;
1156:                }
1157:
1158:                final ArrayList<DiscoServerItem> items = new ArrayList<DiscoServerItem>();
1159:                final String name;
1160:                // Check if there is a system property that overrides the default value
1161:                String serviceName = JiveGlobals
1162:                        .getProperty("muc.service-name");
1163:                if (serviceName != null && serviceName.trim().length() > 0) {
1164:                    name = serviceName;
1165:                } else {
1166:                    // Return the default service name based on the current locale
1167:                    name = LocaleUtils.getLocalizedString("muc.service-name");
1168:                }
1169:
1170:                final DiscoServerItem item = new DiscoServerItem(new JID(
1171:                        getServiceDomain()), name, null, null, this , this );
1172:                items.add(item);
1173:                return items.iterator();
1174:            }
1175:
1176:            public Iterator<Element> getIdentities(String name, String node,
1177:                    JID senderJID) {
1178:                ArrayList<Element> identities = new ArrayList<Element>();
1179:                if (name == null && node == null) {
1180:                    // Answer the identity of the MUC service
1181:                    Element identity = DocumentHelper.createElement("identity");
1182:                    identity.addAttribute("category", "conference");
1183:                    identity.addAttribute("name", "Public Chatrooms");
1184:                    identity.addAttribute("type", "text");
1185:
1186:                    identities.add(identity);
1187:
1188:                    Element searchId = DocumentHelper.createElement("identity");
1189:                    searchId.addAttribute("category", "directory");
1190:                    searchId.addAttribute("name", "Public Chatroom Search");
1191:                    searchId.addAttribute("type", "chatroom");
1192:                    identities.add(searchId);
1193:                } else if (name != null && node == null) {
1194:                    // Answer the identity of a given room
1195:                    MUCRoom room = getChatRoom(name);
1196:                    if (room != null && canDiscoverRoom(room)) {
1197:                        Element identity = DocumentHelper
1198:                                .createElement("identity");
1199:                        identity.addAttribute("category", "conference");
1200:                        identity.addAttribute("name", room
1201:                                .getNaturalLanguageName());
1202:                        identity.addAttribute("type", "text");
1203:
1204:                        identities.add(identity);
1205:                    }
1206:                } else if (name != null && "x-roomuser-item".equals(node)) {
1207:                    // Answer reserved nickname for the sender of the disco request in the requested room
1208:                    MUCRoom room = getChatRoom(name);
1209:                    if (room != null) {
1210:                        String reservedNick = room
1211:                                .getReservedNickname(senderJID.toBareJID());
1212:                        if (reservedNick != null) {
1213:                            Element identity = DocumentHelper
1214:                                    .createElement("identity");
1215:                            identity.addAttribute("category", "conference");
1216:                            identity.addAttribute("name", reservedNick);
1217:                            identity.addAttribute("type", "text");
1218:
1219:                            identities.add(identity);
1220:                        }
1221:                    }
1222:                }
1223:                return identities.iterator();
1224:            }
1225:
1226:            public Iterator<String> getFeatures(String name, String node,
1227:                    JID senderJID) {
1228:                ArrayList<String> features = new ArrayList<String>();
1229:                if (name == null && node == null) {
1230:                    // Answer the features of the MUC service
1231:                    features.add("http://jabber.org/protocol/muc");
1232:                    features.add("http://jabber.org/protocol/disco#info");
1233:                    features.add("http://jabber.org/protocol/disco#items");
1234:                    features.add("jabber:iq:search");
1235:                    features.add(ResultSet.NAMESPACE_RESULT_SET_MANAGEMENT);
1236:                } else if (name != null && node == null) {
1237:                    // Answer the features of a given room
1238:                    MUCRoom room = getChatRoom(name);
1239:                    if (room != null && canDiscoverRoom(room)) {
1240:                        features.add("http://jabber.org/protocol/muc");
1241:                        // Always add public since only public rooms can be discovered
1242:                        features.add("muc_public");
1243:                        if (room.isMembersOnly()) {
1244:                            features.add("muc_membersonly");
1245:                        } else {
1246:                            features.add("muc_open");
1247:                        }
1248:                        if (room.isModerated()) {
1249:                            features.add("muc_moderated");
1250:                        } else {
1251:                            features.add("muc_unmoderated");
1252:                        }
1253:                        if (room.canAnyoneDiscoverJID()) {
1254:                            features.add("muc_nonanonymous");
1255:                        } else {
1256:                            features.add("muc_semianonymous");
1257:                        }
1258:                        if (room.isPasswordProtected()) {
1259:                            features.add("muc_passwordprotected");
1260:                        } else {
1261:                            features.add("muc_unsecured");
1262:                        }
1263:                        if (room.isPersistent()) {
1264:                            features.add("muc_persistent");
1265:                        } else {
1266:                            features.add("muc_temporary");
1267:                        }
1268:                    }
1269:                }
1270:                return features.iterator();
1271:            }
1272:
1273:            public XDataFormImpl getExtendedInfo(String name, String node,
1274:                    JID senderJID) {
1275:                if (name != null && node == null) {
1276:                    // Answer the extended info of a given room
1277:                    MUCRoom room = getChatRoom(name);
1278:                    if (room != null && canDiscoverRoom(room)) {
1279:                        XDataFormImpl dataForm = new XDataFormImpl(
1280:                                DataForm.TYPE_RESULT);
1281:
1282:                        XFormFieldImpl field = new XFormFieldImpl("FORM_TYPE");
1283:                        field.setType(FormField.TYPE_HIDDEN);
1284:                        field
1285:                                .addValue("http://jabber.org/protocol/muc#roominfo");
1286:                        dataForm.addField(field);
1287:
1288:                        field = new XFormFieldImpl("muc#roominfo_description");
1289:                        field.setLabel(LocaleUtils
1290:                                .getLocalizedString("muc.extended.info.desc"));
1291:                        field.addValue(room.getDescription());
1292:                        dataForm.addField(field);
1293:
1294:                        field = new XFormFieldImpl("muc#roominfo_subject");
1295:                        field
1296:                                .setLabel(LocaleUtils
1297:                                        .getLocalizedString("muc.extended.info.subject"));
1298:                        field.addValue(room.getSubject());
1299:                        dataForm.addField(field);
1300:
1301:                        field = new XFormFieldImpl("muc#roominfo_occupants");
1302:                        field
1303:                                .setLabel(LocaleUtils
1304:                                        .getLocalizedString("muc.extended.info.occupants"));
1305:                        field.addValue(Integer.toString(room
1306:                                .getOccupantsCount()));
1307:                        dataForm.addField(field);
1308:
1309:                        /*field = new XFormFieldImpl("muc#roominfo_lang");
1310:                        field.setLabel(LocaleUtils.getLocalizedString("muc.extended.info.language"));
1311:                        field.addValue(room.getLanguage());
1312:                        dataForm.addField(field);*/
1313:
1314:                        field = new XFormFieldImpl(
1315:                                "x-muc#roominfo_creationdate");
1316:                        field
1317:                                .setLabel(LocaleUtils
1318:                                        .getLocalizedString("muc.extended.info.creationdate"));
1319:                        field.addValue(dateFormatter.format(room
1320:                                .getCreationDate()));
1321:                        dataForm.addField(field);
1322:
1323:                        return dataForm;
1324:                    }
1325:                }
1326:                return null;
1327:            }
1328:
1329:            public boolean hasInfo(String name, String node, JID senderJID) {
1330:                // Check if the service is disabled. Info is not available when disabled.
1331:                if (!isServiceEnabled()) {
1332:                    return false;
1333:                }
1334:                if (name == null && node == null) {
1335:                    // We always have info about the MUC service
1336:                    return true;
1337:                } else if (name != null && node == null) {
1338:                    // We only have info if the room exists
1339:                    return hasChatRoom(name);
1340:                } else if (name != null && "x-roomuser-item".equals(node)) {
1341:                    // We always have info about reserved names as long as the room exists
1342:                    return hasChatRoom(name);
1343:                }
1344:                return false;
1345:            }
1346:
1347:            public Iterator<DiscoItem> getItems(String name, String node,
1348:                    JID senderJID) {
1349:                // Check if the service is disabled. Info is not available when disabled.
1350:                if (!isServiceEnabled()) {
1351:                    return null;
1352:                }
1353:                List<DiscoItem> answer = new ArrayList<DiscoItem>();
1354:                if (name == null && node == null) {
1355:                    // Answer all the public rooms as items
1356:                    for (MUCRoom room : rooms.values()) {
1357:                        if (canDiscoverRoom(room)) {
1358:                            answer.add(new DiscoItem(room.getRole()
1359:                                    .getRoleAddress(), room
1360:                                    .getNaturalLanguageName(), null, null));
1361:                        }
1362:                    }
1363:                } else if (name != null && node == null) {
1364:                    // Answer the room occupants as items if that info is publicly available
1365:                    MUCRoom room = getChatRoom(name);
1366:                    if (room != null && canDiscoverRoom(room)) {
1367:                        for (MUCRole role : room.getOccupants()) {
1368:                            // TODO Should we filter occupants that are invisible (presence is not broadcasted)?
1369:                            answer.add(new DiscoItem(role.getRoleAddress(),
1370:                                    null, null, null));
1371:                        }
1372:                    }
1373:                }
1374:                return answer.iterator();
1375:            }
1376:
1377:            private boolean canDiscoverRoom(MUCRoom room) {
1378:                // Check if locked rooms may be discovered
1379:                if (!allowToDiscoverLockedRooms && room.isLocked()) {
1380:                    return false;
1381:                }
1382:                return room.isPublicRoom();
1383:            }
1384:
1385:            public void addListener(MUCEventListener listener) {
1386:                listeners.add(listener);
1387:            }
1388:
1389:            public void removeListener(MUCEventListener listener) {
1390:                listeners.remove(listener);
1391:            }
1392:
1393:            void fireOccupantJoined(JID roomJID, JID user, String nickname) {
1394:                for (MUCEventListener listener : listeners) {
1395:                    listener.occupantJoined(roomJID, user, nickname);
1396:                }
1397:            }
1398:
1399:            void fireOccupantLeft(JID roomJID, JID user) {
1400:                for (MUCEventListener listener : listeners) {
1401:                    listener.occupantLeft(roomJID, user);
1402:                }
1403:            }
1404:
1405:            void fireNicknameChanged(JID roomJID, JID user, String oldNickname,
1406:                    String newNickname) {
1407:                for (MUCEventListener listener : listeners) {
1408:                    listener.nicknameChanged(roomJID, user, oldNickname,
1409:                            newNickname);
1410:                }
1411:            }
1412:
1413:            void fireMessageReceived(JID roomJID, JID user, String nickname,
1414:                    Message message) {
1415:                for (MUCEventListener listener : listeners) {
1416:                    listener.messageReceived(roomJID, user, nickname, message);
1417:                }
1418:            }
1419:
1420:            void fireRoomDestroyed(JID roomJID) {
1421:                for (MUCEventListener listener : listeners) {
1422:                    listener.roomDestroyed(roomJID);
1423:                }
1424:            }
1425:
1426:            /**
1427:             * Converts an array to a comma-delimitted String.
1428:             *
1429:             * @param array the array.
1430:             * @return a comma delimtted String of the array values.
1431:             */
1432:            private static String fromArray(String[] array) {
1433:                StringBuilder buf = new StringBuilder();
1434:                for (int i = 0; i < array.length; i++) {
1435:                    buf.append(array[i]);
1436:                    if (i != array.length - 1) {
1437:                        buf.append(",");
1438:                    }
1439:                }
1440:                return buf.toString();
1441:            }
1442:
1443:            /****************** Statistics code ************************/
1444:            private void addTotalRoomStats() {
1445:                // Register a statistic.
1446:                Statistic statistic = new Statistic() {
1447:                    public String getName() {
1448:                        return LocaleUtils
1449:                                .getLocalizedString("muc.stats.active_group_chats.name");
1450:                    }
1451:
1452:                    public Type getStatType() {
1453:                        return Type.count;
1454:                    }
1455:
1456:                    public String getDescription() {
1457:                        return LocaleUtils
1458:                                .getLocalizedString("muc.stats.active_group_chats.desc");
1459:                    }
1460:
1461:                    public String getUnits() {
1462:                        return LocaleUtils
1463:                                .getLocalizedString("muc.stats.active_group_chats.units");
1464:                    }
1465:
1466:                    public double sample() {
1467:                        return getNumberChatRooms();
1468:                    }
1469:
1470:                    public boolean isPartialSample() {
1471:                        return false;
1472:                    }
1473:                };
1474:                StatisticsManager.getInstance().addStatistic(roomsStatKey,
1475:                        statistic);
1476:            }
1477:
1478:            private void addTotalOccupantsStats() {
1479:                // Register a statistic.
1480:                Statistic statistic = new Statistic() {
1481:                    public String getName() {
1482:                        return LocaleUtils
1483:                                .getLocalizedString("muc.stats.occupants.name");
1484:                    }
1485:
1486:                    public Type getStatType() {
1487:                        return Type.count;
1488:                    }
1489:
1490:                    public String getDescription() {
1491:                        return LocaleUtils
1492:                                .getLocalizedString("muc.stats.occupants.description");
1493:                    }
1494:
1495:                    public String getUnits() {
1496:                        return LocaleUtils
1497:                                .getLocalizedString("muc.stats.occupants.label");
1498:                    }
1499:
1500:                    public double sample() {
1501:                        return getNumberRoomOccupants();
1502:                    }
1503:
1504:                    public boolean isPartialSample() {
1505:                        return false;
1506:                    }
1507:                };
1508:                StatisticsManager.getInstance().addStatistic(occupantsStatKey,
1509:                        statistic);
1510:            }
1511:
1512:            private void addTotalConnectedUsers() {
1513:                // Register a statistic.
1514:                Statistic statistic = new Statistic() {
1515:                    public String getName() {
1516:                        return LocaleUtils
1517:                                .getLocalizedString("muc.stats.users.name");
1518:                    }
1519:
1520:                    public Type getStatType() {
1521:                        return Type.count;
1522:                    }
1523:
1524:                    public String getDescription() {
1525:                        return LocaleUtils
1526:                                .getLocalizedString("muc.stats.users.description");
1527:                    }
1528:
1529:                    public String getUnits() {
1530:                        return LocaleUtils
1531:                                .getLocalizedString("muc.stats.users.label");
1532:                    }
1533:
1534:                    public double sample() {
1535:                        return getNumberConnectedUsers(false);
1536:                    }
1537:
1538:                    public boolean isPartialSample() {
1539:                        return false;
1540:                    }
1541:                };
1542:                StatisticsManager.getInstance().addStatistic(usersStatKey,
1543:                        statistic);
1544:            }
1545:
1546:            private void addNumberIncomingMessages() {
1547:                // Register a statistic.
1548:                Statistic statistic = new Statistic() {
1549:                    public String getName() {
1550:                        return LocaleUtils
1551:                                .getLocalizedString("muc.stats.incoming.name");
1552:                    }
1553:
1554:                    public Type getStatType() {
1555:                        return Type.rate;
1556:                    }
1557:
1558:                    public String getDescription() {
1559:                        return LocaleUtils
1560:                                .getLocalizedString("muc.stats.incoming.description");
1561:                    }
1562:
1563:                    public String getUnits() {
1564:                        return LocaleUtils
1565:                                .getLocalizedString("muc.stats.incoming.label");
1566:                    }
1567:
1568:                    public double sample() {
1569:                        return inMessages.getAndSet(0);
1570:                    }
1571:
1572:                    public boolean isPartialSample() {
1573:                        // Get this value from the other cluster nodes
1574:                        return true;
1575:                    }
1576:                };
1577:                StatisticsManager.getInstance().addMultiStatistic(
1578:                        incomingStatKey, trafficStatGroup, statistic);
1579:            }
1580:
1581:            private void addNumberOutgoingMessages() {
1582:                // Register a statistic.
1583:                Statistic statistic = new Statistic() {
1584:                    public String getName() {
1585:                        return LocaleUtils
1586:                                .getLocalizedString("muc.stats.outgoing.name");
1587:                    }
1588:
1589:                    public Type getStatType() {
1590:                        return Type.rate;
1591:                    }
1592:
1593:                    public String getDescription() {
1594:                        return LocaleUtils
1595:                                .getLocalizedString("muc.stats.outgoing.description");
1596:                    }
1597:
1598:                    public String getUnits() {
1599:                        return LocaleUtils
1600:                                .getLocalizedString("muc.stats.outgoing.label");
1601:                    }
1602:
1603:                    public double sample() {
1604:                        return outMessages.getAndSet(0);
1605:                    }
1606:
1607:                    public boolean isPartialSample() {
1608:                        // Each cluster node knows the total across the cluster
1609:                        return false;
1610:                    }
1611:                };
1612:                StatisticsManager.getInstance().addMultiStatistic(
1613:                        outgoingStatKey, trafficStatGroup, statistic);
1614:            }
1615:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.