Source Code Cross Referenced for LocalMUCRoom.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$
0003:         * $Revision: 3158 $
0004:         * $Date: 2005-12-04 22:55:49 -0300 (Sun, 04 Dec 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.Element;
0013:        import org.jivesoftware.database.SequenceManager;
0014:        import org.jivesoftware.openfire.PacketRouter;
0015:        import org.jivesoftware.openfire.XMPPServer;
0016:        import org.jivesoftware.openfire.auth.UnauthorizedException;
0017:        import org.jivesoftware.openfire.cluster.NodeID;
0018:        import org.jivesoftware.openfire.muc.*;
0019:        import org.jivesoftware.openfire.muc.cluster.*;
0020:        import org.jivesoftware.openfire.user.UserAlreadyExistsException;
0021:        import org.jivesoftware.openfire.user.UserNotFoundException;
0022:        import org.jivesoftware.util.*;
0023:        import org.jivesoftware.util.cache.CacheFactory;
0024:        import org.jivesoftware.util.cache.ExternalizableUtil;
0025:        import org.xmpp.packet.*;
0026:
0027:        import java.io.IOException;
0028:        import java.io.ObjectInput;
0029:        import java.io.ObjectOutput;
0030:        import java.util.*;
0031:        import java.util.concurrent.ConcurrentHashMap;
0032:        import java.util.concurrent.CopyOnWriteArrayList;
0033:        import java.util.concurrent.locks.ReadWriteLock;
0034:        import java.util.concurrent.locks.ReentrantReadWriteLock;
0035:
0036:        /**
0037:         * Implementation of a chatroom that is being hosted by this JVM. A LocalMUCRoom could represent
0038:         * a persistent room which means that its configuration will be maintained in synch with its
0039:         * representation in the database.<p>
0040:         *
0041:         * When running in a cluster each cluster node will have its own copy of local rooms. Persistent
0042:         * rooms will be loaded by each cluster node when starting up. Not persistent rooms will be copied
0043:         * from the senior cluster member. All room occupants will be copied from the senior cluster member
0044:         * too.
0045:         * 
0046:         * @author Gaston Dombiak
0047:         */
0048:        public class LocalMUCRoom implements  MUCRoom {
0049:
0050:            /**
0051:             * The server hosting the room.
0052:             */
0053:            private MultiUserChatServerImpl server;
0054:
0055:            /**
0056:             * The occupants of the room accessible by the occupants nickname.
0057:             */
0058:            private Map<String, MUCRole> occupants = new ConcurrentHashMap<String, MUCRole>();
0059:
0060:            /**
0061:             * The occupants of the room accessible by the occupants bare JID.
0062:             */
0063:            private Map<String, List<MUCRole>> occupantsByBareJID = new ConcurrentHashMap<String, List<MUCRole>>();
0064:
0065:            /**
0066:             * The occupants of the room accessible by the occupants full JID.
0067:             */
0068:            private Map<JID, MUCRole> occupantsByFullJID = new ConcurrentHashMap<JID, MUCRole>();
0069:
0070:            /**
0071:             * The name of the room.
0072:             */
0073:            private String name;
0074:
0075:            /**
0076:             * A lock to protect the room occupants.
0077:             */
0078:            ReadWriteLock lock = new ReentrantReadWriteLock();
0079:
0080:            /**
0081:             * The role of the room itself.
0082:             */
0083:            private MUCRole role = new RoomRole(this );
0084:
0085:            /**
0086:             * The router used to send packets for the room.
0087:             */
0088:            private PacketRouter router;
0089:
0090:            /**
0091:             * The start time of the chat.
0092:             */
0093:            long startTime;
0094:
0095:            /**
0096:             * The end time of the chat.
0097:             */
0098:            long endTime;
0099:
0100:            /**
0101:             * After a room has been destroyed it may remain in memory but it won't be possible to use it.
0102:             * When a room is destroyed it is immediately removed from the MultiUserChatServer but it's
0103:             * possible that while the room was being destroyed it was being used by another thread so we
0104:             * need to protect the room under these rare circumstances.
0105:             */
0106:            boolean isDestroyed = false;
0107:
0108:            /**
0109:             * ChatRoomHistory object.
0110:             */
0111:            private MUCRoomHistory roomHistory;
0112:
0113:            /**
0114:             * Time when the room was locked. A value of zero means that the room is unlocked.
0115:             */
0116:            private long lockedTime;
0117:
0118:            /**
0119:             * List of chatroom's owner. The list contains only bare jid.
0120:             */
0121:            List<String> owners = new CopyOnWriteArrayList<String>();
0122:
0123:            /**
0124:             * List of chatroom's admin. The list contains only bare jid.
0125:             */
0126:            List<String> admins = new CopyOnWriteArrayList<String>();
0127:
0128:            /**
0129:             * List of chatroom's members. The list contains only bare jid.
0130:             */
0131:            private Map<String, String> members = new ConcurrentHashMap<String, String>();
0132:
0133:            /**
0134:             * List of chatroom's outcast. The list contains only bare jid of not allowed users.
0135:             */
0136:            private List<String> outcasts = new CopyOnWriteArrayList<String>();
0137:
0138:            /**
0139:             * The natural language name of the room.
0140:             */
0141:            private String naturalLanguageName;
0142:
0143:            /**
0144:             * Description of the room. The owner can change the description using the room configuration
0145:             * form.
0146:             */
0147:            private String description;
0148:
0149:            /**
0150:             * Indicates if occupants are allowed to change the subject of the room. 
0151:             */
0152:            private boolean canOccupantsChangeSubject = JiveGlobals
0153:                    .getBooleanProperty("muc.room.canOccupantsChangeSubject",
0154:                            false);
0155:
0156:            /**
0157:             * Maximum number of occupants that could be present in the room. If the limit's been reached
0158:             * and a user tries to join, a not-allowed error will be returned.
0159:             */
0160:            private int maxUsers = 30;
0161:
0162:            /**
0163:             * List of roles of which presence will be broadcasted to the rest of the occupants. This
0164:             * feature is useful for implementing "invisible" occupants.
0165:             */
0166:            private List<String> rolesToBroadcastPresence = new ArrayList<String>();
0167:
0168:            /**
0169:             * A public room means that the room is searchable and visible. This means that the room can be
0170:             * located using disco requests.
0171:             */
0172:            private boolean publicRoom = JiveGlobals.getBooleanProperty(
0173:                    "muc.room.publicRoom", true);
0174:
0175:            /**
0176:             * Persistent rooms are saved to the database to make sure that rooms configurations can be
0177:             * restored in case the server goes down.
0178:             */
0179:            private boolean persistent = JiveGlobals.getBooleanProperty(
0180:                    "muc.room.persistent", false);
0181:
0182:            /**
0183:             * Moderated rooms enable only participants to speak. Users that join the room and aren't
0184:             * participants can't speak (they are just visitors).
0185:             */
0186:            private boolean moderated = JiveGlobals.getBooleanProperty(
0187:                    "muc.room.moderated", false);
0188:
0189:            /**
0190:             * A room is considered members-only if an invitation is required in order to enter the room.
0191:             * Any user that is not a member of the room won't be able to join the room unless the user
0192:             * decides to register with the room (thus becoming a member).
0193:             */
0194:            private boolean membersOnly = JiveGlobals.getBooleanProperty(
0195:                    "muc.room.membersOnly", false);
0196:
0197:            /**
0198:             * Some rooms may restrict the occupants that are able to send invitations. Sending an 
0199:             * invitation in a members-only room adds the invitee to the members list.
0200:             */
0201:            private boolean canOccupantsInvite = JiveGlobals
0202:                    .getBooleanProperty("muc.room.canOccupantsInvite", false);
0203:
0204:            /**
0205:             * The password that every occupant should provide in order to enter the room.
0206:             */
0207:            private String password = null;
0208:
0209:            /**
0210:             * Every presence packet can include the JID of every occupant unless the owner deactives this
0211:             * configuration. 
0212:             */
0213:            private boolean canAnyoneDiscoverJID = JiveGlobals
0214:                    .getBooleanProperty("muc.room.canAnyoneDiscoverJID", true);
0215:
0216:            /**
0217:             * Enables the logging of the conversation. The conversation in the room will be saved to the
0218:             * database.
0219:             */
0220:            private boolean logEnabled = JiveGlobals.getBooleanProperty(
0221:                    "muc.room.logEnabled", false);
0222:
0223:            /**
0224:             * Enables the logging of the conversation. The conversation in the room will be saved to the
0225:             * database.
0226:             */
0227:            private boolean loginRestrictedToNickname = JiveGlobals
0228:                    .getBooleanProperty("muc.room.loginRestrictedToNickname",
0229:                            false);
0230:
0231:            /**
0232:             * Enables the logging of the conversation. The conversation in the room will be saved to the
0233:             * database.
0234:             */
0235:            private boolean canChangeNickname = JiveGlobals.getBooleanProperty(
0236:                    "muc.room.canChangeNickname", true);
0237:
0238:            /**
0239:             * Enables the logging of the conversation. The conversation in the room will be saved to the
0240:             * database.
0241:             */
0242:            private boolean registrationEnabled = JiveGlobals
0243:                    .getBooleanProperty("muc.room.registrationEnabled", true);
0244:
0245:            /**
0246:             * Internal component that handles IQ packets sent by the room owners.
0247:             */
0248:            private IQOwnerHandler iqOwnerHandler;
0249:
0250:            /**
0251:             * Internal component that handles IQ packets sent by moderators, admins and owners.
0252:             */
0253:            private IQAdminHandler iqAdminHandler;
0254:
0255:            /**
0256:             * The last known subject of the room. This information is used to respond disco requests. The
0257:             * MUCRoomHistory class holds the history of the room together with the last message that set
0258:             * the room's subject.
0259:             */
0260:            private String subject = "";
0261:
0262:            /**
0263:             * The ID of the room. If the room is temporary and does not log its conversation then the value
0264:             * will always be -1. Otherwise a value will be obtained from the database.
0265:             */
0266:            private long roomID = -1;
0267:
0268:            /**
0269:             * The date when the room was created.
0270:             */
0271:            private Date creationDate;
0272:
0273:            /**
0274:             * The last date when the room's configuration was modified.
0275:             */
0276:            private Date modificationDate;
0277:
0278:            /**
0279:             * The date when the last occupant left the room. A null value means that there are occupants
0280:             * in the room at the moment.
0281:             */
0282:            private Date emptyDate;
0283:
0284:            /**
0285:             * Indicates if the room is present in the database.
0286:             */
0287:            private boolean savedToDB = false;
0288:
0289:            /**
0290:             * Do not use this constructor. It was added to implement the Externalizable
0291:             * interface required to work inside of a cluster.
0292:             */
0293:            public LocalMUCRoom() {
0294:            }
0295:
0296:            /**
0297:             * Create a new chat room.
0298:             * 
0299:             * @param chatserver the server hosting the room.
0300:             * @param roomname the name of the room.
0301:             * @param packetRouter the router for sending packets from the room.
0302:             */
0303:            LocalMUCRoom(MultiUserChatServer chatserver, String roomname,
0304:                    PacketRouter packetRouter) {
0305:                this .server = (MultiUserChatServerImpl) chatserver;
0306:                this .name = roomname;
0307:                this .naturalLanguageName = roomname;
0308:                this .description = roomname;
0309:                this .router = packetRouter;
0310:                this .startTime = System.currentTimeMillis();
0311:                this .creationDate = new Date(startTime);
0312:                this .modificationDate = new Date(startTime);
0313:                this .emptyDate = new Date(startTime);
0314:                // TODO Allow to set the history strategy from the configuration form?
0315:                roomHistory = new MUCRoomHistory(this , new HistoryStrategy(
0316:                        server.getHistoryStrategy()));
0317:                this .iqOwnerHandler = new IQOwnerHandler(this , packetRouter);
0318:                this .iqAdminHandler = new IQAdminHandler(this , packetRouter);
0319:                // No one can join the room except the room's owner
0320:                this .lockedTime = startTime;
0321:                // Set the default roles for which presence is broadcast
0322:                rolesToBroadcastPresence.add("moderator");
0323:                rolesToBroadcastPresence.add("participant");
0324:                rolesToBroadcastPresence.add("visitor");
0325:            }
0326:
0327:            public String getName() {
0328:                return name;
0329:            }
0330:
0331:            public long getID() {
0332:                if (isPersistent() || isLogEnabled()) {
0333:                    if (roomID == -1) {
0334:                        roomID = SequenceManager.nextID(JiveConstants.MUC_ROOM);
0335:                    }
0336:                }
0337:                return roomID;
0338:            }
0339:
0340:            public void setID(long roomID) {
0341:                this .roomID = roomID;
0342:            }
0343:
0344:            public Date getCreationDate() {
0345:                return creationDate;
0346:            }
0347:
0348:            public void setCreationDate(Date creationDate) {
0349:                this .creationDate = creationDate;
0350:            }
0351:
0352:            public Date getModificationDate() {
0353:                return modificationDate;
0354:            }
0355:
0356:            public void setModificationDate(Date modificationDate) {
0357:                this .modificationDate = modificationDate;
0358:            }
0359:
0360:            public void setEmptyDate(Date emptyDate) {
0361:                // Do nothing if old value is same as new value
0362:                if (this .emptyDate == emptyDate) {
0363:                    return;
0364:                }
0365:                this .emptyDate = emptyDate;
0366:                MUCPersistenceManager.updateRoomEmptyDate(this );
0367:            }
0368:
0369:            public Date getEmptyDate() {
0370:                return this .emptyDate;
0371:            }
0372:
0373:            public MUCRole getRole() {
0374:                return role;
0375:            }
0376:
0377:            public MUCRole getOccupant(String nickname)
0378:                    throws UserNotFoundException {
0379:                if (nickname == null) {
0380:                    throw new UserNotFoundException();
0381:                }
0382:                MUCRole role = occupants.get(nickname.toLowerCase());
0383:                if (role != null) {
0384:                    return role;
0385:                }
0386:                throw new UserNotFoundException();
0387:            }
0388:
0389:            public List<MUCRole> getOccupantsByBareJID(String jid)
0390:                    throws UserNotFoundException {
0391:                List<MUCRole> roles = occupantsByBareJID.get(jid);
0392:                if (roles != null && !roles.isEmpty()) {
0393:                    return Collections.unmodifiableList(roles);
0394:                }
0395:                throw new UserNotFoundException();
0396:            }
0397:
0398:            public MUCRole getOccupantByFullJID(JID jid) {
0399:                MUCRole role = occupantsByFullJID.get(jid);
0400:                if (role != null) {
0401:                    return role;
0402:                }
0403:                return null;
0404:            }
0405:
0406:            public Collection<MUCRole> getOccupants() {
0407:                return Collections.unmodifiableCollection(occupants.values());
0408:            }
0409:
0410:            public int getOccupantsCount() {
0411:                return occupants.size();
0412:            }
0413:
0414:            public boolean hasOccupant(String nickname) {
0415:                return occupants.containsKey(nickname.toLowerCase());
0416:            }
0417:
0418:            public String getReservedNickname(String bareJID) {
0419:                String answer = members.get(bareJID);
0420:                if (answer == null || answer.trim().length() == 0) {
0421:                    return null;
0422:                }
0423:                return answer;
0424:            }
0425:
0426:            public MUCRole.Affiliation getAffiliation(String bareJID) {
0427:                if (owners.contains(bareJID)) {
0428:                    return MUCRole.Affiliation.owner;
0429:                } else if (admins.contains(bareJID)) {
0430:                    return MUCRole.Affiliation.admin;
0431:                } else if (members.containsKey(bareJID)) {
0432:                    return MUCRole.Affiliation.member;
0433:                } else if (outcasts.contains(bareJID)) {
0434:                    return MUCRole.Affiliation.outcast;
0435:                }
0436:                return MUCRole.Affiliation.none;
0437:            }
0438:
0439:            public LocalMUCRole joinRoom(String nickname, String password,
0440:                    HistoryRequest historyRequest, LocalMUCUser user,
0441:                    Presence presence) throws UnauthorizedException,
0442:                    UserAlreadyExistsException, RoomLockedException,
0443:                    ForbiddenException, RegistrationRequiredException,
0444:                    ConflictException, ServiceUnavailableException,
0445:                    NotAcceptableException {
0446:                LocalMUCRole joinRole = null;
0447:                lock.writeLock().lock();
0448:                try {
0449:                    // If the room has a limit of max user then check if the limit has been reached
0450:                    if (isDestroyed
0451:                            || (getMaxUsers() > 0 && getOccupantsCount() >= getMaxUsers())) {
0452:                        throw new ServiceUnavailableException();
0453:                    }
0454:                    boolean isOwner = owners.contains(user.getAddress()
0455:                            .toBareJID());
0456:                    // If the room is locked and this user is not an owner raise a RoomLocked exception
0457:                    if (isLocked()) {
0458:                        if (!isOwner) {
0459:                            throw new RoomLockedException();
0460:                        }
0461:                    }
0462:                    // If the user is already in the room raise a UserAlreadyExists exception
0463:                    if (occupants.containsKey(nickname.toLowerCase())) {
0464:                        throw new UserAlreadyExistsException();
0465:                    }
0466:                    // If the room is password protected and the provided password is incorrect raise a
0467:                    // Unauthorized exception
0468:                    if (isPasswordProtected()) {
0469:                        if (password == null || !password.equals(getPassword())) {
0470:                            throw new UnauthorizedException();
0471:                        }
0472:                    }
0473:                    // If another user attempts to join the room with a nickname reserved by the first user
0474:                    // raise a ConflictException
0475:                    if (members.containsValue(nickname)) {
0476:                        if (!nickname.equals(members.get(user.getAddress()
0477:                                .toBareJID()))) {
0478:                            throw new ConflictException();
0479:                        }
0480:                    }
0481:                    if (isLoginRestrictedToNickname()) {
0482:                        String reservedNickname = members.get(user.getAddress()
0483:                                .toBareJID());
0484:                        if (reservedNickname != null
0485:                                && !nickname.equals(reservedNickname)) {
0486:                            throw new NotAcceptableException();
0487:                        }
0488:                    }
0489:
0490:                    // Set the corresponding role based on the user's affiliation
0491:                    MUCRole.Role role;
0492:                    MUCRole.Affiliation affiliation;
0493:                    if (isOwner) {
0494:                        // The user is an owner. Set the role and affiliation accordingly.
0495:                        role = MUCRole.Role.moderator;
0496:                        affiliation = MUCRole.Affiliation.owner;
0497:                    } else if (server.getSysadmins().contains(
0498:                            user.getAddress().toBareJID())) {
0499:                        // The user is a system administrator of the MUC service. Treat him as an owner 
0500:                        // although he won't appear in the list of owners
0501:                        role = MUCRole.Role.moderator;
0502:                        affiliation = MUCRole.Affiliation.owner;
0503:                    } else if (admins.contains(user.getAddress().toBareJID())) {
0504:                        // The user is an admin. Set the role and affiliation accordingly.
0505:                        role = MUCRole.Role.moderator;
0506:                        affiliation = MUCRole.Affiliation.admin;
0507:                    } else if (members.containsKey(user.getAddress()
0508:                            .toBareJID())) {
0509:                        // The user is a member. Set the role and affiliation accordingly.
0510:                        role = MUCRole.Role.participant;
0511:                        affiliation = MUCRole.Affiliation.member;
0512:                    } else if (outcasts.contains(user.getAddress().toBareJID())) {
0513:                        // The user is an outcast. Raise a "Forbidden" exception.
0514:                        throw new ForbiddenException();
0515:                    } else {
0516:                        // The user has no affiliation (i.e. NONE). Set the role accordingly.
0517:                        if (isMembersOnly()) {
0518:                            // The room is members-only and the user is not a member. Raise a
0519:                            // "Registration Required" exception.
0520:                            throw new RegistrationRequiredException();
0521:                        }
0522:                        role = (isModerated() ? MUCRole.Role.visitor
0523:                                : MUCRole.Role.participant);
0524:                        affiliation = MUCRole.Affiliation.none;
0525:                    }
0526:                    // Create a new role for this user in this room
0527:                    joinRole = new LocalMUCRole(server, this , nickname, role,
0528:                            affiliation, user, presence, router);
0529:                    // Add the new user as an occupant of this room
0530:                    occupants.put(nickname.toLowerCase(), joinRole);
0531:                    // Update the tables of occupants based on the bare and full JID
0532:                    List<MUCRole> list = occupantsByBareJID.get(user
0533:                            .getAddress().toBareJID());
0534:                    if (list == null) {
0535:                        list = new ArrayList<MUCRole>();
0536:                        occupantsByBareJID.put(user.getAddress().toBareJID(),
0537:                                list);
0538:                    }
0539:                    list.add(joinRole);
0540:                    occupantsByFullJID.put(user.getAddress(), joinRole);
0541:                } finally {
0542:                    lock.writeLock().unlock();
0543:                }
0544:                // Notify other cluster nodes that a new occupant joined the room
0545:                CacheFactory.doClusterTask(new OccupantAddedEvent(this ,
0546:                        joinRole));
0547:
0548:                // Send presence of existing occupants to new occupant
0549:                sendInitialPresences(joinRole);
0550:                // It is assumed that the room is new based on the fact that it's locked and
0551:                // that it was locked when it was created.
0552:                boolean isRoomNew = isLocked()
0553:                        && creationDate.getTime() == lockedTime;
0554:                try {
0555:                    // Send the presence of this new occupant to existing occupants
0556:                    Presence joinPresence = joinRole.getPresence().createCopy();
0557:                    if (isRoomNew) {
0558:                        Element frag = joinPresence.getChildElement("x",
0559:                                "http://jabber.org/protocol/muc#user");
0560:                        frag.addElement("status").addAttribute("code", "201");
0561:                    }
0562:                    broadcastPresence(joinPresence);
0563:                } catch (Exception e) {
0564:                    Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
0565:                }
0566:                // If the room has just been created send the "room locked until configuration is
0567:                // confirmed" message
0568:                if (isRoomNew) {
0569:                    Message message = new Message();
0570:                    message.setType(Message.Type.groupchat);
0571:                    message.setBody(LocaleUtils.getLocalizedString("muc.new"));
0572:                    message.setFrom(role.getRoleAddress());
0573:                    joinRole.send(message);
0574:                } else if (isLocked()) {
0575:                    // Warn the owner that the room is locked but it's not new
0576:                    Message message = new Message();
0577:                    message.setType(Message.Type.groupchat);
0578:                    message.setBody(LocaleUtils
0579:                            .getLocalizedString("muc.locked"));
0580:                    message.setFrom(role.getRoleAddress());
0581:                    joinRole.send(message);
0582:                } else if (canAnyoneDiscoverJID()) {
0583:                    // Warn the new occupant that the room is non-anonymous (i.e. his JID will be
0584:                    // public)
0585:                    Message message = new Message();
0586:                    message.setType(Message.Type.groupchat);
0587:                    message.setBody(LocaleUtils
0588:                            .getLocalizedString("muc.warnnonanonymous"));
0589:                    message.setFrom(role.getRoleAddress());
0590:                    Element frag = message.addChildElement("x",
0591:                            "http://jabber.org/protocol/muc#user");
0592:                    frag.addElement("status").addAttribute("code", "100");
0593:                    joinRole.send(message);
0594:                }
0595:                if (historyRequest == null) {
0596:                    Iterator history = roomHistory.getMessageHistory();
0597:                    while (history.hasNext()) {
0598:                        joinRole.send((Message) history.next());
0599:                    }
0600:                } else {
0601:                    historyRequest.sendHistory(joinRole, roomHistory);
0602:                }
0603:                // Update the date when the last occupant left the room
0604:                setEmptyDate(null);
0605:                // Fire event that occupant joined the room
0606:                server.fireOccupantJoined(getRole().getRoleAddress(), user
0607:                        .getAddress(), joinRole.getNickname());
0608:                return joinRole;
0609:            }
0610:
0611:            /**
0612:             * Sends presence of existing occupants to new occupant.
0613:             *
0614:             * @param joinRole the role of the new occupant in the room.
0615:             */
0616:            private void sendInitialPresences(LocalMUCRole joinRole) {
0617:                for (MUCRole occupant : occupants.values()) {
0618:                    if (occupant == joinRole) {
0619:                        continue;
0620:                    }
0621:                    Presence occupantPresence = occupant.getPresence();
0622:                    // Skip to the next occupant if we cannot send presence of this occupant
0623:                    if (hasToCheckRoleToBroadcastPresence()) {
0624:                        Element frag = occupantPresence.getChildElement("x",
0625:                                "http://jabber.org/protocol/muc#user");
0626:                        // Check if we can broadcast the presence for this role
0627:                        if (!canBroadcastPresence(frag.element("item")
0628:                                .attributeValue("role"))) {
0629:                            continue;
0630:                        }
0631:                    }
0632:                    // Don't include the occupant's JID if the room is semi-anon and the new occupant
0633:                    // is not a moderator
0634:                    if (!canAnyoneDiscoverJID()
0635:                            && MUCRole.Role.moderator != joinRole.getRole()) {
0636:                        occupantPresence = occupantPresence.createCopy();
0637:                        Element frag = occupantPresence.getChildElement("x",
0638:                                "http://jabber.org/protocol/muc#user");
0639:                        frag.element("item").addAttribute("jid", null);
0640:                    }
0641:                    joinRole.send(occupantPresence);
0642:                }
0643:            }
0644:
0645:            public void occupantAdded(OccupantAddedEvent event) {
0646:                // Do not add new occupant with one with same nickname already exists 
0647:                if (occupants.containsKey(event.getNickname().toLowerCase())) {
0648:                    // TODO Handle conflict of nicknames
0649:                    return;
0650:                }
0651:                // Create a proxy for the occupant that joined the room from another cluster node
0652:                RemoteMUCRole joinRole = new RemoteMUCRole(server, event);
0653:                // Add the new user as an occupant of this room
0654:                occupants.put(event.getNickname().toLowerCase(), joinRole);
0655:                // Update the tables of occupants based on the bare and full JID
0656:                List<MUCRole> list = occupantsByBareJID.get(event
0657:                        .getUserAddress().toBareJID());
0658:                if (list == null) {
0659:                    list = new ArrayList<MUCRole>();
0660:                    occupantsByBareJID.put(event.getUserAddress().toBareJID(),
0661:                            list);
0662:                }
0663:                list.add(joinRole);
0664:                occupantsByFullJID.put(event.getUserAddress(), joinRole);
0665:
0666:                // Update the date when the last occupant left the room
0667:                setEmptyDate(null);
0668:                // Fire event that occupant joined the room
0669:                server.fireOccupantJoined(getRole().getRoleAddress(), event
0670:                        .getUserAddress(), joinRole.getNickname());
0671:                // Check if we need to send presences of the new occupant to occupants hosted by this JVM
0672:                if (event.isSendPresence()) {
0673:                    for (MUCRole occupant : occupants.values()) {
0674:                        if (occupant.isLocal()) {
0675:                            occupant.send(event.getPresence().createCopy());
0676:                        }
0677:                    }
0678:                }
0679:            }
0680:
0681:            public void leaveRoom(MUCRole leaveRole) {
0682:                if (leaveRole.isLocal()) {
0683:                    // Ask other cluster nodes to remove occupant from room
0684:                    OccupantLeftEvent event = new OccupantLeftEvent(this ,
0685:                            leaveRole);
0686:                    CacheFactory.doClusterTask(event);
0687:                }
0688:
0689:                try {
0690:                    Presence presence = leaveRole.getPresence().createCopy();
0691:                    presence.setType(Presence.Type.unavailable);
0692:                    presence.setStatus(null);
0693:                    // Change (or add) presence information about roles and affiliations
0694:                    Element childElement = presence.getChildElement("x",
0695:                            "http://jabber.org/protocol/muc#user");
0696:                    if (childElement == null) {
0697:                        childElement = presence.addChildElement("x",
0698:                                "http://jabber.org/protocol/muc#user");
0699:                    }
0700:                    Element item = childElement.element("item");
0701:                    if (item == null) {
0702:                        item = childElement.addElement("item");
0703:                    }
0704:                    item.addAttribute("role", "none");
0705:                    // Inform the leaving user that he/she has left the room
0706:                    leaveRole.send(presence);
0707:                    // Inform the rest of the room occupants that the user has left the room
0708:                    broadcastPresence(presence);
0709:                } catch (Exception e) {
0710:                    Log.error(e);
0711:                }
0712:
0713:                // Remove occupant from room and destroy room if empty and not persistent
0714:                OccupantLeftEvent event = new OccupantLeftEvent(this , leaveRole);
0715:                event.setOriginator(true);
0716:                event.run();
0717:            }
0718:
0719:            public void leaveRoom(OccupantLeftEvent event) {
0720:                MUCRole leaveRole = event.getRole();
0721:                if (leaveRole == null) {
0722:                    return;
0723:                }
0724:                lock.writeLock().lock();
0725:                try {
0726:                    // Removes the role from the room
0727:                    removeOccupantRole(leaveRole);
0728:
0729:                    // TODO Implement this: If the room owner becomes unavailable for any reason before
0730:                    // submitting the form (e.g., a lost connection), the service will receive a presence
0731:                    // stanza of type "unavailable" from the owner to the room@service/nick or room@service
0732:                    // (or both). The service MUST then destroy the room, sending a presence stanza of type
0733:                    // "unavailable" from the room to the owner including a <destroy/> element and reason
0734:                    // (if provided) as defined under the "Destroying a Room" use case.
0735:
0736:                    // Remove the room from the server only if there are no more occupants and the room is
0737:                    // not persistent
0738:                    if (occupants.isEmpty() && !isPersistent()) {
0739:                        endTime = System.currentTimeMillis();
0740:                        if (event.isOriginator()) {
0741:                            server.removeChatRoom(name);
0742:                        }
0743:                        // Fire event that the room has been destroyed
0744:                        server.fireRoomDestroyed(getRole().getRoleAddress());
0745:                    }
0746:                    if (occupants.isEmpty()) {
0747:                        // Update the date when the last occupant left the room
0748:                        setEmptyDate(new Date());
0749:                    }
0750:                } finally {
0751:                    lock.writeLock().unlock();
0752:                }
0753:            }
0754:
0755:            /**
0756:             * Removes the role of the occupant from all the internal occupants collections. The role will
0757:             * also be removed from the user's roles.
0758:             *
0759:             * @param leaveRole the role to remove.
0760:             */
0761:            private void removeOccupantRole(MUCRole leaveRole) {
0762:                occupants.remove(leaveRole.getNickname().toLowerCase());
0763:
0764:                JID userAddress = leaveRole.getUserAddress();
0765:                // Notify the user that he/she is no longer in the room
0766:                leaveRole.destroy();
0767:                // Update the tables of occupants based on the bare and full JID
0768:                List list = occupantsByBareJID.get(userAddress.toBareJID());
0769:                if (list != null) {
0770:                    list.remove(leaveRole);
0771:                    if (list.isEmpty()) {
0772:                        occupantsByBareJID.remove(userAddress.toBareJID());
0773:                    }
0774:                }
0775:                occupantsByFullJID.remove(userAddress);
0776:                // Fire event that occupant left the room
0777:                server
0778:                        .fireOccupantLeft(getRole().getRoleAddress(),
0779:                                userAddress);
0780:            }
0781:
0782:            public void destroyRoom(DestroyRoomRequest destroyRequest) {
0783:                String alternateJID = destroyRequest.getAlternateJID();
0784:                String reason = destroyRequest.getReason();
0785:                MUCRole leaveRole;
0786:                Collection<MUCRole> removedRoles = new ArrayList<MUCRole>();
0787:                lock.writeLock().lock();
0788:                try {
0789:                    boolean hasRemoteOccupants = false;
0790:                    // Remove each occupant
0791:                    for (String nickname : occupants.keySet()) {
0792:                        leaveRole = occupants.remove(nickname);
0793:
0794:                        if (leaveRole != null) {
0795:                            // Add the removed occupant to the list of removed occupants. We are keeping a
0796:                            // list of removed occupants to process later outside of the lock.
0797:                            if (leaveRole.isLocal()) {
0798:                                removedRoles.add(leaveRole);
0799:                            } else {
0800:                                hasRemoteOccupants = true;
0801:                            }
0802:                            removeOccupantRole(leaveRole);
0803:                        }
0804:                    }
0805:                    endTime = System.currentTimeMillis();
0806:                    // Set that the room has been destroyed
0807:                    isDestroyed = true;
0808:                    if (destroyRequest.isOriginator()) {
0809:                        if (hasRemoteOccupants) {
0810:                            // Ask other cluster nodes to remove occupants since room is being destroyed
0811:                            CacheFactory.doClusterTask(new DestroyRoomRequest(
0812:                                    this , alternateJID, reason));
0813:                        }
0814:                        // Removes the room from the list of rooms hosted in the server
0815:                        server.removeChatRoom(name);
0816:                    }
0817:                } finally {
0818:                    lock.writeLock().unlock();
0819:                }
0820:                // Send an unavailable presence to each removed occupant
0821:                for (MUCRole removedRole : removedRoles) {
0822:                    try {
0823:                        // Send a presence stanza of type "unavailable" to the occupant
0824:                        Presence presence = createPresence(Presence.Type.unavailable);
0825:                        presence.setFrom(removedRole.getRoleAddress());
0826:
0827:                        // A fragment containing the x-extension for room destruction.
0828:                        Element fragment = presence.addChildElement("x",
0829:                                "http://jabber.org/protocol/muc#user");
0830:                        Element item = fragment.addElement("item");
0831:                        item.addAttribute("affiliation", "none");
0832:                        item.addAttribute("role", "none");
0833:                        if (alternateJID != null && alternateJID.length() > 0) {
0834:                            fragment.addElement("destroy").addAttribute("jid",
0835:                                    alternateJID);
0836:                        }
0837:                        if (reason != null && reason.length() > 0) {
0838:                            Element destroy = fragment.element("destroy");
0839:                            if (destroy == null) {
0840:                                destroy = fragment.addElement("destroy");
0841:                            }
0842:                            destroy.addElement("reason").setText(reason);
0843:                        }
0844:                        removedRole.send(presence);
0845:                    } catch (Exception e) {
0846:                        Log.error(e);
0847:                    }
0848:                }
0849:                if (destroyRequest.isOriginator()) {
0850:                    // Remove the room from the DB if the room was persistent
0851:                    MUCPersistenceManager.deleteFromDB(this );
0852:                }
0853:                // Fire event that the room has been destroyed
0854:                server.fireRoomDestroyed(getRole().getRoleAddress());
0855:            }
0856:
0857:            public void destroyRoom(String alternateJID, String reason) {
0858:                DestroyRoomRequest destroyRequest = new DestroyRoomRequest(
0859:                        this , alternateJID, reason);
0860:                destroyRequest.setOriginator(true);
0861:                destroyRequest.run();
0862:            }
0863:
0864:            public Presence createPresence(Presence.Type presenceType)
0865:                    throws UnauthorizedException {
0866:                Presence presence = new Presence();
0867:                presence.setType(presenceType);
0868:                presence.setFrom(role.getRoleAddress());
0869:                return presence;
0870:            }
0871:
0872:            public void serverBroadcast(String msg) {
0873:                Message message = new Message();
0874:                message.setType(Message.Type.groupchat);
0875:                message.setBody(msg);
0876:                message.setFrom(role.getRoleAddress());
0877:                broadcast(message);
0878:            }
0879:
0880:            public void sendPublicMessage(Message message, MUCRole senderRole)
0881:                    throws ForbiddenException {
0882:                // Check that if the room is moderated then the sender of the message has to have voice
0883:                if (isModerated()
0884:                        && senderRole.getRole().compareTo(
0885:                                MUCRole.Role.participant) > 0) {
0886:                    throw new ForbiddenException();
0887:                }
0888:                // Send the message to all occupants
0889:                message.setFrom(senderRole.getRoleAddress());
0890:                send(message);
0891:                // Fire event that message was receibed by the room
0892:                server.fireMessageReceived(getRole().getRoleAddress(),
0893:                        senderRole.getUserAddress(), senderRole.getNickname(),
0894:                        message);
0895:            }
0896:
0897:            public void sendPrivatePacket(Packet packet, MUCRole senderRole)
0898:                    throws NotFoundException {
0899:                String resource = packet.getTo().getResource();
0900:                MUCRole occupant = occupants.get(resource.toLowerCase());
0901:                if (occupant != null) {
0902:                    packet.setFrom(senderRole.getRoleAddress());
0903:                    occupant.send(packet);
0904:                } else {
0905:                    throw new NotFoundException();
0906:                }
0907:            }
0908:
0909:            public void send(Packet packet) {
0910:                if (packet instanceof  Message) {
0911:                    broadcast((Message) packet);
0912:                } else if (packet instanceof  Presence) {
0913:                    broadcastPresence((Presence) packet);
0914:                } else if (packet instanceof  IQ) {
0915:                    IQ reply = IQ.createResultIQ((IQ) packet);
0916:                    reply.setChildElement(((IQ) packet).getChildElement());
0917:                    reply.setError(PacketError.Condition.bad_request);
0918:                    router.route(reply);
0919:                }
0920:            }
0921:
0922:            /**
0923:             * Broadcasts the specified presence to all room occupants. If the presence belongs to a
0924:             * user whose role cannot be broadcast then the presence will only be sent to the presence's
0925:             * user. On the other hand, the JID of the user that sent the presence won't be included if the
0926:             * room is semi-anon and the target occupant is not a moderator.
0927:             *
0928:             * @param presence the presence to broadcast.
0929:             */
0930:            private void broadcastPresence(Presence presence) {
0931:                if (presence == null) {
0932:                    return;
0933:                }
0934:                if (hasToCheckRoleToBroadcastPresence()) {
0935:                    Element frag = presence.getChildElement("x",
0936:                            "http://jabber.org/protocol/muc#user");
0937:                    // Check if we can broadcast the presence for this role
0938:                    if (!canBroadcastPresence(frag.element("item")
0939:                            .attributeValue("role"))) {
0940:                        // Just send the presence to the sender of the presence
0941:                        try {
0942:                            MUCRole occupant = getOccupant(presence.getFrom()
0943:                                    .getResource());
0944:                            occupant.send(presence);
0945:                        } catch (UserNotFoundException e) {
0946:                            // Do nothing
0947:                        }
0948:                        return;
0949:                    }
0950:                }
0951:
0952:                // Broadcast presence to occupants hosted by other cluster nodes
0953:                BroascastPresenceRequest request = new BroascastPresenceRequest(
0954:                        this , presence);
0955:                CacheFactory.doClusterTask(request);
0956:
0957:                // Broadcast presence to occupants connected to this JVM
0958:                request = new BroascastPresenceRequest(this , presence);
0959:                request.setOriginator(true);
0960:                request.run();
0961:            }
0962:
0963:            public void broadcast(BroascastPresenceRequest presenceRequest) {
0964:                String jid = null;
0965:                Presence presence = presenceRequest.getPresence();
0966:                Element frag = presence.getChildElement("x",
0967:                        "http://jabber.org/protocol/muc#user");
0968:                // Don't include the occupant's JID if the room is semi-anon and the new occupant
0969:                // is not a moderator
0970:                if (!canAnyoneDiscoverJID()) {
0971:                    jid = frag.element("item").attributeValue("jid");
0972:                }
0973:                for (MUCRole occupant : occupants.values()) {
0974:                    if (!occupant.isLocal()) {
0975:                        continue;
0976:                    }
0977:                    // Don't include the occupant's JID if the room is semi-anon and the new occupant
0978:                    // is not a moderator
0979:                    if (!canAnyoneDiscoverJID()) {
0980:                        if (MUCRole.Role.moderator == occupant.getRole()) {
0981:                            frag.element("item").addAttribute("jid", jid);
0982:                        } else {
0983:                            frag.element("item").addAttribute("jid", null);
0984:                        }
0985:                    }
0986:                    occupant.send(presence);
0987:                }
0988:            }
0989:
0990:            private void broadcast(Message message) {
0991:                // Broadcast message to occupants hosted by other cluster nodes
0992:                BroascastMessageRequest request = new BroascastMessageRequest(
0993:                        this , message, occupants.size());
0994:                CacheFactory.doClusterTask(request);
0995:
0996:                // Broadcast message to occupants connected to this JVM
0997:                request = new BroascastMessageRequest(this , message, occupants
0998:                        .size());
0999:                request.setOriginator(true);
1000:                request.run();
1001:            }
1002:
1003:            public void broadcast(BroascastMessageRequest messageRequest) {
1004:                Message message = messageRequest.getMessage();
1005:                // Add message to the room history
1006:                roomHistory.addMessage(message);
1007:                // Send message to occupants connected to this JVM
1008:                for (MUCRole occupant : occupants.values()) {
1009:                    // Do not send broadcast messages to deaf occupants or occupants hosted in
1010:                    // other cluster nodes
1011:                    if (occupant.isLocal() && !occupant.isVoiceOnly()) {
1012:                        occupant.send(message);
1013:                    }
1014:                }
1015:                if (messageRequest.isOriginator() && isLogEnabled()) {
1016:                    MUCRole senderRole = null;
1017:                    JID senderAddress;
1018:                    if (message.getFrom() != null
1019:                            && message.getFrom().getResource() != null) {
1020:                        senderRole = occupants.get(message.getFrom()
1021:                                .getResource().toLowerCase());
1022:                    }
1023:                    if (senderRole == null) {
1024:                        // The room itself is sending the message
1025:                        senderAddress = getRole().getRoleAddress();
1026:                    } else {
1027:                        // An occupant is sending the message
1028:                        senderAddress = senderRole.getUserAddress();
1029:                    }
1030:                    // Log the conversation
1031:                    server.logConversation(this , message, senderAddress);
1032:                }
1033:                server.messageBroadcastedTo(messageRequest.getOccupants());
1034:            }
1035:
1036:            /**
1037:             * An empty role that represents the room itself in the chatroom. Chatrooms need to be able to
1038:             * speak (server messages) and so must have their own role in the chatroom.
1039:             */
1040:            private class RoomRole implements  MUCRole {
1041:
1042:                private MUCRoom room;
1043:
1044:                private RoomRole(MUCRoom room) {
1045:                    this .room = room;
1046:                }
1047:
1048:                public Presence getPresence() {
1049:                    return null;
1050:                }
1051:
1052:                public void setPresence(Presence presence) {
1053:                }
1054:
1055:                public void setRole(MUCRole.Role newRole) {
1056:                }
1057:
1058:                public MUCRole.Role getRole() {
1059:                    return MUCRole.Role.moderator;
1060:                }
1061:
1062:                public void setAffiliation(MUCRole.Affiliation newAffiliation) {
1063:                }
1064:
1065:                public MUCRole.Affiliation getAffiliation() {
1066:                    return MUCRole.Affiliation.owner;
1067:                }
1068:
1069:                public void changeNickname(String nickname) {
1070:                }
1071:
1072:                public String getNickname() {
1073:                    return null;
1074:                }
1075:
1076:                public boolean isVoiceOnly() {
1077:                    return false;
1078:                }
1079:
1080:                public boolean isLocal() {
1081:                    return true;
1082:                }
1083:
1084:                public NodeID getNodeID() {
1085:                    return XMPPServer.getInstance().getNodeID();
1086:                }
1087:
1088:                public MUCRoom getChatRoom() {
1089:                    return room;
1090:                }
1091:
1092:                private JID crJID = null;
1093:
1094:                public JID getRoleAddress() {
1095:                    if (crJID == null) {
1096:                        crJID = new JID(room.getName(), server
1097:                                .getServiceDomain(), null, true);
1098:                    }
1099:                    return crJID;
1100:                }
1101:
1102:                public JID getUserAddress() {
1103:                    return null;
1104:                }
1105:
1106:                public void send(Packet packet) {
1107:                    room.send(packet);
1108:                }
1109:
1110:                public void destroy() {
1111:                }
1112:            }
1113:
1114:            public long getChatLength() {
1115:                return endTime - startTime;
1116:            }
1117:
1118:            /**
1119:             * Updates all the presences of the given user with the new affiliation and role information. Do
1120:             * nothing if the given jid is not present in the room. If the user has joined the room from
1121:             * several client resources, all his/her occupants' presences will be updated.
1122:             * 
1123:             * @param bareJID the bare jid of the user to update his/her role.
1124:             * @param newAffiliation the new affiliation for the JID.
1125:             * @param newRole the new role for the JID.
1126:             * @return the list of updated presences of all the client resources that the client used to
1127:             *         join the room.
1128:             * @throws NotAllowedException If trying to change the moderator role to an owner or an admin or
1129:             *         if trying to ban an owner or an administrator.
1130:             */
1131:            private List<Presence> changeOccupantAffiliation(String bareJID,
1132:                    MUCRole.Affiliation newAffiliation, MUCRole.Role newRole)
1133:                    throws NotAllowedException {
1134:                List<Presence> presences = new ArrayList<Presence>();
1135:                // Get all the roles (i.e. occupants) of this user based on his/her bare JID
1136:                List<MUCRole> roles = occupantsByBareJID.get(bareJID);
1137:                if (roles == null) {
1138:                    return presences;
1139:                }
1140:                // Collect all the updated presences of these roles
1141:                for (MUCRole role : roles) {
1142:                    // Update the presence with the new affiliation and role
1143:                    if (role.isLocal()) {
1144:                        role.setAffiliation(newAffiliation);
1145:                        role.setRole(newRole);
1146:                        // Notify the othe cluster nodes to update the occupant
1147:                        CacheFactory.doClusterTask(new UpdateOccupant(this ,
1148:                                role));
1149:                        // Prepare a new presence to be sent to all the room occupants
1150:                        presences.add(role.getPresence().createCopy());
1151:                    } else {
1152:                        // Ask the cluster node hosting the occupant to make the changes. Note that if the change
1153:                        // is not allowed a NotAllowedException will be thrown
1154:                        Element element = (Element) CacheFactory
1155:                                .doSynchronousClusterTask(
1156:                                        new UpdateOccupantRequest(this , role
1157:                                                .getNickname(), newAffiliation,
1158:                                                newRole), role.getNodeID()
1159:                                                .toByteArray());
1160:                        if (element != null) {
1161:                            // Prepare a new presence to be sent to all the room occupants
1162:                            presences.add(new Presence(element, true));
1163:                        } else {
1164:                            throw new NotAllowedException();
1165:                        }
1166:                    }
1167:                }
1168:                // Answer all the updated presences
1169:                return presences;
1170:            }
1171:
1172:            /**
1173:             * Updates the presence of the given user with the new role information. Do nothing if the given
1174:             * jid is not present in the room.
1175:             * 
1176:             * @param jid the full jid of the user to update his/her role.
1177:             * @param newRole the new role for the JID.
1178:             * @return the updated presence of the user or null if none.
1179:             * @throws NotAllowedException If trying to change the moderator role to an owner or an admin.
1180:             */
1181:            private Presence changeOccupantRole(JID jid, MUCRole.Role newRole)
1182:                    throws NotAllowedException {
1183:                // Try looking the role in the bare JID list
1184:                MUCRole role = occupantsByFullJID.get(jid);
1185:                if (role != null) {
1186:                    // Update the presence with the new role
1187:                    role.setRole(newRole);
1188:                    // Notify the othe cluster nodes to update the occupant
1189:                    CacheFactory.doClusterTask(new UpdateOccupant(this , role));
1190:                    // Prepare a new presence to be sent to all the room occupants
1191:                    return role.getPresence().createCopy();
1192:                }
1193:                return null;
1194:            }
1195:
1196:            public void addFirstOwner(String bareJID) {
1197:                owners.add(bareJID);
1198:            }
1199:
1200:            public List<Presence> addOwner(String bareJID, MUCRole sendRole)
1201:                    throws ForbiddenException {
1202:                MUCRole.Affiliation oldAffiliation = MUCRole.Affiliation.none;
1203:                if (MUCRole.Affiliation.owner != sendRole.getAffiliation()) {
1204:                    throw new ForbiddenException();
1205:                }
1206:                // Check if user is already an owner
1207:                if (owners.contains(bareJID)) {
1208:                    // Do nothing
1209:                    return Collections.emptyList();
1210:                }
1211:                owners.add(bareJID);
1212:                // Remove the user from other affiliation lists
1213:                if (removeAdmin(bareJID)) {
1214:                    oldAffiliation = MUCRole.Affiliation.admin;
1215:                } else if (removeMember(bareJID)) {
1216:                    oldAffiliation = MUCRole.Affiliation.member;
1217:                } else if (removeOutcast(bareJID)) {
1218:                    oldAffiliation = MUCRole.Affiliation.outcast;
1219:                }
1220:                // Update the DB if the room is persistent
1221:                MUCPersistenceManager.saveAffiliationToDB(this , bareJID, null,
1222:                        MUCRole.Affiliation.owner, oldAffiliation);
1223:                // Update the presence with the new affiliation and inform all occupants
1224:                try {
1225:                    return changeOccupantAffiliation(bareJID,
1226:                            MUCRole.Affiliation.owner, MUCRole.Role.moderator);
1227:                } catch (NotAllowedException e) {
1228:                    // We should never receive this exception....in theory
1229:                    return null;
1230:                }
1231:            }
1232:
1233:            private boolean removeOwner(String bareJID) {
1234:                return owners.remove(bareJID);
1235:            }
1236:
1237:            public List<Presence> addAdmin(String bareJID, MUCRole sendRole)
1238:                    throws ForbiddenException, ConflictException {
1239:                MUCRole.Affiliation oldAffiliation = MUCRole.Affiliation.none;
1240:                if (MUCRole.Affiliation.owner != sendRole.getAffiliation()) {
1241:                    throw new ForbiddenException();
1242:                }
1243:                // Check that the room always has an owner
1244:                if (owners.contains(bareJID) && owners.size() == 1) {
1245:                    throw new ConflictException();
1246:                }
1247:                // Check if user is already an admin
1248:                if (admins.contains(bareJID)) {
1249:                    // Do nothing
1250:                    return Collections.emptyList();
1251:                }
1252:                admins.add(bareJID);
1253:                // Remove the user from other affiliation lists
1254:                if (removeOwner(bareJID)) {
1255:                    oldAffiliation = MUCRole.Affiliation.owner;
1256:                } else if (removeMember(bareJID)) {
1257:                    oldAffiliation = MUCRole.Affiliation.member;
1258:                } else if (removeOutcast(bareJID)) {
1259:                    oldAffiliation = MUCRole.Affiliation.outcast;
1260:                }
1261:                // Update the DB if the room is persistent
1262:                MUCPersistenceManager.saveAffiliationToDB(this , bareJID, null,
1263:                        MUCRole.Affiliation.admin, oldAffiliation);
1264:                // Update the presence with the new affiliation and inform all occupants
1265:                try {
1266:                    return changeOccupantAffiliation(bareJID,
1267:                            MUCRole.Affiliation.admin, MUCRole.Role.moderator);
1268:                } catch (NotAllowedException e) {
1269:                    // We should never receive this exception....in theory
1270:                    return null;
1271:                }
1272:            }
1273:
1274:            private boolean removeAdmin(String bareJID) {
1275:                return admins.remove(bareJID);
1276:            }
1277:
1278:            public List<Presence> addMember(String bareJID, String nickname,
1279:                    MUCRole sendRole) throws ForbiddenException,
1280:                    ConflictException {
1281:                MUCRole.Affiliation oldAffiliation = (members
1282:                        .containsKey(bareJID) ? MUCRole.Affiliation.member
1283:                        : MUCRole.Affiliation.none);
1284:                if (isMembersOnly()) {
1285:                    if (!canOccupantsInvite()) {
1286:                        if (MUCRole.Affiliation.admin != sendRole
1287:                                .getAffiliation()
1288:                                && MUCRole.Affiliation.owner != sendRole
1289:                                        .getAffiliation()) {
1290:                            throw new ForbiddenException();
1291:                        }
1292:                    }
1293:                } else {
1294:                    if (MUCRole.Affiliation.admin != sendRole.getAffiliation()
1295:                            && MUCRole.Affiliation.owner != sendRole
1296:                                    .getAffiliation()) {
1297:                        throw new ForbiddenException();
1298:                    }
1299:                }
1300:                // Check if the desired nickname is already reserved for another member
1301:                if (nickname != null && nickname.trim().length() > 0
1302:                        && members.containsValue(nickname)) {
1303:                    if (!nickname.equals(members.get(bareJID))) {
1304:                        throw new ConflictException();
1305:                    }
1306:                }
1307:                // Check that the room always has an owner
1308:                if (owners.contains(bareJID) && owners.size() == 1) {
1309:                    throw new ConflictException();
1310:                }
1311:                // Associate the reserved nickname with the bareJID. If nickname is null then associate an
1312:                // empty string
1313:                members.put(bareJID, (nickname == null ? "" : nickname));
1314:                // Remove the user from other affiliation lists
1315:                if (removeOwner(bareJID)) {
1316:                    oldAffiliation = MUCRole.Affiliation.owner;
1317:                } else if (removeAdmin(bareJID)) {
1318:                    oldAffiliation = MUCRole.Affiliation.admin;
1319:                } else if (removeOutcast(bareJID)) {
1320:                    oldAffiliation = MUCRole.Affiliation.outcast;
1321:                }
1322:                // Update the DB if the room is persistent
1323:                MUCPersistenceManager.saveAffiliationToDB(this , bareJID,
1324:                        nickname, MUCRole.Affiliation.member, oldAffiliation);
1325:                // Update the presence with the new affiliation and inform all occupants
1326:                try {
1327:                    return changeOccupantAffiliation(bareJID,
1328:                            MUCRole.Affiliation.member,
1329:                            MUCRole.Role.participant);
1330:                } catch (NotAllowedException e) {
1331:                    // We should never receive this exception....in theory
1332:                    return null;
1333:                }
1334:            }
1335:
1336:            private boolean removeMember(String bareJID) {
1337:                boolean answer = members.containsKey(bareJID);
1338:                members.remove(bareJID);
1339:                return answer;
1340:            }
1341:
1342:            public List<Presence> addOutcast(String bareJID, String reason,
1343:                    MUCRole senderRole) throws NotAllowedException,
1344:                    ForbiddenException, ConflictException {
1345:                MUCRole.Affiliation oldAffiliation = MUCRole.Affiliation.none;
1346:                if (MUCRole.Affiliation.admin != senderRole.getAffiliation()
1347:                        && MUCRole.Affiliation.owner != senderRole
1348:                                .getAffiliation()) {
1349:                    throw new ForbiddenException();
1350:                }
1351:                // Check that the room always has an owner
1352:                if (owners.contains(bareJID) && owners.size() == 1) {
1353:                    throw new ConflictException();
1354:                }
1355:                // Check if user is already an outcast
1356:                if (outcasts.contains(bareJID)) {
1357:                    // Do nothing
1358:                    return Collections.emptyList();
1359:                }
1360:                // Update the presence with the new affiliation and inform all occupants
1361:                // actorJID will be null if the room itself (ie. via admin console) made the request
1362:                JID actorJID = senderRole.getUserAddress();
1363:                List<Presence> updatedPresences = changeOccupantAffiliation(
1364:                        bareJID, MUCRole.Affiliation.outcast, MUCRole.Role.none);
1365:                Element frag;
1366:                // Add the status code and reason why the user was banned to the presences that will
1367:                // be sent to the room occupants (the banned user will not receive this presences)
1368:                for (Presence presence : updatedPresences) {
1369:                    frag = presence.getChildElement("x",
1370:                            "http://jabber.org/protocol/muc#user");
1371:                    // Add the status code 301 that indicates that the user was banned
1372:                    frag.addElement("status").addAttribute("code", "301");
1373:                    // Add the reason why the user was banned
1374:                    if (reason != null && reason.trim().length() > 0) {
1375:                        frag.element("item").addElement("reason").setText(
1376:                                reason);
1377:                    }
1378:
1379:                    // Remove the banned users from the room. If a user has joined the room from
1380:                    // different client resources, he/she will be kicked from all the client resources
1381:                    // Effectively kick the occupant from the room
1382:                    kickPresence(presence, actorJID);
1383:                }
1384:                // Update the affiliation lists
1385:                outcasts.add(bareJID);
1386:                // Remove the user from other affiliation lists
1387:                if (removeOwner(bareJID)) {
1388:                    oldAffiliation = MUCRole.Affiliation.owner;
1389:                } else if (removeAdmin(bareJID)) {
1390:                    oldAffiliation = MUCRole.Affiliation.admin;
1391:                } else if (removeMember(bareJID)) {
1392:                    oldAffiliation = MUCRole.Affiliation.member;
1393:                }
1394:                // Update the DB if the room is persistent
1395:                MUCPersistenceManager.saveAffiliationToDB(this , bareJID, null,
1396:                        MUCRole.Affiliation.outcast, oldAffiliation);
1397:                return updatedPresences;
1398:            }
1399:
1400:            private boolean removeOutcast(String bareJID) {
1401:                return outcasts.remove(bareJID);
1402:            }
1403:
1404:            public List<Presence> addNone(String bareJID, MUCRole senderRole)
1405:                    throws ForbiddenException, ConflictException {
1406:                MUCRole.Affiliation oldAffiliation = MUCRole.Affiliation.none;
1407:                if (MUCRole.Affiliation.admin != senderRole.getAffiliation()
1408:                        && MUCRole.Affiliation.owner != senderRole
1409:                                .getAffiliation()) {
1410:                    throw new ForbiddenException();
1411:                }
1412:                // Check that the room always has an owner
1413:                if (owners.contains(bareJID) && owners.size() == 1) {
1414:                    throw new ConflictException();
1415:                }
1416:                List<Presence> updatedPresences = null;
1417:                boolean wasMember = members.containsKey(bareJID)
1418:                        || admins.contains(bareJID) || owners.contains(bareJID);
1419:                // Remove the user from ALL the affiliation lists
1420:                if (removeOwner(bareJID)) {
1421:                    oldAffiliation = MUCRole.Affiliation.owner;
1422:                } else if (removeAdmin(bareJID)) {
1423:                    oldAffiliation = MUCRole.Affiliation.admin;
1424:                } else if (removeMember(bareJID)) {
1425:                    oldAffiliation = MUCRole.Affiliation.member;
1426:                } else if (removeOutcast(bareJID)) {
1427:                    oldAffiliation = MUCRole.Affiliation.outcast;
1428:                }
1429:                // Remove the affiliation of this user from the DB if the room is persistent
1430:                MUCPersistenceManager.removeAffiliationFromDB(this , bareJID,
1431:                        oldAffiliation);
1432:
1433:                // Update the presence with the new affiliation and inform all occupants
1434:                try {
1435:                    MUCRole.Role newRole;
1436:                    if (isMembersOnly() && wasMember) {
1437:                        newRole = MUCRole.Role.none;
1438:                    } else {
1439:                        newRole = isModerated() ? MUCRole.Role.visitor
1440:                                : MUCRole.Role.participant;
1441:                    }
1442:                    updatedPresences = changeOccupantAffiliation(bareJID,
1443:                            MUCRole.Affiliation.none, newRole);
1444:                    if (isMembersOnly() && wasMember) {
1445:                        // If the room is members-only, remove the user from the room including a status
1446:                        // code of 321 to indicate that the user was removed because of an affiliation
1447:                        // change
1448:                        Element frag;
1449:                        // Add the status code to the presences that will be sent to the room occupants
1450:                        for (Presence presence : updatedPresences) {
1451:                            // Set the presence as an unavailable presence
1452:                            presence.setType(Presence.Type.unavailable);
1453:                            presence.setStatus(null);
1454:                            frag = presence.getChildElement("x",
1455:                                    "http://jabber.org/protocol/muc#user");
1456:                            // Add the status code 321 that indicates that the user was removed because of
1457:                            // an affiliation change
1458:                            frag.addElement("status").addAttribute("code",
1459:                                    "321");
1460:
1461:                            // Remove the ex-member from the room. If a user has joined the room from
1462:                            // different client resources, he/she will be kicked from all the client
1463:                            // resources.
1464:                            // Effectively kick the occupant from the room
1465:                            JID actorJID = senderRole.getUserAddress();
1466:                            kickPresence(presence, actorJID);
1467:                        }
1468:                    }
1469:                } catch (NotAllowedException e) {
1470:                    // We should never receive this exception....in theory
1471:                }
1472:                return updatedPresences;
1473:            }
1474:
1475:            public boolean isLocked() {
1476:                return lockedTime > 0;
1477:            }
1478:
1479:            public boolean isManuallyLocked() {
1480:                return lockedTime > 0 && creationDate.getTime() != lockedTime;
1481:            }
1482:
1483:            public void presenceUpdated(MUCRole occupantRole,
1484:                    Presence newPresence) {
1485:                // Ask other cluster nodes to update the presence of the occupant
1486:                UpdatePresence request = new UpdatePresence(this , newPresence
1487:                        .createCopy(), occupantRole.getNickname());
1488:                CacheFactory.doClusterTask(request);
1489:
1490:                // Update the presence of the occupant
1491:                request = new UpdatePresence(this , newPresence.createCopy(),
1492:                        occupantRole.getNickname());
1493:                request.setOriginator(true);
1494:                request.run();
1495:
1496:                // Broadcast new presence of occupant
1497:                broadcastPresence(occupantRole.getPresence().createCopy());
1498:            }
1499:
1500:            /**
1501:             * Updates the presence of an occupant with the new presence included in the request.
1502:             *
1503:             * @param updatePresence request to update an occupant's presence.
1504:             */
1505:            public void presenceUpdated(UpdatePresence updatePresence) {
1506:                MUCRole occupantRole = occupants.get(updatePresence
1507:                        .getNickname().toLowerCase());
1508:                if (occupantRole != null) {
1509:                    occupantRole.setPresence(updatePresence.getPresence());
1510:                } else {
1511:                    Log
1512:                            .debug("LocalMUCRoom: Failed to update presence of room occupant. Occupant nickname: "
1513:                                    + updatePresence.getNickname());
1514:                }
1515:            }
1516:
1517:            public void occupantUpdated(UpdateOccupant update) {
1518:                MUCRole occupantRole = occupants.get(update.getNickname()
1519:                        .toLowerCase());
1520:                if (occupantRole != null) {
1521:                    if (!occupantRole.isLocal()) {
1522:                        occupantRole.setPresence(update.getPresence());
1523:                        try {
1524:                            occupantRole.setRole(update.getRole());
1525:                            occupantRole
1526:                                    .setAffiliation(update.getAffiliation());
1527:                        } catch (NotAllowedException e) {
1528:                            // Ignore. Should never happen with remote roles
1529:                        }
1530:                    } else {
1531:                        Log
1532:                                .error("Tried to update local occupant with info of local occupant?. Occupant nickname: "
1533:                                        + update.getNickname());
1534:                    }
1535:                } else {
1536:                    Log
1537:                            .debug("LocalMUCRoom: Failed to update information of room occupant. Occupant nickname: "
1538:                                    + update.getNickname());
1539:                }
1540:            }
1541:
1542:            public Presence updateOccupant(UpdateOccupantRequest updateRequest)
1543:                    throws NotAllowedException {
1544:                MUCRole occupantRole = occupants.get(updateRequest
1545:                        .getNickname().toLowerCase());
1546:                if (occupantRole != null) {
1547:                    occupantRole.setAffiliation(updateRequest.getAffiliation());
1548:                    occupantRole.setRole(updateRequest.getRole());
1549:                    // Notify the othe cluster nodes to update the occupant
1550:                    CacheFactory.doClusterTask(new UpdateOccupant(this ,
1551:                            occupantRole));
1552:                    return occupantRole.getPresence();
1553:                } else {
1554:                    Log
1555:                            .debug("LocalMUCRoom: Failed to update information of local room occupant. Occupant nickname: "
1556:                                    + updateRequest.getNickname());
1557:                }
1558:                return null;
1559:            }
1560:
1561:            public void nicknameChanged(MUCRole occupantRole,
1562:                    Presence newPresence, String oldNick, String newNick) {
1563:                // Ask other cluster nodes to update the nickname of the occupant
1564:                ChangeNickname request = new ChangeNickname(this , oldNick,
1565:                        newNick, newPresence.createCopy());
1566:                CacheFactory.doClusterTask(request);
1567:
1568:                // Update the nickname of the occupant
1569:                request = new ChangeNickname(this , oldNick, newNick,
1570:                        newPresence.createCopy());
1571:                request.setOriginator(true);
1572:                request.run();
1573:
1574:                // Broadcast new presence of occupant
1575:                broadcastPresence(occupantRole.getPresence().createCopy());
1576:            }
1577:
1578:            public void nicknameChanged(ChangeNickname changeNickname) {
1579:                MUCRole occupantRole = occupants.get(changeNickname
1580:                        .getOldNick().toLowerCase());
1581:                if (occupantRole != null) {
1582:                    // Update the role with the new info
1583:                    occupantRole.setPresence(changeNickname.getPresence());
1584:                    occupantRole.changeNickname(changeNickname.getNewNick());
1585:                    // Fire event that user changed his nickname
1586:                    server.fireNicknameChanged(getRole().getRoleAddress(),
1587:                            occupantRole.getUserAddress(), changeNickname
1588:                                    .getOldNick(), changeNickname.getNewNick());
1589:                    // Associate the existing MUCRole with the new nickname
1590:                    occupants.put(changeNickname.getNewNick().toLowerCase(),
1591:                            occupantRole);
1592:                    // Remove the old nickname
1593:                    occupants.remove(changeNickname.getOldNick().toLowerCase());
1594:                }
1595:            }
1596:
1597:            public void changeSubject(Message packet, MUCRole role)
1598:                    throws ForbiddenException {
1599:                if ((canOccupantsChangeSubject() && role.getRole().compareTo(
1600:                        MUCRole.Role.visitor) < 0)
1601:                        || MUCRole.Role.moderator == role.getRole()) {
1602:                    // Do nothing if the new subject is the same as the existing one
1603:                    if (packet.getSubject().equals(subject)) {
1604:                        return;
1605:                    }
1606:                    // Set the new subject to the room
1607:                    subject = packet.getSubject();
1608:                    MUCPersistenceManager.updateRoomSubject(this );
1609:                    // Notify all the occupants that the subject has changed
1610:                    packet.setFrom(role.getRoleAddress());
1611:                    send(packet);
1612:                } else {
1613:                    throw new ForbiddenException();
1614:                }
1615:            }
1616:
1617:            public String getSubject() {
1618:                return subject;
1619:            }
1620:
1621:            public void setSubject(String subject) {
1622:                this .subject = subject;
1623:            }
1624:
1625:            public void sendInvitation(JID to, String reason,
1626:                    MUCRole senderRole, List<Element> extensions)
1627:                    throws ForbiddenException {
1628:                if (!isMembersOnly()
1629:                        || canOccupantsInvite()
1630:                        || MUCRole.Affiliation.admin == senderRole
1631:                                .getAffiliation()
1632:                        || MUCRole.Affiliation.owner == senderRole
1633:                                .getAffiliation()) {
1634:                    // If the room is not members-only OR if the room is members-only and anyone can send
1635:                    // invitations or the sender is an admin or an owner, then send the invitation
1636:                    Message message = new Message();
1637:                    message.setFrom(role.getRoleAddress());
1638:                    message.setTo(to);
1639:                    // Add a list of extensions sent with the original message invitation (if any)
1640:                    if (extensions != null) {
1641:                        for (Element element : extensions) {
1642:                            element.setParent(null);
1643:                            message.getElement().add(element);
1644:                        }
1645:                    }
1646:                    Element frag = message.addChildElement("x",
1647:                            "http://jabber.org/protocol/muc#user");
1648:                    // ChatUser will be null if the room itself (ie. via admin console) made the request
1649:                    if (senderRole.getUserAddress() != null) {
1650:                        frag.addElement("invite").addAttribute("from",
1651:                                senderRole.getUserAddress().toBareJID());
1652:                    }
1653:                    if (reason != null && reason.length() > 0) {
1654:                        Element invite = frag.element("invite");
1655:                        if (invite == null) {
1656:                            invite = frag.addElement("invite");
1657:                        }
1658:                        invite.addElement("reason").setText(reason);
1659:                    }
1660:                    if (isPasswordProtected()) {
1661:                        frag.addElement("password").setText(getPassword());
1662:                    }
1663:
1664:                    // Include the jabber:x:conference information for backward compatibility
1665:                    frag = message.addChildElement("x", "jabber:x:conference");
1666:                    frag.addAttribute("jid", role.getRoleAddress().toBareJID());
1667:
1668:                    // Send the message with the invitation
1669:                    router.route(message);
1670:                } else {
1671:                    throw new ForbiddenException();
1672:                }
1673:            }
1674:
1675:            public void sendInvitationRejection(JID to, String reason,
1676:                    JID sender) {
1677:                Message message = new Message();
1678:                message.setFrom(role.getRoleAddress());
1679:                message.setTo(to);
1680:                Element frag = message.addChildElement("x",
1681:                        "http://jabber.org/protocol/muc#user");
1682:                frag.addElement("decline").addAttribute("from",
1683:                        sender.toBareJID());
1684:                if (reason != null && reason.length() > 0) {
1685:                    frag.element("decline").addElement("reason")
1686:                            .setText(reason);
1687:                }
1688:
1689:                // Send the message with the invitation
1690:                router.route(message);
1691:            }
1692:
1693:            public IQOwnerHandler getIQOwnerHandler() {
1694:                return iqOwnerHandler;
1695:            }
1696:
1697:            public IQAdminHandler getIQAdminHandler() {
1698:                return iqAdminHandler;
1699:            }
1700:
1701:            public MUCRoomHistory getRoomHistory() {
1702:                return roomHistory;
1703:            }
1704:
1705:            public Collection<String> getOwners() {
1706:                return Collections.unmodifiableList(owners);
1707:            }
1708:
1709:            public Collection<String> getAdmins() {
1710:                return Collections.unmodifiableList(admins);
1711:            }
1712:
1713:            public Collection<String> getMembers() {
1714:                return Collections.unmodifiableMap(members).keySet();
1715:            }
1716:
1717:            public Collection<String> getOutcasts() {
1718:                return Collections.unmodifiableList(outcasts);
1719:            }
1720:
1721:            public Collection<MUCRole> getModerators() {
1722:                List<MUCRole> moderators = new ArrayList<MUCRole>();
1723:                for (MUCRole role : occupants.values()) {
1724:                    if (MUCRole.Role.moderator == role.getRole()) {
1725:                        moderators.add(role);
1726:                    }
1727:                }
1728:                return moderators;
1729:            }
1730:
1731:            public Collection<MUCRole> getParticipants() {
1732:                List<MUCRole> participants = new ArrayList<MUCRole>();
1733:                for (MUCRole role : occupants.values()) {
1734:                    if (MUCRole.Role.participant == role.getRole()) {
1735:                        participants.add(role);
1736:                    }
1737:                }
1738:                return participants;
1739:            }
1740:
1741:            public Presence addModerator(JID jid, MUCRole senderRole)
1742:                    throws ForbiddenException {
1743:                if (MUCRole.Affiliation.admin != senderRole.getAffiliation()
1744:                        && MUCRole.Affiliation.owner != senderRole
1745:                                .getAffiliation()) {
1746:                    throw new ForbiddenException();
1747:                }
1748:                // Update the presence with the new role and inform all occupants
1749:                try {
1750:                    return changeOccupantRole(jid, MUCRole.Role.moderator);
1751:                } catch (NotAllowedException e) {
1752:                    // We should never receive this exception....in theory
1753:                    return null;
1754:                }
1755:            }
1756:
1757:            public Presence addParticipant(JID jid, String reason,
1758:                    MUCRole senderRole) throws NotAllowedException,
1759:                    ForbiddenException {
1760:                if (MUCRole.Role.moderator != senderRole.getRole()) {
1761:                    throw new ForbiddenException();
1762:                }
1763:                // Update the presence with the new role and inform all occupants
1764:                Presence updatedPresence = changeOccupantRole(jid,
1765:                        MUCRole.Role.participant);
1766:                if (updatedPresence != null) {
1767:                    Element frag = updatedPresence.getChildElement("x",
1768:                            "http://jabber.org/protocol/muc#user");
1769:                    // Add the reason why the user was granted voice
1770:                    if (reason != null && reason.trim().length() > 0) {
1771:                        frag.element("item").addElement("reason").setText(
1772:                                reason);
1773:                    }
1774:                }
1775:                return updatedPresence;
1776:            }
1777:
1778:            public Presence addVisitor(JID jid, MUCRole senderRole)
1779:                    throws NotAllowedException, ForbiddenException {
1780:                if (MUCRole.Role.moderator != senderRole.getRole()) {
1781:                    throw new ForbiddenException();
1782:                }
1783:                return changeOccupantRole(jid, MUCRole.Role.visitor);
1784:            }
1785:
1786:            public Presence kickOccupant(JID jid, JID actorJID, String reason)
1787:                    throws NotAllowedException {
1788:                // Update the presence with the new role and inform all occupants
1789:                Presence updatedPresence = changeOccupantRole(jid,
1790:                        MUCRole.Role.none);
1791:                if (updatedPresence != null) {
1792:                    Element frag = updatedPresence.getChildElement("x",
1793:                            "http://jabber.org/protocol/muc#user");
1794:
1795:                    // Add the status code 307 that indicates that the user was kicked
1796:                    frag.addElement("status").addAttribute("code", "307");
1797:                    // Add the reason why the user was kicked
1798:                    if (reason != null && reason.trim().length() > 0) {
1799:                        frag.element("item").addElement("reason").setText(
1800:                                reason);
1801:                    }
1802:
1803:                    // Effectively kick the occupant from the room
1804:                    kickPresence(updatedPresence, actorJID);
1805:                }
1806:                return updatedPresence;
1807:            }
1808:
1809:            /**
1810:             * Kicks the occupant from the room. This means that the occupant will receive an unavailable
1811:             * presence with the actor that initiated the kick (if any). The occupant will also be removed
1812:             * from the occupants lists.
1813:             * 
1814:             * @param kickPresence the presence of the occupant to kick from the room.
1815:             * @param actorJID The JID of the actor that initiated the kick or <tt>null</tt> if the info
1816:             * was not provided.
1817:             */
1818:            private void kickPresence(Presence kickPresence, JID actorJID) {
1819:                MUCRole kickedRole;
1820:                // Get the role to kick
1821:                kickedRole = occupants.get(kickPresence.getFrom().getResource()
1822:                        .toLowerCase());
1823:                if (kickedRole != null) {
1824:                    kickPresence = kickPresence.createCopy();
1825:                    // Add the actor's JID that kicked this user from the room
1826:                    if (actorJID != null && actorJID.toString().length() > 0) {
1827:                        Element frag = kickPresence.getChildElement("x",
1828:                                "http://jabber.org/protocol/muc#user");
1829:                        frag.element("item").addElement("actor").addAttribute(
1830:                                "jid", actorJID.toBareJID());
1831:                    }
1832:                    // Send the unavailable presence to the banned user
1833:                    kickedRole.send(kickPresence);
1834:                    // Remove the occupant from the room's occupants lists
1835:                    OccupantLeftEvent event = new OccupantLeftEvent(this ,
1836:                            kickedRole);
1837:                    event.setOriginator(true);
1838:                    event.run();
1839:
1840:                    // Remove the occupant from the room's occupants lists
1841:                    event = new OccupantLeftEvent(this , kickedRole);
1842:                    CacheFactory.doClusterTask(event);
1843:                }
1844:            }
1845:
1846:            public boolean canAnyoneDiscoverJID() {
1847:                return canAnyoneDiscoverJID;
1848:            }
1849:
1850:            public void setCanAnyoneDiscoverJID(boolean canAnyoneDiscoverJID) {
1851:                this .canAnyoneDiscoverJID = canAnyoneDiscoverJID;
1852:            }
1853:
1854:            public boolean canOccupantsChangeSubject() {
1855:                return canOccupantsChangeSubject;
1856:            }
1857:
1858:            public void setCanOccupantsChangeSubject(
1859:                    boolean canOccupantsChangeSubject) {
1860:                this .canOccupantsChangeSubject = canOccupantsChangeSubject;
1861:            }
1862:
1863:            public boolean canOccupantsInvite() {
1864:                return canOccupantsInvite;
1865:            }
1866:
1867:            public void setCanOccupantsInvite(boolean canOccupantsInvite) {
1868:                this .canOccupantsInvite = canOccupantsInvite;
1869:            }
1870:
1871:            public String getNaturalLanguageName() {
1872:                return naturalLanguageName;
1873:            }
1874:
1875:            public void setNaturalLanguageName(String naturalLanguageName) {
1876:                this .naturalLanguageName = naturalLanguageName;
1877:            }
1878:
1879:            public String getDescription() {
1880:                return description;
1881:            }
1882:
1883:            public void setDescription(String description) {
1884:                this .description = description;
1885:            }
1886:
1887:            public boolean isMembersOnly() {
1888:                return membersOnly;
1889:            }
1890:
1891:            public List<Presence> setMembersOnly(boolean membersOnly) {
1892:                List<Presence> presences = new ArrayList<Presence>();
1893:                if (membersOnly && !this .membersOnly) {
1894:                    // If the room was not members-only and now it is, kick occupants that aren't member
1895:                    // of the room
1896:                    for (MUCRole occupant : occupants.values()) {
1897:                        if (occupant.getAffiliation().compareTo(
1898:                                MUCRole.Affiliation.member) > 0) {
1899:                            try {
1900:                                presences
1901:                                        .add(kickOccupant(
1902:                                                occupant.getRoleAddress(),
1903:                                                null,
1904:                                                LocaleUtils
1905:                                                        .getLocalizedString("muc.roomIsNowMembersOnly")));
1906:                            } catch (NotAllowedException e) {
1907:                                Log.error(e);
1908:                            }
1909:                        }
1910:                    }
1911:                }
1912:                this .membersOnly = membersOnly;
1913:                return presences;
1914:            }
1915:
1916:            public boolean isLogEnabled() {
1917:                return logEnabled;
1918:            }
1919:
1920:            public void setLogEnabled(boolean logEnabled) {
1921:                this .logEnabled = logEnabled;
1922:            }
1923:
1924:            public void setLoginRestrictedToNickname(boolean restricted) {
1925:                this .loginRestrictedToNickname = restricted;
1926:            }
1927:
1928:            public boolean isLoginRestrictedToNickname() {
1929:                return loginRestrictedToNickname;
1930:            }
1931:
1932:            public void setChangeNickname(boolean canChange) {
1933:                this .canChangeNickname = canChange;
1934:            }
1935:
1936:            public boolean canChangeNickname() {
1937:                return canChangeNickname;
1938:            }
1939:
1940:            public void setRegistrationEnabled(boolean registrationEnabled) {
1941:                this .registrationEnabled = registrationEnabled;
1942:            }
1943:
1944:            public boolean isRegistrationEnabled() {
1945:                return registrationEnabled;
1946:            }
1947:
1948:            public int getMaxUsers() {
1949:                return maxUsers;
1950:            }
1951:
1952:            public void setMaxUsers(int maxUsers) {
1953:                this .maxUsers = maxUsers;
1954:            }
1955:
1956:            public boolean isModerated() {
1957:                return moderated;
1958:            }
1959:
1960:            public void setModerated(boolean moderated) {
1961:                this .moderated = moderated;
1962:            }
1963:
1964:            public String getPassword() {
1965:                return password;
1966:            }
1967:
1968:            public void setPassword(String password) {
1969:                this .password = password;
1970:            }
1971:
1972:            public boolean isPasswordProtected() {
1973:                return password != null && password.trim().length() > 0;
1974:            }
1975:
1976:            public boolean isPersistent() {
1977:                return persistent;
1978:            }
1979:
1980:            public boolean wasSavedToDB() {
1981:                return isPersistent() && savedToDB;
1982:            }
1983:
1984:            public void setSavedToDB(boolean saved) {
1985:                this .savedToDB = saved;
1986:            }
1987:
1988:            public void setPersistent(boolean persistent) {
1989:                this .persistent = persistent;
1990:            }
1991:
1992:            public boolean isPublicRoom() {
1993:                return !isDestroyed && publicRoom;
1994:            }
1995:
1996:            public void setPublicRoom(boolean publicRoom) {
1997:                this .publicRoom = publicRoom;
1998:            }
1999:
2000:            public List<String> getRolesToBroadcastPresence() {
2001:                return Collections.unmodifiableList(rolesToBroadcastPresence);
2002:            }
2003:
2004:            public void setRolesToBroadcastPresence(
2005:                    List<String> rolesToBroadcastPresence) {
2006:                // TODO If the list changes while there are occupants in the room we must send available or
2007:                // unavailable presences of the affected occupants to the rest of the occupants
2008:                this .rolesToBroadcastPresence = rolesToBroadcastPresence;
2009:            }
2010:
2011:            /**
2012:             * Returns true if we need to check whether a presence could be sent or not.
2013:             *
2014:             * @return true if we need to check whether a presence could be sent or not.
2015:             */
2016:            private boolean hasToCheckRoleToBroadcastPresence() {
2017:                // For performance reasons the check is done based on the size of the collection.
2018:                return rolesToBroadcastPresence.size() < 3;
2019:            }
2020:
2021:            public boolean canBroadcastPresence(String roleToBroadcast) {
2022:                return "none".equals(roleToBroadcast)
2023:                        || rolesToBroadcastPresence.contains(roleToBroadcast);
2024:            }
2025:
2026:            public void lock(MUCRole senderRole) throws ForbiddenException {
2027:                if (MUCRole.Affiliation.owner != senderRole.getAffiliation()) {
2028:                    throw new ForbiddenException();
2029:                }
2030:                if (isLocked()) {
2031:                    // Do nothing if the room was already locked
2032:                    return;
2033:                }
2034:                setLocked(true);
2035:                if (senderRole.getUserAddress() != null) {
2036:                    // Send to the occupant that locked the room a message saying so
2037:                    Message message = new Message();
2038:                    message.setType(Message.Type.groupchat);
2039:                    message.setBody(LocaleUtils
2040:                            .getLocalizedString("muc.locked"));
2041:                    message.setFrom(getRole().getRoleAddress());
2042:                    senderRole.send(message);
2043:                }
2044:            }
2045:
2046:            public void unlock(MUCRole senderRole) throws ForbiddenException {
2047:                if (MUCRole.Affiliation.owner != senderRole.getAffiliation()) {
2048:                    throw new ForbiddenException();
2049:                }
2050:                if (!isLocked()) {
2051:                    // Do nothing if the room was already unlocked
2052:                    return;
2053:                }
2054:                setLocked(false);
2055:                if (senderRole.getUserAddress() != null) {
2056:                    // Send to the occupant that unlocked the room a message saying so
2057:                    Message message = new Message();
2058:                    message.setType(Message.Type.groupchat);
2059:                    message.setBody(LocaleUtils
2060:                            .getLocalizedString("muc.unlocked"));
2061:                    message.setFrom(getRole().getRoleAddress());
2062:                    senderRole.send(message);
2063:                }
2064:            }
2065:
2066:            private void setLocked(boolean locked) {
2067:                if (locked) {
2068:                    this .lockedTime = System.currentTimeMillis();
2069:                } else {
2070:                    this .lockedTime = 0;
2071:                }
2072:                MUCPersistenceManager.updateRoomLock(this );
2073:            }
2074:
2075:            /**
2076:             * Sets the date when the room was locked. Initially when the room is created it is locked so
2077:             * the locked date is the creation date of the room. Afterwards, the room may be manually
2078:             * locked and unlocked so the locked date may be in these cases different than the creation
2079:             * date. A Date with time 0 means that the the room is unlocked.
2080:             *
2081:             * @param lockedTime the date when the room was locked.
2082:             */
2083:            void setLockedDate(Date lockedTime) {
2084:                this .lockedTime = lockedTime.getTime();
2085:            }
2086:
2087:            /**
2088:             * Returns the date when the room was locked. Initially when the room is created it is locked so
2089:             * the locked date is the creation date of the room. Afterwards, the room may be manually
2090:             * locked and unlocked so the locked date may be in these cases different than the creation
2091:             * date. When the room is unlocked a Date with time 0 is returned.
2092:             *
2093:             * @return the date when the room was locked.
2094:             */
2095:            Date getLockedDate() {
2096:                return new Date(lockedTime);
2097:            }
2098:
2099:            public List<Presence> addAdmins(List<String> newAdmins,
2100:                    MUCRole senderRole) throws ForbiddenException,
2101:                    ConflictException {
2102:                List<Presence> answer = new ArrayList<Presence>(newAdmins
2103:                        .size());
2104:                for (String newAdmin : newAdmins) {
2105:                    if (newAdmin.trim().length() > 0
2106:                            && !admins.contains(newAdmin)) {
2107:                        answer.addAll(addAdmin(newAdmin, senderRole));
2108:                    }
2109:                }
2110:                return answer;
2111:            }
2112:
2113:            public List<Presence> addOwners(List<String> newOwners,
2114:                    MUCRole senderRole) throws ForbiddenException {
2115:                List<Presence> answer = new ArrayList<Presence>(newOwners
2116:                        .size());
2117:                for (String newOwner : newOwners) {
2118:                    if (newOwner.trim().length() > 0
2119:                            && !owners.contains(newOwner)) {
2120:                        answer.addAll(addOwner(newOwner, senderRole));
2121:                    }
2122:                }
2123:                return answer;
2124:            }
2125:
2126:            public void saveToDB() {
2127:                // Make the room persistent
2128:                MUCPersistenceManager.saveToDB(this );
2129:                if (!savedToDB) {
2130:                    // Set that the room is now in the DB
2131:                    savedToDB = true;
2132:                    // Save the existing room owners to the DB
2133:                    for (String owner : owners) {
2134:                        MUCPersistenceManager.saveAffiliationToDB(this , owner,
2135:                                null, MUCRole.Affiliation.owner,
2136:                                MUCRole.Affiliation.none);
2137:                    }
2138:                    // Save the existing room admins to the DB
2139:                    for (String admin : admins) {
2140:                        MUCPersistenceManager.saveAffiliationToDB(this , admin,
2141:                                null, MUCRole.Affiliation.admin,
2142:                                MUCRole.Affiliation.none);
2143:                    }
2144:                    // Save the existing room members to the DB
2145:                    for (String bareJID : members.keySet()) {
2146:                        MUCPersistenceManager.saveAffiliationToDB(this ,
2147:                                bareJID, members.get(bareJID),
2148:                                MUCRole.Affiliation.member,
2149:                                MUCRole.Affiliation.none);
2150:                    }
2151:                    // Save the existing room outcasts to the DB
2152:                    for (String outcast : outcasts) {
2153:                        MUCPersistenceManager.saveAffiliationToDB(this ,
2154:                                outcast, null, MUCRole.Affiliation.outcast,
2155:                                MUCRole.Affiliation.none);
2156:                    }
2157:                }
2158:            }
2159:
2160:            public void writeExternal(ObjectOutput out) throws IOException {
2161:                ExternalizableUtil.getInstance().writeSafeUTF(out, name);
2162:                ExternalizableUtil.getInstance().writeLong(out, startTime);
2163:                ExternalizableUtil.getInstance().writeLong(out, lockedTime);
2164:                ExternalizableUtil.getInstance().writeStringList(out, owners);
2165:                ExternalizableUtil.getInstance().writeStringList(out, admins);
2166:                ExternalizableUtil.getInstance().writeStringMap(out, members);
2167:                ExternalizableUtil.getInstance().writeStringList(out, outcasts);
2168:                ExternalizableUtil.getInstance().writeSafeUTF(out,
2169:                        naturalLanguageName);
2170:                ExternalizableUtil.getInstance().writeSafeUTF(out, description);
2171:                ExternalizableUtil.getInstance().writeBoolean(out,
2172:                        canOccupantsChangeSubject);
2173:                ExternalizableUtil.getInstance().writeInt(out, maxUsers);
2174:                ExternalizableUtil.getInstance().writeStringList(out,
2175:                        rolesToBroadcastPresence);
2176:                ExternalizableUtil.getInstance().writeBoolean(out, publicRoom);
2177:                ExternalizableUtil.getInstance().writeBoolean(out, persistent);
2178:                ExternalizableUtil.getInstance().writeBoolean(out, moderated);
2179:                ExternalizableUtil.getInstance().writeBoolean(out, membersOnly);
2180:                ExternalizableUtil.getInstance().writeBoolean(out,
2181:                        canOccupantsInvite);
2182:                ExternalizableUtil.getInstance().writeSafeUTF(out, password);
2183:                ExternalizableUtil.getInstance().writeBoolean(out,
2184:                        canAnyoneDiscoverJID);
2185:                ExternalizableUtil.getInstance().writeBoolean(out, logEnabled);
2186:                ExternalizableUtil.getInstance().writeBoolean(out,
2187:                        loginRestrictedToNickname);
2188:                ExternalizableUtil.getInstance().writeBoolean(out,
2189:                        canChangeNickname);
2190:                ExternalizableUtil.getInstance().writeBoolean(out,
2191:                        registrationEnabled);
2192:                ExternalizableUtil.getInstance().writeSafeUTF(out, subject);
2193:                ExternalizableUtil.getInstance().writeLong(out, roomID);
2194:                ExternalizableUtil.getInstance().writeLong(out,
2195:                        creationDate.getTime());
2196:                ExternalizableUtil.getInstance().writeLong(out,
2197:                        modificationDate.getTime());
2198:                ExternalizableUtil.getInstance().writeBoolean(out,
2199:                        emptyDate != null);
2200:                if (emptyDate != null) {
2201:                    ExternalizableUtil.getInstance().writeLong(out,
2202:                            emptyDate.getTime());
2203:                }
2204:                ExternalizableUtil.getInstance().writeBoolean(out, savedToDB);
2205:            }
2206:
2207:            public void readExternal(ObjectInput in) throws IOException,
2208:                    ClassNotFoundException {
2209:                name = ExternalizableUtil.getInstance().readSafeUTF(in);
2210:                startTime = ExternalizableUtil.getInstance().readLong(in);
2211:                lockedTime = ExternalizableUtil.getInstance().readLong(in);
2212:                owners.addAll(ExternalizableUtil.getInstance().readStringList(
2213:                        in));
2214:                admins.addAll(ExternalizableUtil.getInstance().readStringList(
2215:                        in));
2216:                members.putAll(ExternalizableUtil.getInstance().readStringMap(
2217:                        in));
2218:                outcasts.addAll(ExternalizableUtil.getInstance()
2219:                        .readStringList(in));
2220:                naturalLanguageName = ExternalizableUtil.getInstance()
2221:                        .readSafeUTF(in);
2222:                description = ExternalizableUtil.getInstance().readSafeUTF(in);
2223:                canOccupantsChangeSubject = ExternalizableUtil.getInstance()
2224:                        .readBoolean(in);
2225:                maxUsers = ExternalizableUtil.getInstance().readInt(in);
2226:                rolesToBroadcastPresence.addAll(ExternalizableUtil
2227:                        .getInstance().readStringList(in));
2228:                publicRoom = ExternalizableUtil.getInstance().readBoolean(in);
2229:                persistent = ExternalizableUtil.getInstance().readBoolean(in);
2230:                moderated = ExternalizableUtil.getInstance().readBoolean(in);
2231:                membersOnly = ExternalizableUtil.getInstance().readBoolean(in);
2232:                canOccupantsInvite = ExternalizableUtil.getInstance()
2233:                        .readBoolean(in);
2234:                password = ExternalizableUtil.getInstance().readSafeUTF(in);
2235:                canAnyoneDiscoverJID = ExternalizableUtil.getInstance()
2236:                        .readBoolean(in);
2237:                logEnabled = ExternalizableUtil.getInstance().readBoolean(in);
2238:                loginRestrictedToNickname = ExternalizableUtil.getInstance()
2239:                        .readBoolean(in);
2240:                canChangeNickname = ExternalizableUtil.getInstance()
2241:                        .readBoolean(in);
2242:                registrationEnabled = ExternalizableUtil.getInstance()
2243:                        .readBoolean(in);
2244:                subject = ExternalizableUtil.getInstance().readSafeUTF(in);
2245:                roomID = ExternalizableUtil.getInstance().readLong(in);
2246:                creationDate = new Date(ExternalizableUtil.getInstance()
2247:                        .readLong(in));
2248:                modificationDate = new Date(ExternalizableUtil.getInstance()
2249:                        .readLong(in));
2250:                if (ExternalizableUtil.getInstance().readBoolean(in)) {
2251:                    emptyDate = new Date(ExternalizableUtil.getInstance()
2252:                            .readLong(in));
2253:                }
2254:                savedToDB = ExternalizableUtil.getInstance().readBoolean(in);
2255:
2256:                server = (MultiUserChatServerImpl) XMPPServer.getInstance()
2257:                        .getMultiUserChatServer();
2258:                roomHistory = new MUCRoomHistory(this , new HistoryStrategy(
2259:                        server.getHistoryStrategy()));
2260:
2261:                PacketRouter packetRouter = XMPPServer.getInstance()
2262:                        .getPacketRouter();
2263:                this .iqOwnerHandler = new IQOwnerHandler(this , packetRouter);
2264:                this .iqAdminHandler = new IQAdminHandler(this , packetRouter);
2265:
2266:                server = (MultiUserChatServerImpl) XMPPServer.getInstance()
2267:                        .getMultiUserChatServer();
2268:                router = packetRouter;
2269:            }
2270:
2271:            public void updateConfiguration(LocalMUCRoom otherRoom) {
2272:                startTime = otherRoom.startTime;
2273:                lockedTime = otherRoom.lockedTime;
2274:                owners = otherRoom.owners;
2275:                admins = otherRoom.admins;
2276:                members = otherRoom.members;
2277:                outcasts = otherRoom.outcasts;
2278:                naturalLanguageName = otherRoom.naturalLanguageName;
2279:                description = otherRoom.description;
2280:                canOccupantsChangeSubject = otherRoom.canOccupantsChangeSubject;
2281:                maxUsers = otherRoom.maxUsers;
2282:                rolesToBroadcastPresence = otherRoom.rolesToBroadcastPresence;
2283:                publicRoom = otherRoom.publicRoom;
2284:                persistent = otherRoom.persistent;
2285:                moderated = otherRoom.moderated;
2286:                membersOnly = otherRoom.membersOnly;
2287:                canOccupantsInvite = otherRoom.canOccupantsInvite;
2288:                password = otherRoom.password;
2289:                canAnyoneDiscoverJID = otherRoom.canAnyoneDiscoverJID;
2290:                logEnabled = otherRoom.logEnabled;
2291:                loginRestrictedToNickname = otherRoom.loginRestrictedToNickname;
2292:                canChangeNickname = otherRoom.canChangeNickname;
2293:                registrationEnabled = otherRoom.registrationEnabled;
2294:                subject = otherRoom.subject;
2295:                roomID = otherRoom.roomID;
2296:                creationDate = otherRoom.creationDate;
2297:                modificationDate = otherRoom.modificationDate;
2298:                emptyDate = otherRoom.emptyDate;
2299:                savedToDB = otherRoom.savedToDB;
2300:            }
2301:
2302:            /*
2303:             * (non-Javadoc)
2304:             * @see org.jivesoftware.util.resultsetmanager.Result#getUID()
2305:             */
2306:            public String getUID() {
2307:                // name is unique for each one particular MUC service.
2308:                return name;
2309:            }
2310:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.