Source Code Cross Referenced for BaseMessageService.java in  » ERP-CRM-Financial » sakai » org » sakaiproject » message » impl » 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 » ERP CRM Financial » sakai » org.sakaiproject.message.impl 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /**********************************************************************************
0002:         * $URL: https://source.sakaiproject.org/svn/message/tags/sakai_2-4-1/message-impl/impl/src/java/org/sakaiproject/message/impl/BaseMessageService.java $
0003:         * $Id: BaseMessageService.java 22951 2007-03-19 17:51:47Z josrodri@iupui.edu $
0004:         ***********************************************************************************
0005:         *
0006:         * Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation.
0007:         * 
0008:         * Licensed under the Educational Community License, Version 1.0 (the "License"); 
0009:         * you may not use this file except in compliance with the License. 
0010:         * You may obtain a copy of the License at
0011:         * 
0012:         *      http://www.opensource.org/licenses/ecl1.php
0013:         * 
0014:         * Unless required by applicable law or agreed to in writing, software 
0015:         * distributed under the License is distributed on an "AS IS" BASIS, 
0016:         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
0017:         * See the License for the specific language governing permissions and 
0018:         * limitations under the License.
0019:         *
0020:         **********************************************************************************/package org.sakaiproject.message.impl;
0021:
0022:        import java.io.ByteArrayOutputStream;
0023:        import java.io.OutputStream;
0024:        import java.io.OutputStreamWriter;
0025:        import java.util.Collection;
0026:        import java.util.Collections;
0027:        import java.util.HashMap;
0028:        import java.util.HashSet;
0029:        import java.util.Hashtable;
0030:        import java.util.Iterator;
0031:        import java.util.List;
0032:        import java.util.Map;
0033:        import java.util.Observable;
0034:        import java.util.Properties;
0035:        import java.util.Set;
0036:        import java.util.Stack;
0037:        import java.util.Vector;
0038:
0039:        import javax.servlet.http.HttpServletRequest;
0040:        import javax.servlet.http.HttpServletResponse;
0041:
0042:        import org.apache.commons.logging.Log;
0043:        import org.apache.commons.logging.LogFactory;
0044:        import org.sakaiproject.api.app.scheduler.ScheduledInvocationManager;
0045:        import org.sakaiproject.authz.api.AuthzGroupService;
0046:        import org.sakaiproject.authz.api.AuthzPermissionException;
0047:        import org.sakaiproject.authz.api.GroupNotDefinedException;
0048:        import org.sakaiproject.authz.api.SecurityService;
0049:        import org.sakaiproject.component.api.ServerConfigurationService;
0050:        import org.sakaiproject.component.cover.ComponentManager;
0051:        import org.sakaiproject.entity.api.Entity;
0052:        import org.sakaiproject.entity.api.Summary;
0053:        import org.sakaiproject.entity.api.EntityManager;
0054:        import org.sakaiproject.entity.api.EntityNotDefinedException;
0055:        import org.sakaiproject.entity.api.EntityPermissionException;
0056:        import org.sakaiproject.entity.api.HttpAccess;
0057:        import org.sakaiproject.entity.api.Reference;
0058:        import org.sakaiproject.entity.api.ResourceProperties;
0059:        import org.sakaiproject.entity.api.ResourcePropertiesEdit;
0060:        import org.sakaiproject.event.api.Event;
0061:        import org.sakaiproject.event.api.EventTrackingService;
0062:        import org.sakaiproject.event.api.NotificationService;
0063:        import org.sakaiproject.exception.IdInvalidException;
0064:        import org.sakaiproject.exception.IdUnusedException;
0065:        import org.sakaiproject.exception.IdUsedException;
0066:        import org.sakaiproject.exception.InUseException;
0067:        import org.sakaiproject.exception.PermissionException;
0068:        import org.sakaiproject.id.api.IdManager;
0069:        import org.sakaiproject.javax.Filter;
0070:        import org.sakaiproject.memory.api.Cache;
0071:        import org.sakaiproject.memory.api.CacheRefresher;
0072:        import org.sakaiproject.memory.api.MemoryService;
0073:        import org.sakaiproject.message.api.Message;
0074:        import org.sakaiproject.message.api.MessageChannel;
0075:        import org.sakaiproject.message.api.MessageChannelEdit;
0076:        import org.sakaiproject.message.api.MessageEdit;
0077:        import org.sakaiproject.message.api.MessageHeader;
0078:        import org.sakaiproject.message.api.MessageHeaderEdit;
0079:        import org.sakaiproject.message.api.MessageService;
0080:        import org.sakaiproject.site.api.Group;
0081:        import org.sakaiproject.site.api.Site;
0082:        import org.sakaiproject.site.api.SiteService;
0083:        import org.sakaiproject.site.api.ToolConfiguration;
0084:        import org.sakaiproject.thread_local.api.ThreadLocalManager;
0085:        import org.sakaiproject.time.api.Time;
0086:        import org.sakaiproject.time.api.TimeService;
0087:        import org.sakaiproject.tool.api.Session;
0088:        import org.sakaiproject.tool.api.SessionBindingEvent;
0089:        import org.sakaiproject.tool.api.SessionBindingListener;
0090:        import org.sakaiproject.tool.api.SessionManager;
0091:        import org.sakaiproject.tool.api.ToolSession;
0092:        import org.sakaiproject.user.api.User;
0093:        import org.sakaiproject.user.api.UserDirectoryService;
0094:        import org.sakaiproject.user.api.UserNotDefinedException;
0095:        import org.sakaiproject.util.BaseResourcePropertiesEdit;
0096:        import org.sakaiproject.util.EntityCollections;
0097:        import org.sakaiproject.util.FormattedText;
0098:        import org.sakaiproject.util.StorageUser;
0099:        import org.sakaiproject.util.Validator;
0100:        import org.w3c.dom.Document;
0101:        import org.w3c.dom.Element;
0102:        import org.w3c.dom.Node;
0103:        import org.w3c.dom.NodeList;
0104:
0105:        /**
0106:         * BaseMessageService is...
0107:         */
0108:        public abstract class BaseMessageService implements  MessageService,
0109:                StorageUser, CacheRefresher {
0110:            /** Our logger. */
0111:            private static Log M_log = LogFactory
0112:                    .getLog(BaseMessageService.class);
0113:
0114:            /** A Storage object for persistent storage. */
0115:            protected Storage m_storage = null;
0116:
0117:            /** A Cache object for caching: channels keyed by reference. (if m_caching) */
0118:            protected Cache m_channelCache = null;
0119:
0120:            /** A bunch of caches for messages: keyed by channel id, the cache is keyed by message reference. (if m_caching) */
0121:            protected Hashtable m_messageCaches = null;
0122:
0123:            private static final String CHANNEL_PROP = "channel";
0124:            private static final String PROPERTIES = "properties";
0125:            private static final String PROPERTY = "property";
0126:            private static final String SYNOPTIC_TOOL = "synoptic_tool";
0127:            private static final String NAME = "name";
0128:            private static final String VALUE = "value";
0129:            private static final String STATE_UPDATE = "update";
0130:
0131:            /** added to allow for scheduled notifications */
0132:            private static final String SCHED_INV_UUID = "schInvUuid";
0133:            private static final String SCHINV_DELETE_EVENT = "schInv.delete";
0134:
0135:            /**
0136:             * Access this service from the inner classes.
0137:             */
0138:            protected BaseMessageService service() {
0139:                return this ;
0140:            }
0141:
0142:            /**********************************************************************************************************************************************************************************************************************************************************
0143:             * Constructors, Dependencies and their setter methods
0144:             *********************************************************************************************************************************************************************************************************************************************************/
0145:
0146:            /** Dependency: MemoryService. */
0147:            protected MemoryService m_memoryService = null;
0148:
0149:            /**
0150:             * Dependency: MemoryService.
0151:             * 
0152:             * @param service
0153:             *        The MemoryService.
0154:             */
0155:            public void setMemoryService(MemoryService service) {
0156:                m_memoryService = service;
0157:            }
0158:
0159:            /** Dependency: ServerConfigurationService. */
0160:            protected ServerConfigurationService m_serverConfigurationService = null;
0161:
0162:            /**
0163:             * Dependency: ServerConfigurationService.
0164:             * 
0165:             * @param service
0166:             *        The ServerConfigurationService.
0167:             */
0168:            public void setServerConfigurationService(
0169:                    ServerConfigurationService service) {
0170:                m_serverConfigurationService = service;
0171:            }
0172:
0173:            /** Dependency: SessionManager. */
0174:            protected SessionManager m_sessionManager = null;
0175:
0176:            /**
0177:             * Dependency: SessionManager.
0178:             * 
0179:             * @param service
0180:             *        The SessionManager.
0181:             */
0182:            public void setSessionManager(SessionManager service) {
0183:                m_sessionManager = service;
0184:            }
0185:
0186:            /** Dependency: AuthzGroupService. */
0187:            protected AuthzGroupService m_authzGroupService = null;
0188:
0189:            /**
0190:             * Dependency: AuthzGroupService.
0191:             * 
0192:             * @param service
0193:             *        The AuthzGroupService.
0194:             */
0195:            public void setAuthzGroupService(AuthzGroupService service) {
0196:                m_authzGroupService = service;
0197:            }
0198:
0199:            /** Dependency: SecurityService. */
0200:            protected SecurityService m_securityService = null;
0201:
0202:            /**
0203:             * Dependency: SecurityService.
0204:             * 
0205:             * @param service
0206:             *        The SecurityService.
0207:             */
0208:            public void setSecurityService(SecurityService service) {
0209:                m_securityService = service;
0210:            }
0211:
0212:            /** Dependency: TimeService. */
0213:            protected TimeService m_timeService = null;
0214:
0215:            /**
0216:             * Dependency: TimeService.
0217:             * 
0218:             * @param service
0219:             *        The TimeService.
0220:             */
0221:            public void setTimeService(TimeService service) {
0222:                m_timeService = service;
0223:            }
0224:
0225:            /** Dependency: EventTrackingService. */
0226:            protected EventTrackingService m_eventTrackingService = null;
0227:
0228:            /**
0229:             * Dependency: EventTrackingService.
0230:             * 
0231:             * @param service
0232:             *        The EventTrackingService.
0233:             */
0234:            public void setEventTrackingService(EventTrackingService service) {
0235:                m_eventTrackingService = service;
0236:            }
0237:
0238:            /** Dependency: IdManager. */
0239:            protected IdManager m_idManager = null;
0240:
0241:            /**
0242:             * Dependency: IdManager.
0243:             * 
0244:             * @param service
0245:             *        The IdManager.
0246:             */
0247:            public void setIdManager(IdManager service) {
0248:                m_idManager = service;
0249:            }
0250:
0251:            /** Dependency: SiteService. */
0252:            protected SiteService m_siteService = null;
0253:
0254:            /**
0255:             * Dependency: SiteService.
0256:             * 
0257:             * @param service
0258:             *        The SiteService.
0259:             */
0260:            public void setSiteService(SiteService service) {
0261:                m_siteService = service;
0262:            }
0263:
0264:            /** Dependency: UserDirectoryService. */
0265:            protected UserDirectoryService m_userDirectoryService = null;
0266:
0267:            /**
0268:             * Dependency: UserDirectoryService.
0269:             * 
0270:             * @param service
0271:             *        The UserDirectoryService.
0272:             */
0273:            public void setUserDirectoryService(UserDirectoryService service) {
0274:                m_userDirectoryService = service;
0275:            }
0276:
0277:            /** Dependency: ThreadLocalManager. */
0278:            protected ThreadLocalManager m_threadLocalManager = null;
0279:
0280:            /**
0281:             * Dependency: ThreadLocalManager.
0282:             * 
0283:             * @param service
0284:             *        The ThreadLocalManager.
0285:             */
0286:            public void setThreadLocalManager(ThreadLocalManager service) {
0287:                m_threadLocalManager = service;
0288:            }
0289:
0290:            /** Configuration: cache, or not. */
0291:            protected boolean m_caching = false;
0292:
0293:            /**
0294:             * Configuration: set the locks-in-db
0295:             * 
0296:             * @param path
0297:             *        The storage path.
0298:             */
0299:            public void setCaching(String value) {
0300:                m_caching = new Boolean(value).booleanValue();
0301:            }
0302:
0303:            /** Dependency: EntityManager. */
0304:            protected EntityManager m_entityManager = null;
0305:
0306:            /**
0307:             * Dependency: EntityManager.
0308:             * 
0309:             * @param service
0310:             *        The EntityManager.
0311:             */
0312:            public void setEntityManager(EntityManager service) {
0313:                m_entityManager = service;
0314:            }
0315:
0316:            /**********************************************************************************************************************************************************************************************************************************************************
0317:             * Init and Destroy
0318:             *********************************************************************************************************************************************************************************************************************************************************/
0319:
0320:            /**
0321:             * Final initialization, once all dependencies are set.
0322:             */
0323:            public void init() {
0324:                try {
0325:                    // construct a storage helper and read
0326:                    m_storage = newStorage();
0327:                    m_storage.open();
0328:
0329:                    // make the channel cache
0330:                    if (m_caching) {
0331:                        m_channelCache = m_memoryService.newCache(this ,
0332:                                getAccessPoint(true) + Entity.SEPARATOR
0333:                                        + REF_TYPE_CHANNEL + Entity.SEPARATOR);
0334:
0335:                        // make the table to hold the message caches
0336:                        m_messageCaches = new Hashtable();
0337:                    }
0338:
0339:                    M_log.info("init(): caching: " + m_caching);
0340:                } catch (Throwable t) {
0341:                    M_log.warn("init(): ", t);
0342:                }
0343:
0344:                // entity producer registration in the extension services
0345:
0346:                // Functions are registered in the extension services
0347:
0348:            } // init
0349:
0350:            /**
0351:             * Destroy
0352:             */
0353:            public void destroy() {
0354:                if (m_caching) {
0355:                    m_channelCache.destroy();
0356:                    m_channelCache = null;
0357:
0358:                    m_messageCaches.clear();
0359:                    m_messageCaches = null;
0360:                }
0361:
0362:                m_storage.close();
0363:                m_storage = null;
0364:
0365:                M_log.info("destroy()");
0366:            }
0367:
0368:            /**********************************************************************************************************************************************************************************************************************************************************
0369:             * Abstractions, etc.
0370:             *********************************************************************************************************************************************************************************************************************************************************/
0371:
0372:            /**
0373:             * Report the Service API name being implemented.
0374:             */
0375:            protected abstract String serviceName();
0376:
0377:            /**
0378:             * Construct a Storage object.
0379:             * 
0380:             * @return The new storage object.
0381:             */
0382:            protected abstract Storage newStorage();
0383:
0384:            /**
0385:             * Construct a new message header from XML in a DOM element.
0386:             * 
0387:             * @param msg
0388:             *        The message to own this header.
0389:             * @param id
0390:             *        The message Id.
0391:             * @return The new message header.
0392:             */
0393:            protected abstract MessageHeaderEdit newMessageHeader(Message msg,
0394:                    String id);
0395:
0396:            /**
0397:             * Construct a new message header from XML in a DOM element.
0398:             * 
0399:             * @param msg
0400:             *        The message to own this header.
0401:             * @param el
0402:             *        The XML DOM element that has the header information.
0403:             * @return The new message header.
0404:             */
0405:            protected abstract MessageHeaderEdit newMessageHeader(Message msg,
0406:                    Element el);
0407:
0408:            /**
0409:             * Construct a new message header as a copy of another.
0410:             * 
0411:             * @param msg
0412:             *        The message to own this header.
0413:             * @param other
0414:             *        The other header to copy.
0415:             * @return The new message header.
0416:             */
0417:            protected abstract MessageHeaderEdit newMessageHeader(Message msg,
0418:                    MessageHeader other);
0419:
0420:            /**
0421:             * Form a tracking event string based on a security function string.
0422:             * 
0423:             * @param secure
0424:             *        The security function string.
0425:             * @return The event tracking string.
0426:             */
0427:            protected abstract String eventId(String secure);
0428:
0429:            /**
0430:             * Return the reference rooot for use in resource references and urls.
0431:             * 
0432:             * @return The reference rooot for use in resource references and urls.
0433:             */
0434:            protected abstract String getReferenceRoot();
0435:
0436:            /**********************************************************************************************************************************************************************************************************************************************************
0437:             * MessageService implementation
0438:             *********************************************************************************************************************************************************************************************************************************************************/
0439:
0440:            /**
0441:             * Access the partial URL that forms the root of resource URLs.
0442:             * 
0443:             * @param relative
0444:             *        if true, form within the access path only (i.e. starting with /msg)
0445:             * @return the partial URL that forms the root of resource URLs.
0446:             */
0447:            protected String getAccessPoint(boolean relative) {
0448:                return (relative ? "" : m_serverConfigurationService
0449:                        .getAccessUrl())
0450:                        + getReferenceRoot();
0451:
0452:            } // getAccessPoint
0453:
0454:            /**
0455:             * Access the internal reference which can be used to access the channel from within the system.
0456:             * 
0457:             * @param context
0458:             *        The context.
0459:             * @param id
0460:             *        The channel id.
0461:             * @return The the internal reference which can be used to access the channel from within the system.
0462:             */
0463:            public String channelReference(String context, String id) {
0464:                return getAccessPoint(true) + Entity.SEPARATOR
0465:                        + REF_TYPE_CHANNEL + Entity.SEPARATOR + context
0466:                        + Entity.SEPARATOR + id;
0467:
0468:            } // channelReference
0469:
0470:            /**
0471:             * Access the internal reference which can be used to access the message from within the system.
0472:             * 
0473:             * @param context
0474:             *        The context.
0475:             * @param channelId
0476:             *        The channel id.
0477:             * @param id
0478:             *        The message id.
0479:             * @return The the internal reference which can be used to access the message from within the system.
0480:             */
0481:            public String messageReference(String context, String channelId,
0482:                    String id) {
0483:                return getAccessPoint(true) + Entity.SEPARATOR
0484:                        + REF_TYPE_MESSAGE + Entity.SEPARATOR + context
0485:                        + Entity.SEPARATOR + channelId + Entity.SEPARATOR + id;
0486:
0487:            } // messageReference
0488:
0489:            /**
0490:             * Access the internal reference which can be used to access the message from within the system.
0491:             * 
0492:             * @param channelRef
0493:             *        The channel reference.
0494:             * @param id
0495:             *        The message id.
0496:             * @return The the internal reference which can be used to access the message from within the system.
0497:             */
0498:            public String messageReference(String channelRef, String id) {
0499:                StringBuffer buf = new StringBuffer();
0500:
0501:                // start with the channel ref
0502:                buf.append(channelRef);
0503:
0504:                // swap channel for msg
0505:                int pos = buf.indexOf(REF_TYPE_CHANNEL);
0506:                buf.replace(pos, pos + REF_TYPE_CHANNEL.length(),
0507:                        REF_TYPE_MESSAGE);
0508:
0509:                // add the id
0510:                buf.append(Entity.SEPARATOR);
0511:                buf.append(id);
0512:
0513:                return buf.toString();
0514:            }
0515:
0516:            /**
0517:             * Check security permission.
0518:             * 
0519:             * @param lock
0520:             *        The lock id string.
0521:             * @param resource
0522:             *        The resource reference string, or null if no resource is involved.
0523:             * @return true if allowd, false if not
0524:             */
0525:            protected boolean unlockCheck(String lock, String resource) {
0526:                if (!m_securityService.unlock(eventId(lock), resource)) {
0527:                    return false;
0528:                }
0529:
0530:                return true;
0531:
0532:            } // unlockCheck
0533:
0534:            /**
0535:             * Check security permission, for either of two locks/
0536:             * 
0537:             * @param lock1
0538:             *        The lock id string.
0539:             * @param lock2
0540:             *        The lock id string.
0541:             * @param resource
0542:             *        The resource reference string, or null if no resource is involved.
0543:             * @return true if either allowed, false if not
0544:             */
0545:            protected boolean unlockCheck2(String lock1, String lock2,
0546:                    String resource) {
0547:                // check the first lock
0548:                if (m_securityService.unlock(eventId(lock1), resource))
0549:                    return true;
0550:
0551:                // if the second is different, check that
0552:                if ((lock1 != lock2)
0553:                        && (m_securityService.unlock(eventId(lock2), resource)))
0554:                    return true;
0555:
0556:                return false;
0557:
0558:            } // unlockCheck2
0559:
0560:            /**
0561:             * Check security permission, for any of three locks/
0562:             * 
0563:             * @param lock1
0564:             *        The lock id string.
0565:             * @param lock2
0566:             *        The lock id string.
0567:             * @param resource
0568:             *        The resource reference string, or null if no resource is involved.
0569:             * @return true if either allowed, false if not
0570:             */
0571:            protected boolean unlockCheck3(String lock1, String lock2,
0572:                    String lock3, String resource) {
0573:                // check the first lock
0574:                if (m_securityService.unlock(eventId(lock1), resource))
0575:                    return true;
0576:
0577:                // if the second is different, check that
0578:                if ((lock1 != lock2)
0579:                        && (m_securityService.unlock(eventId(lock2), resource)))
0580:                    return true;
0581:
0582:                // if the third is different, check that
0583:                if ((lock1 != lock3) && (lock2 != lock3)
0584:                        && (m_securityService.unlock(eventId(lock3), resource)))
0585:                    return true;
0586:
0587:                return false;
0588:
0589:            } // unlockCheck3
0590:
0591:            /**
0592:             * Check security permission.
0593:             * 
0594:             * @param lock
0595:             *        The lock id string.
0596:             * @param resource
0597:             *        The resource reference string, or null if no resource is involved.
0598:             * @exception PermissionException
0599:             *            Thrown if the user does not have access
0600:             */
0601:            protected void unlock(String lock, String resource)
0602:                    throws PermissionException {
0603:                if (!unlockCheck(lock, resource)) {
0604:                    throw new PermissionException(m_sessionManager
0605:                            .getCurrentSessionUserId(), eventId(lock), resource);
0606:                }
0607:
0608:            } // unlock
0609:
0610:            /**
0611:             * Check security permission.
0612:             * 
0613:             * @param lock1
0614:             *        The lock id string.
0615:             * @param lock2
0616:             *        The lock id string.
0617:             * @param resource
0618:             *        The resource reference string, or null if no resource is involved.
0619:             * @exception PermissionException
0620:             *            Thrown if the user does not have access to either.
0621:             */
0622:            protected void unlock2(String lock1, String lock2, String resource)
0623:                    throws PermissionException {
0624:                if (!unlockCheck2(lock1, lock2, resource)) {
0625:                    throw new PermissionException(m_sessionManager
0626:                            .getCurrentSessionUserId(), eventId(lock1) + "|"
0627:                            + eventId(lock2), resource);
0628:                }
0629:
0630:            } // unlock2
0631:
0632:            /**
0633:             * Check security permission.
0634:             * 
0635:             * @param lock1
0636:             *        The lock id string.
0637:             * @param lock2
0638:             *        The lock id string.
0639:             * @param lock3
0640:             *        The lock id string.
0641:             * @param resource
0642:             *        The resource reference string, or null if no resource is involved.
0643:             * @exception PermissionException
0644:             *            Thrown if the user does not have access to either.
0645:             */
0646:            protected void unlock3(String lock1, String lock2, String lock3,
0647:                    String resource) throws PermissionException {
0648:                if (!unlockCheck3(lock1, lock2, lock3, resource)) {
0649:                    throw new PermissionException(m_sessionManager
0650:                            .getCurrentSessionUserId(), eventId(lock1) + "|"
0651:                            + eventId(lock2) + "|" + eventId(lock3), resource);
0652:                }
0653:
0654:            } // unlock3
0655:
0656:            /**
0657:             * Return a list of all the defined channels.
0658:             * 
0659:             * @return a list of MessageChannel (or extension) objects (may be empty).
0660:             */
0661:            public List getChannels() {
0662:                List channels = new Vector();
0663:                if ((!m_caching) || (m_channelCache == null)
0664:                        || (m_channelCache.disabled())) {
0665:                    channels = m_storage.getChannels();
0666:                    return channels;
0667:                }
0668:
0669:                // use the cache
0670:                // if the cache is complete, use it
0671:                if (m_channelCache.isComplete()) {
0672:                    // get just the channels in the cache
0673:                    channels = m_channelCache.getAll();
0674:                }
0675:
0676:                // otherwise get all the channels from storage
0677:                else {
0678:                    // Note: while we are getting from storage, storage might change. These can be processed
0679:                    // after we get the storage entries, and put them in the cache, and mark the cache complete.
0680:                    // -ggolden
0681:                    synchronized (m_channelCache) {
0682:                        // if we were waiting and it's now complete...
0683:                        if (m_channelCache.isComplete()) {
0684:                            // get just the channels in the cache
0685:                            channels = m_channelCache.getAll();
0686:                            return channels;
0687:                        }
0688:
0689:                        // save up any events to the cache until we get past this load
0690:                        m_channelCache.holdEvents();
0691:
0692:                        channels = m_storage.getChannels();
0693:                        // update the cache, and mark it complete
0694:                        for (int i = 0; i < channels.size(); i++) {
0695:                            MessageChannel channel = (MessageChannel) channels
0696:                                    .get(i);
0697:                            m_channelCache.put(channel.getReference(), channel);
0698:                        }
0699:
0700:                        m_channelCache.setComplete();
0701:
0702:                        // now we are complete, process any cached events
0703:                        m_channelCache.processEvents();
0704:                    }
0705:                }
0706:
0707:                return channels;
0708:
0709:            } // getChannels
0710:
0711:            /**
0712:             * check permissions for getChannel().
0713:             * 
0714:             * @param ref
0715:             *        The channel reference.
0716:             * @return true if the user is allowed to getChannel(channelId), false if not.
0717:             */
0718:            public boolean allowGetChannel(String ref) {
0719:                return unlockCheck(SECURE_READ, ref);
0720:
0721:            } // allowGetChannel
0722:
0723:            /**
0724:             * Return a specific channel.
0725:             * 
0726:             * @param ref
0727:             *        The channel reference.
0728:             * @return the MessageChannel that has the specified name.
0729:             * @exception IdUnusedException
0730:             *            If this name is not defined for any channel.
0731:             * @exception PermissionException
0732:             *            If the user does not have any permissions to the channel.
0733:             */
0734:            public MessageChannel getChannel(String ref)
0735:                    throws IdUnusedException, PermissionException {
0736:                MessageChannel c = findChannel(ref);
0737:                if (c == null)
0738:                    throw new IdUnusedException(ref);
0739:
0740:                // check security (throws if not permitted)
0741:                unlock(SECURE_READ, ref);
0742:
0743:                return c;
0744:
0745:            } // getChannel
0746:
0747:            /**
0748:             * Find the channel, in cache or info store - cache it if newly found.
0749:             * 
0750:             * @param ref
0751:             *        The channel reference.
0752:             * @return The channel, if found.
0753:             */
0754:            protected MessageChannel findChannel(String ref) {
0755:                if (ref == null)
0756:                    return null;
0757:
0758:                MessageChannel channel = null;
0759:
0760:                if ((!m_caching) || (m_channelCache == null)
0761:                        || (m_channelCache.disabled())) {
0762:                    // if we have done this already in this thread, use that
0763:                    channel = (MessageChannel) m_threadLocalManager.get(ref);
0764:                    if (channel == null) {
0765:                        channel = m_storage.getChannel(ref);
0766:
0767:                        // "cache" the channel in the current service in case they are needed again in this thread...
0768:                        if (channel != null) {
0769:                            m_threadLocalManager.set(ref, channel);
0770:                        }
0771:                    }
0772:
0773:                    return channel;
0774:                }
0775:
0776:                // use the cache
0777:                // if we have it cached, use it (even if it's cached as a null, a miss)
0778:                if (m_channelCache.containsKey(ref)) {
0779:                    channel = (MessageChannel) m_channelCache.get(ref);
0780:                }
0781:
0782:                // if not in the cache, see if we have it in our info store
0783:                else {
0784:                    channel = m_storage.getChannel(ref);
0785:
0786:                    // if so, cache it, even misses
0787:                    m_channelCache.put(ref, channel);
0788:                }
0789:
0790:                return channel;
0791:
0792:            } // findChannel
0793:
0794:            /**
0795:             * check permissions for addChannel().
0796:             * 
0797:             * @param ref
0798:             *        The channel reference.
0799:             * @return true if the user is allowed to addChannel(channelId), false if not.
0800:             */
0801:            public boolean allowAddChannel(String ref) {
0802:                // check security (throws if not permitted)
0803:                return unlockCheck(SECURE_ADD, ref);
0804:
0805:            } // allowAddChannel
0806:
0807:            /**
0808:             * Add a new channel. Must commitEdit() to make official, or cancelEdit() when done!
0809:             * 
0810:             * @param ref
0811:             *        The channel reference.
0812:             * @return The newly created channel, locked for update.
0813:             * @exception IdUsedException
0814:             *            if the id is not unique.
0815:             * @exception IdInvalidException
0816:             *            if the id is not made up of valid characters.
0817:             * @exception PermissionException
0818:             *            if the user does not have permission to add a channel.
0819:             */
0820:            public MessageChannelEdit addChannel(String ref)
0821:                    throws IdUsedException, IdInvalidException,
0822:                    PermissionException {
0823:                // check the name's validity
0824:                if (!m_entityManager.checkReference(ref))
0825:                    throw new IdInvalidException(ref);
0826:
0827:                // check for existance
0828:                if (m_storage.checkChannel(ref)) {
0829:                    throw new IdUsedException(ref);
0830:                }
0831:
0832:                // check security
0833:                unlock(SECURE_ADD, ref);
0834:
0835:                // keep it
0836:                MessageChannelEdit channel = m_storage.putChannel(ref);
0837:
0838:                ((BaseMessageChannelEdit) channel).setEvent(SECURE_ADD);
0839:
0840:                return channel;
0841:
0842:            } // addChannel
0843:
0844:            /**
0845:             * check permissions for editChannel()
0846:             * 
0847:             * @param ref
0848:             *        The channel reference.
0849:             * @return true if the user is allowed to update the channel, false if not.
0850:             */
0851:            public boolean allowEditChannel(String ref) {
0852:                // check security (throws if not permitted)
0853:                return unlockCheck3(SECURE_ADD, SECURE_UPDATE_ANY,
0854:                        SECURE_UPDATE_OWN, ref);
0855:
0856:            } // allowEditChannel
0857:
0858:            /**
0859:             * Return a specific channel, as specified by channel id, locked for update. Must commitEdit() to make official, or cancelEdit() when done!
0860:             * 
0861:             * @param ref
0862:             *        The channel reference.
0863:             * @return the Channel that has the specified id.
0864:             * @exception IdUnusedException
0865:             *            If this name is not a defined channel.
0866:             * @exception PermissionException
0867:             *            If the user does not have any permissions to edit the channel.
0868:             * @exception InUseException
0869:             *            if the channel is locked for edit by someone else.
0870:             */
0871:            public MessageChannelEdit editChannel(String ref)
0872:                    throws IdUnusedException, PermissionException,
0873:                    InUseException {
0874:                // check for existance
0875:                if (!m_storage.checkChannel(ref)) {
0876:                    throw new IdUnusedException(ref);
0877:                }
0878:
0879:                // check security (throws if not permitted)
0880:                unlock3(SECURE_ADD, SECURE_UPDATE_ANY, SECURE_UPDATE_OWN, ref);
0881:
0882:                // ignore the cache - get the channel with a lock from the info store
0883:                MessageChannelEdit edit = m_storage.editChannel(ref);
0884:                if (edit == null)
0885:                    throw new InUseException(ref);
0886:
0887:                ((BaseMessageChannelEdit) edit).setEvent(SECURE_UPDATE_ANY);
0888:
0889:                return edit;
0890:
0891:            } // editChannel
0892:
0893:            /**
0894:             * Commit the changes made to a MessageChannelEdit object, and release the lock. The MessageChannelEdit is disabled, and not to be used after this call.
0895:             * 
0896:             * @param user
0897:             *        The MessageChannelEdit object to commit.
0898:             */
0899:            public void commitChannel(MessageChannelEdit edit) {
0900:                if (edit == null)
0901:                    return;
0902:
0903:                // check for closed edit
0904:                if (!edit.isActiveEdit()) {
0905:                    try {
0906:                        throw new Exception();
0907:                    } catch (Exception e) {
0908:                        M_log.warn("commitEdit(): closed ChannelEdit", e);
0909:                    }
0910:                    return;
0911:                }
0912:
0913:                m_storage.commitChannel(edit);
0914:
0915:                // track event (no notification)
0916:                Event event = m_eventTrackingService.newEvent(
0917:                        eventId(((BaseMessageChannelEdit) edit).getEvent()),
0918:                        edit.getReference(), true,
0919:                        NotificationService.NOTI_NONE);
0920:
0921:                m_eventTrackingService.post(event);
0922:
0923:                // close the edit object
0924:                ((BaseMessageChannelEdit) edit).closeEdit();
0925:
0926:            } // commitChannel
0927:
0928:            /**
0929:             * Cancel the changes made to a MessageChannelEdit object, and release the lock. The MessageChannelEdit is disabled, and not to be used after this call.
0930:             * 
0931:             * @param user
0932:             *        The MessageChannelEdit object to commit.
0933:             */
0934:            public void cancelChannel(MessageChannelEdit edit) {
0935:                if (edit == null)
0936:                    return;
0937:
0938:                // check for closed edit
0939:                if (!edit.isActiveEdit()) {
0940:                    try {
0941:                        throw new Exception();
0942:                    } catch (Exception e) {
0943:                        M_log
0944:                                .warn(
0945:                                        "cancelChannelEdit(): closed MessageChannelEdit",
0946:                                        e);
0947:                    }
0948:                    return;
0949:                }
0950:
0951:                // release the edit lock
0952:                m_storage.cancelChannel(edit);
0953:
0954:                // close the edit object
0955:                ((BaseMessageChannelEdit) edit).closeEdit();
0956:
0957:            } // cancelChannel
0958:
0959:            /**
0960:             * Check permissions for removeChannel().
0961:             * 
0962:             * @param ref
0963:             *        The channel reference.
0964:             * @return true if the user is allowed to removeChannel(), false if not.
0965:             */
0966:            public boolean allowRemoveChannel(String ref) {
0967:                // check security (throws if not permitted)
0968:                return unlockCheck(SECURE_REMOVE_ANY, ref);
0969:
0970:            } // allowRemoveChannel
0971:
0972:            /**
0973:             * Remove a channel. Remove a channel - it must be locked from editChannel().
0974:             * 
0975:             * @param channel
0976:             *        The channel to remove.
0977:             * @exception PermissionException
0978:             *            if the user does not have permission to remove a channel.
0979:             */
0980:            public void removeChannel(MessageChannelEdit channel)
0981:                    throws PermissionException {
0982:                if (channel == null)
0983:                    return;
0984:
0985:                // check for closed edit
0986:                if (!channel.isActiveEdit()) {
0987:                    try {
0988:                        throw new Exception();
0989:                    } catch (Exception e) {
0990:                        M_log.warn("removeChannel(): closed ChannelEdit", e);
0991:                    }
0992:                    return;
0993:                }
0994:
0995:                // check security
0996:                unlock(SECURE_REMOVE_ANY, channel.getReference());
0997:
0998:                m_storage.removeChannel(channel);
0999:
1000:                // track event
1001:                Event event = m_eventTrackingService.newEvent(
1002:                        eventId(SECURE_REMOVE_ANY), channel.getReference(),
1003:                        true);
1004:                m_eventTrackingService.post(event);
1005:
1006:                // mark the channel as removed
1007:                ((BaseMessageChannelEdit) channel).setRemoved(event);
1008:
1009:                // close the edit object
1010:                ((BaseMessageChannelEdit) channel).closeEdit();
1011:
1012:                // remove any realm defined for this resource
1013:                try {
1014:                    m_authzGroupService.removeAuthzGroup(m_authzGroupService
1015:                            .getAuthzGroup(channel.getReference()));
1016:                } catch (AuthzPermissionException e) {
1017:                    M_log.warn("removeChannel: removing realm for : "
1018:                            + channel.getReference() + " : " + e);
1019:                } catch (GroupNotDefinedException ignore) {
1020:                }
1021:
1022:            } // removeChannel
1023:
1024:            /**
1025:             * Get a message, given a reference. This call avoids the need to have channel security, as long as the user has permissions to the message.
1026:             * 
1027:             * @param ref
1028:             *        The message reference
1029:             * @return The message.
1030:             * @exception IdUnusedException
1031:             *            If this reference does not identify a message.
1032:             * @exception PermissionException
1033:             *            If the user does not have any permissions to the message.
1034:             */
1035:            public Message getMessage(Reference ref) throws IdUnusedException,
1036:                    PermissionException {
1037:                // could also check type, but need to know which message service we are working for! -ggolden
1038:                if (!ref.getSubType().equals(REF_TYPE_MESSAGE)) {
1039:                    throw new IdUnusedException(ref.getReference());
1040:                }
1041:
1042:                // check security on the message
1043:                if (!allowGetMessage(channelReference(ref.getContext(), ref
1044:                        .getContainer()), ref.getReference())) {
1045:                    throw new PermissionException(m_sessionManager
1046:                            .getCurrentSessionUserId(), eventId(SECURE_READ),
1047:                            ref.getReference());
1048:                }
1049:
1050:                // get the channel, no security check
1051:                MessageChannel c = findChannel(channelReference(ref
1052:                        .getContext(), ref.getContainer()));
1053:                if (c == null) {
1054:                    throw new IdUnusedException(ref.getContainer());
1055:                }
1056:
1057:                // get the message from the channel
1058:                Message m = ((BaseMessageChannelEdit) c).findMessage(ref
1059:                        .getId());
1060:
1061:                return m;
1062:
1063:            } // getMessage
1064:
1065:            /**
1066:             * Check the message read permission for the message
1067:             * 
1068:             * @param ref
1069:             *        The Reference (assumed to be to a message).
1070:             * @return True if the end user has permission to read the message, or permission to all messages in the channel, false if not.
1071:             */
1072:            protected boolean allowGetMessage(String channelRef, String msgRef) {
1073:                // Assume this reference is for a message
1074:
1075:                // check the message
1076:                return unlockCheck(SECURE_READ, msgRef);
1077:            }
1078:
1079:            /**
1080:             * Cancel the changes made to a MessageEdit object, and release the lock. The MessageChannelEdit is disabled, and not to be used after this call.
1081:             * 
1082:             * @param user
1083:             *        The MessageEdit object to cancel.
1084:             */
1085:            public void cancelMessage(MessageEdit edit) {
1086:                if ((edit == null)
1087:                        || (((BaseMessageEdit) edit).m_channel == null))
1088:                    return;
1089:
1090:                ((BaseMessageEdit) edit).m_channel.cancelMessage(edit);
1091:            }
1092:
1093:            /**
1094:             * {@inheritDoc}
1095:             */
1096:            public List getMessages(String channelRef, Time afterDate,
1097:                    int limitedToLatest, boolean ascending,
1098:                    boolean includeDrafts, boolean pubViewOnly)
1099:                    throws PermissionException {
1100:                // channel read security
1101:                if (!pubViewOnly) {
1102:                    unlock(SECURE_READ, channelRef);
1103:                }
1104:
1105:                // get the channel, no security check
1106:                MessageChannel c = findChannel(channelRef);
1107:                if (c == null)
1108:                    return new Vector();
1109:
1110:                // null - no drafts, "*", all drafts, <userId> drafts created by user id only
1111:                String draftsForId = null;
1112:                if (includeDrafts) {
1113:                    if (unlockCheck(SECURE_READ_DRAFT, channelRef)) {
1114:                        draftsForId = "*";
1115:                    } else {
1116:                        draftsForId = m_sessionManager
1117:                                .getCurrentSessionUserId();
1118:                    }
1119:                }
1120:
1121:                if ((!m_caching) || (m_channelCache == null)
1122:                        || (m_channelCache.disabled())) {
1123:                    // get messages filtered by date and count and drafts, in descending (latest first) order
1124:                    List rv = m_storage.getMessages(c, afterDate,
1125:                            limitedToLatest, draftsForId, pubViewOnly);
1126:
1127:                    // if ascending, reverse
1128:                    if (ascending) {
1129:                        Collections.reverse(rv);
1130:                    }
1131:
1132:                    return rv;
1133:                }
1134:
1135:                // use the cache
1136:
1137:                // get the messages
1138:                List msgs = ((BaseMessageChannelEdit) c).findFilterMessages(
1139:                        new MessageSelectionFilter(afterDate, draftsForId,
1140:                                pubViewOnly), ascending);
1141:
1142:                // sub-select count
1143:                if ((limitedToLatest != 0) && (limitedToLatest < msgs.size())) {
1144:                    if (ascending) {
1145:                        msgs = msgs.subList(msgs.size() - limitedToLatest, msgs
1146:                                .size());
1147:                    } else {
1148:                        msgs = msgs.subList(0, limitedToLatest);
1149:                    }
1150:                }
1151:
1152:                return msgs;
1153:            }
1154:
1155:            /**
1156:             * Access a list of channel ids that are defined related to the context.
1157:             * 
1158:             * @param context
1159:             *        The context in which to search
1160:             * @return A List (String) of channel id for channels withing the context.
1161:             */
1162:            public List getChannelIds(String context) {
1163:                if ((!m_caching) || (m_channelCache == null)
1164:                        || (m_channelCache.disabled())) {
1165:                    return m_storage.getChannelIdsMatching(channelReference(
1166:                            context, ""));
1167:                }
1168:
1169:                // use the cache
1170:                List channels = getChannels();
1171:                List rv = new Vector();
1172:                for (Iterator i = channels.iterator(); i.hasNext();) {
1173:                    MessageChannel channel = (MessageChannel) i.next();
1174:                    if (context.equals(channel.getContext())) {
1175:                        rv.add(channel.getId());
1176:                    }
1177:                }
1178:
1179:                return rv;
1180:            }
1181:
1182:            /**********************************************************************************************************************************************************************************************************************************************************
1183:             * ResourceService implementation
1184:             *********************************************************************************************************************************************************************************************************************************************************/
1185:
1186:            /**
1187:             * {@inheritDoc}
1188:             */
1189:            public boolean willArchiveMerge() {
1190:                return true;
1191:            }
1192:
1193:            /**
1194:             * {@inheritDoc}
1195:             */
1196:            public HttpAccess getHttpAccess() {
1197:                return new HttpAccess() {
1198:
1199:                    public void handleAccess(HttpServletRequest req,
1200:                            HttpServletResponse res, Reference ref,
1201:                            Collection copyrightAcceptedRefs)
1202:                            throws EntityPermissionException,
1203:                            EntityNotDefinedException {
1204:                        try {
1205:                            // We need to write to a temporary stream for better speed, plus
1206:                            // so we can get a byte count. Internet Explorer has problems
1207:                            // if we don't make the setContentLength() call.
1208:                            ByteArrayOutputStream outByteStream = new ByteArrayOutputStream();
1209:                            OutputStreamWriter sw = new OutputStreamWriter(
1210:                                    outByteStream);
1211:
1212:                            String skin = m_serverConfigurationService
1213:                                    .getString("skin.default");
1214:                            String skinRepo = m_serverConfigurationService
1215:                                    .getString("skin.repo");
1216:
1217:                            Message message = getMessage(ref);
1218:                            String title = ref.getDescription();
1219:                            MessageHeader messageHead = message.getHeader();
1220:                            String date = messageHead.getDate()
1221:                                    .toStringLocalFullZ();
1222:                            String from = messageHead.getFrom()
1223:                                    .getDisplayName();
1224:                            String groups = "";
1225:                            Collection gr = messageHead.getGroups();
1226:                            for (Iterator i = gr.iterator(); i.hasNext();) {
1227:                                groups += "<li>" + i.next() + "</li>";
1228:                            }
1229:                            String body = message.getBody();
1230:
1231:                            sw
1232:                                    .write("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
1233:                                            + "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" xml:lang=\"en\">\n"
1234:                                            + "<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"
1235:                                            + "<link href=\"");
1236:                            sw.write(skinRepo);
1237:                            sw
1238:                                    .write("/tool_base.css\" type=\"text/css\" rel=\"stylesheet\" media=\"all\" />\n"
1239:                                            + "<link href=\"");
1240:                            sw.write(skinRepo);
1241:                            sw.write("/");
1242:                            sw.write(skin);
1243:                            sw
1244:                                    .write("/tool.css\" type=\"text/css\" rel=\"stylesheet\" media=\"all\" />\n"
1245:                                            + "<meta http-equiv=\"Content-Style-Type\" content=\"text/css\" />\n"
1246:                                            + "<title>");
1247:                            sw.write(title);
1248:                            sw
1249:                                    .write("</title></head><body><div class=\"portletBody\">\n"
1250:                                            + "<h2>");
1251:                            sw.write(title);
1252:                            sw.write("</h2><ul><li>Date ");
1253:                            sw.write(date);
1254:                            sw.write("</li>");
1255:                            sw.write("<li>From ");
1256:                            sw.write(from);
1257:                            sw.write("</li>");
1258:                            sw.write(groups);
1259:                            sw.write("<ul><p>");
1260:                            sw.write(body);
1261:                            sw.write("</p></div></body></html> ");
1262:
1263:                            sw.flush();
1264:                            res.setContentType("text/html");
1265:                            res.setContentLength(outByteStream.size());
1266:
1267:                            if (outByteStream.size() > 0) {
1268:                                // Increase the buffer size for more speed.
1269:                                res.setBufferSize(outByteStream.size());
1270:                            }
1271:
1272:                            OutputStream out = null;
1273:                            try {
1274:                                out = res.getOutputStream();
1275:                                if (outByteStream.size() > 0) {
1276:                                    outByteStream.writeTo(out);
1277:                                }
1278:                                out.flush();
1279:                                out.close();
1280:                            } catch (Throwable ignore) {
1281:                            } finally {
1282:                                if (out != null) {
1283:                                    try {
1284:                                        out.close();
1285:                                    } catch (Throwable ignore) {
1286:                                    }
1287:                                }
1288:                            }
1289:                        } catch (PermissionException e) {
1290:                            throw new EntityPermissionException(e.getUser(), e
1291:                                    .getLocalizedMessage(), e.getResource());
1292:                        } catch (IdUnusedException e) {
1293:                            throw new EntityNotDefinedException(e.getId());
1294:                        } catch (Throwable t) {
1295:                            throw new RuntimeException(
1296:                                    "Faied to find message ", t);
1297:                        }
1298:                    }
1299:                };
1300:            }
1301:
1302:            /**
1303:             * {@inheritDoc}
1304:             */
1305:            public String getEntityDescription(Reference ref) {
1306:                // we could check that the type is one of the message services, but lets just assume it is so we don't need to know them here -ggolden
1307:
1308:                String rv = "Message: " + ref.getReference();
1309:
1310:                try {
1311:                    // if this is a channel
1312:                    if (REF_TYPE_CHANNEL.equals(ref.getSubType())) {
1313:                        MessageChannel channel = getChannel(ref.getReference());
1314:                        rv = "Channel: " + channel.getId() + " ("
1315:                                + channel.getContext() + ")";
1316:                    }
1317:                } catch (PermissionException e) {
1318:                } catch (IdUnusedException e) {
1319:                } catch (NullPointerException e) {
1320:                }
1321:
1322:                return rv;
1323:            }
1324:
1325:            /**
1326:             * {@inheritDoc}
1327:             */
1328:            public ResourceProperties getEntityResourceProperties(Reference ref) {
1329:                // we could check that the type is one of the message services, but lets just assume it is so we don't need to know them here -ggolden
1330:
1331:                ResourceProperties rv = null;
1332:
1333:                try {
1334:                    // if this is a channel
1335:                    if (REF_TYPE_CHANNEL.equals(ref.getSubType())) {
1336:                        MessageChannel channel = getChannel(ref.getReference());
1337:                        rv = channel.getProperties();
1338:                    }
1339:
1340:                    // otherwise a message
1341:                    else if (REF_TYPE_MESSAGE.equals(ref.getSubType())) {
1342:                        Message message = getMessage(ref);
1343:                        rv = message.getProperties();
1344:                    }
1345:
1346:                    else
1347:                        M_log
1348:                                .warn("getProperties(): unknown message ref subtype: "
1349:                                        + ref.getSubType()
1350:                                        + " in ref: "
1351:                                        + ref.getReference());
1352:                } catch (PermissionException e) {
1353:                    M_log.warn("getProperties(): " + e);
1354:                } catch (IdUnusedException e) {
1355:                    // This just means that the resource once pointed to as an attachment or something has been deleted.
1356:                    // M_log.warn("getProperties(): " + e);
1357:                } catch (NullPointerException e) {
1358:                    M_log.warn("getProperties(): " + e);
1359:                }
1360:
1361:                return rv;
1362:            }
1363:
1364:            /**
1365:             * {@inheritDoc}
1366:             */
1367:            public Entity getEntity(Reference ref) {
1368:                // we could check that the type is one of the message services, but lets just assume it is so we don't need to know them here -ggolden
1369:
1370:                Entity rv = null;
1371:
1372:                try {
1373:                    // if this is a channel
1374:                    if (REF_TYPE_CHANNEL.equals(ref.getSubType())) {
1375:                        rv = getChannel(ref.getReference());
1376:                    }
1377:
1378:                    // otherwise a message
1379:                    else if (REF_TYPE_MESSAGE.equals(ref.getSubType())) {
1380:                        rv = getMessage(ref);
1381:                    }
1382:
1383:                    // else try {throw new Exception();} catch (Exception e) {M_log.warn("getResource(): unknown message ref subtype: " + m_subType + " in ref: " + m_reference, e);}
1384:                    else
1385:                        M_log
1386:                                .warn("getResource(): unknown message ref subtype: "
1387:                                        + ref.getSubType()
1388:                                        + " in ref: "
1389:                                        + ref.getReference());
1390:                } catch (PermissionException e) {
1391:                    M_log.warn("getResource(): " + e);
1392:                } catch (IdUnusedException e) {
1393:                    M_log.warn("getResource(): " + e);
1394:                } catch (NullPointerException e) {
1395:                    M_log.warn("getResource(): " + e);
1396:                }
1397:
1398:                return rv;
1399:            }
1400:
1401:            /**
1402:             * {@inheritDoc}
1403:             */
1404:            public Collection getEntityAuthzGroups(Reference ref, String userId) {
1405:                // we could check that the type is one of the message services, but lets just assume it is so we don't need to know them here -ggolden
1406:
1407:                Collection rv = new Vector();
1408:
1409:                // for MessageService messages:
1410:                // if access set to CHANNEL (or PUBLIC), use the message, channel and site authzGroups.
1411:                // if access set to GROUPED, use the message, and the groups, but not the channel or site authzGroups.
1412:                // if the user has SECURE_ALL_GROUPS in the context, ignore GROUPED access and treat as if CHANNEL
1413:
1414:                // for Channels, use the channel and site authzGroups.
1415:                try {
1416:                    // for message
1417:                    if (REF_TYPE_MESSAGE.equals(ref.getSubType())) {
1418:                        // message
1419:                        rv.add(ref.getReference());
1420:
1421:                        boolean grouped = false;
1422:                        Collection groups = null;
1423:
1424:                        // check SECURE_ALL_GROUPS - if not, check if the message has groups or not
1425:                        // TODO: the last param needs to be a ContextService.getRef(ref.getContext())... or a ref.getContextAuthzGroup() -ggolden
1426:                        if ((userId == null)
1427:                                || ((!m_securityService.isSuperUser(userId)) && (!m_authzGroupService
1428:                                        .isAllowed(userId,
1429:                                                eventId(SECURE_ALL_GROUPS),
1430:                                                m_siteService.siteReference(ref
1431:                                                        .getContext()))))) {
1432:                            // get the channel to get the message to get group information
1433:                            // TODO: check for efficiency, cache and thread local caching usage -ggolden
1434:                            String channelRef = channelReference(ref
1435:                                    .getContext(), ref.getContainer());
1436:                            MessageChannel c = findChannel(channelRef);
1437:                            if (c != null) {
1438:                                Message m = ((BaseMessageChannelEdit) c)
1439:                                        .findMessage(ref.getId());
1440:                                if (m != null) {
1441:                                    grouped = MessageHeader.MessageAccess.GROUPED == m
1442:                                            .getHeader().getAccess();
1443:                                    groups = m.getHeader().getGroups();
1444:                                }
1445:                            }
1446:                        }
1447:
1448:                        if (grouped) {
1449:                            // groups
1450:                            rv.addAll(groups);
1451:                        }
1452:
1453:                        // not grouped
1454:                        else {
1455:                            // channel
1456:                            rv.add(channelReference(ref.getContext(), ref
1457:                                    .getContainer()));
1458:
1459:                            // site
1460:                            ref.addSiteContextAuthzGroup(rv);
1461:                        }
1462:                    }
1463:
1464:                    // for channel
1465:                    else {
1466:                        // channel
1467:                        rv.add(channelReference(ref.getContext(), ref.getId()));
1468:
1469:                        // site
1470:                        ref.addSiteContextAuthzGroup(rv);
1471:                    }
1472:                } catch (Throwable e) {
1473:                    M_log.warn("getEntityAuthzGroups(): " + e);
1474:                }
1475:
1476:                return rv;
1477:            }
1478:
1479:            /**
1480:             * {@inheritDoc}
1481:             */
1482:            public String getEntityUrl(Reference ref) {
1483:                // we could check that the type is one of the message services, but lets just assume it is so we don't need to know them here -ggolden
1484:
1485:                String url = null;
1486:
1487:                try {
1488:                    // if this is a channel
1489:                    if (REF_TYPE_CHANNEL.equals(ref.getSubType())) {
1490:                        MessageChannel channel = getChannel(ref.getReference());
1491:                        url = channel.getUrl();
1492:                    }
1493:
1494:                    // otherwise a message
1495:                    else if (REF_TYPE_MESSAGE.equals(ref.getSubType())) {
1496:                        Message message = getMessage(ref);
1497:                        url = message.getUrl();
1498:                    }
1499:
1500:                    else
1501:                        M_log.warn("getUrl(): unknown message ref subtype: "
1502:                                + ref.getSubType() + " in ref: "
1503:                                + ref.getReference());
1504:                } catch (PermissionException e) {
1505:                    M_log.warn("getUrl(): " + e);
1506:                } catch (IdUnusedException e) {
1507:                    M_log.warn("getUrl(): " + e);
1508:                } catch (NullPointerException e) {
1509:                    M_log.warn("getUrl(): " + e);
1510:                }
1511:
1512:                return url;
1513:            }
1514:
1515:            /**
1516:             * {@inheritDoc}
1517:             */
1518:            public String archive(String siteId, Document doc, Stack stack,
1519:                    String archivePath, List attachments) {
1520:                // prepare the buffer for the results log
1521:                StringBuffer results = new StringBuffer();
1522:
1523:                // start with an element with our very own (service) name
1524:                Element element = doc.createElement(serviceName());
1525:                ((Element) stack.peek()).appendChild(element);
1526:                stack.push(element);
1527:
1528:                // get the channel associated with this site
1529:                String channelRef = channelReference(siteId,
1530:                        SiteService.MAIN_CONTAINER);
1531:
1532:                results.append("archiving " + getLabel() + " channel "
1533:                        + channelRef + ".\n");
1534:
1535:                try {
1536:                    // do the channel
1537:                    MessageChannel channel = getChannel(channelRef);
1538:                    Element containerElement = channel.toXml(doc, stack);
1539:                    stack.push(containerElement);
1540:
1541:                    // do the messages in the channel
1542:                    Iterator messages = channel.getMessages(null, true)
1543:                            .iterator();
1544:                    while (messages.hasNext()) {
1545:                        Message msg = (Message) messages.next();
1546:                        msg.toXml(doc, stack);
1547:
1548:                        // collect message attachments
1549:                        MessageHeader header = msg.getHeader();
1550:                        List atts = header.getAttachments();
1551:                        for (int i = 0; i < atts.size(); i++) {
1552:                            Reference ref = (Reference) atts.get(i);
1553:                            // if it's in the attachment area, and not already in the list
1554:                            if ((ref.getReference()
1555:                                    .startsWith("/content/attachment/"))
1556:                                    && (!attachments.contains(ref))) {
1557:                                attachments.add(ref);
1558:                            }
1559:                        }
1560:                    }
1561:
1562:                    // archive the synoptic tool options
1563:                    archiveSynopticOptions(siteId, doc, element);
1564:
1565:                    stack.pop();
1566:                } catch (Exception any) {
1567:                    M_log
1568:                            .warn("archve: exception archiving messages for service: "
1569:                                    + serviceName() + " channel: " + channelRef);
1570:                }
1571:
1572:                stack.pop();
1573:
1574:                return results.toString();
1575:            }
1576:
1577:            /**
1578:             * try to add synoptic options for this tool to the archive, if they exist
1579:             * @param siteId
1580:             * @param doc
1581:             * @param element
1582:             */
1583:            public void archiveSynopticOptions(String siteId, Document doc,
1584:                    Element element) {
1585:                try {
1586:                    // archive the synoptic tool options
1587:                    Site site = m_siteService.getSite(siteId);
1588:                    ToolConfiguration synTool = site
1589:                            .getToolForCommonId("sakai.synoptic." + getLabel());
1590:                    Properties synProp = synTool.getPlacementConfig();
1591:                    if (synProp != null && synProp.size() > 0) {
1592:                        Element synElement = doc.createElement(SYNOPTIC_TOOL);
1593:                        Element synProps = doc.createElement(PROPERTIES);
1594:
1595:                        Set synPropSet = synProp.keySet();
1596:                        Iterator propIter = synPropSet.iterator();
1597:                        while (propIter.hasNext()) {
1598:                            String propName = (String) propIter.next();
1599:                            Element synPropEl = doc.createElement(PROPERTY);
1600:                            synPropEl.setAttribute(NAME, propName);
1601:                            synPropEl.setAttribute(VALUE, synProp
1602:                                    .getProperty(propName));
1603:                            synProps.appendChild(synPropEl);
1604:                        }
1605:
1606:                        synElement.appendChild(synProps);
1607:                        element.appendChild(synElement);
1608:                    }
1609:                } catch (Exception e) {
1610:                    M_log
1611:                            .warn("archive: exception archiving synoptic options for service: "
1612:                                    + serviceName());
1613:                }
1614:            }
1615:
1616:            /**
1617:             * {@inheritDoc}
1618:             */
1619:            public String merge(String siteId, Element root,
1620:                    String archivePath, String fromSiteId, Map attachmentNames,
1621:                    Map userIdTrans, Set userListAllowImport) {
1622:                // get the system name: FROM_WT, FROM_CT, FROM_SAKAI
1623:                String source = "";
1624:                // root: <service> node
1625:                Node parent = root.getParentNode(); // parent: <archive> node containing "system"
1626:                if (parent.getNodeType() == Node.ELEMENT_NODE) {
1627:                    Element parentEl = (Element) parent;
1628:                    source = parentEl.getAttribute("system");
1629:                }
1630:
1631:                HashSet userSet = (HashSet) userListAllowImport;
1632:
1633:                Map ids = new HashMap();
1634:
1635:                // prepare the buffer for the results log
1636:                StringBuffer results = new StringBuffer();
1637:
1638:                // get the channel associated with this site
1639:                String channelRef = channelReference(siteId,
1640:                        SiteService.MAIN_CONTAINER);
1641:
1642:                int count = 0;
1643:
1644:                try {
1645:                    MessageChannel channel = null;
1646:                    try {
1647:                        channel = getChannel(channelRef);
1648:                    } catch (IdUnusedException e) {
1649:                        MessageChannelEdit edit = addChannel(channelRef);
1650:                        commitChannel(edit);
1651:                        channel = edit;
1652:                    }
1653:
1654:                    // pass the DOM to get new message ids, record the mapping from old to new, and adjust attachments
1655:                    NodeList children2 = root.getChildNodes();
1656:                    int length2 = children2.getLength();
1657:                    for (int i2 = 0; i2 < length2; i2++) {
1658:                        Node child2 = children2.item(i2);
1659:                        if (child2.getNodeType() == Node.ELEMENT_NODE) {
1660:                            Element element2 = (Element) child2;
1661:
1662:                            // get the "channel" child
1663:                            if (element2.getTagName().equals("channel")) {
1664:                                NodeList children3 = element2.getChildNodes();
1665:                                final int length3 = children3.getLength();
1666:                                for (int i3 = 0; i3 < length3; i3++) {
1667:                                    Node child3 = children3.item(i3);
1668:                                    if (child3.getNodeType() == Node.ELEMENT_NODE) {
1669:                                        Element element3 = (Element) child3;
1670:
1671:                                        parseMergeChannelExtra(element3,
1672:                                                channelRef);
1673:
1674:                                        // for "message" children
1675:                                        if (element3.getTagName().equals(
1676:                                                "message")) {
1677:                                            // get the header child
1678:                                            NodeList children4 = element3
1679:                                                    .getChildNodes();
1680:                                            final int length4 = children4
1681:                                                    .getLength();
1682:                                            for (int i4 = 0; i4 < length4; i4++) {
1683:                                                Node child4 = children4
1684:                                                        .item(i4);
1685:                                                if (child4.getNodeType() == Node.ELEMENT_NODE) {
1686:                                                    Element element4 = (Element) child4;
1687:
1688:                                                    // for "header" children
1689:                                                    if (element4.getTagName()
1690:                                                            .equals("header")) {
1691:                                                        String oldUserId = element4
1692:                                                                .getAttribute("from");
1693:
1694:                                                        // userIdTrans is not empty only when from WT
1695:                                                        if (!userIdTrans
1696:                                                                .isEmpty()) {
1697:                                                            if (userIdTrans
1698:                                                                    .containsKey(oldUserId)) {
1699:                                                                element4
1700:                                                                        .setAttribute(
1701:                                                                                "from",
1702:                                                                                (String) userIdTrans
1703:                                                                                        .get(oldUserId));
1704:                                                            }
1705:                                                        }
1706:
1707:                                                        // adjust the id
1708:                                                        String oldId = element4
1709:                                                                .getAttribute("id");
1710:                                                        String newId = m_idManager
1711:                                                                .createUuid();
1712:                                                        ids.put(oldId, newId);
1713:                                                        element4.setAttribute(
1714:                                                                "id", newId);
1715:
1716:                                                        // get the attachment kids
1717:                                                        NodeList children5 = element4
1718:                                                                .getChildNodes();
1719:                                                        final int length5 = children5
1720:                                                                .getLength();
1721:                                                        for (int i5 = 0; i5 < length5; i5++) {
1722:                                                            Node child5 = children5
1723:                                                                    .item(i5);
1724:                                                            if (child5
1725:                                                                    .getNodeType() == Node.ELEMENT_NODE) {
1726:                                                                Element element5 = (Element) child5;
1727:
1728:                                                                // for "attachment" children
1729:                                                                if (element5
1730:                                                                        .getTagName()
1731:                                                                        .equals(
1732:                                                                                "attachment")) {
1733:                                                                    // map the attachment area folder name
1734:                                                                    String oldUrl = element5
1735:                                                                            .getAttribute("relative-url");
1736:                                                                    if (oldUrl
1737:                                                                            .startsWith("/content/attachment/")) {
1738:                                                                        String newUrl = (String) attachmentNames
1739:                                                                                .get(oldUrl);
1740:                                                                        if (newUrl != null) {
1741:                                                                            if (newUrl
1742:                                                                                    .startsWith("/attachment/"))
1743:                                                                                newUrl = "/content"
1744:                                                                                        .concat(newUrl);
1745:
1746:                                                                            element5
1747:                                                                                    .setAttribute(
1748:                                                                                            "relative-url",
1749:                                                                                            Validator
1750:                                                                                                    .escapeQuestionMark(newUrl));
1751:                                                                        }
1752:                                                                    }
1753:
1754:                                                                    // map any references to this site to the new site id
1755:                                                                    else if (oldUrl
1756:                                                                            .startsWith("/content/group/"
1757:                                                                                    + fromSiteId
1758:                                                                                    + "/")) {
1759:                                                                        String newUrl = "/content/group/"
1760:                                                                                + siteId
1761:                                                                                + oldUrl
1762:                                                                                        .substring(15 + fromSiteId
1763:                                                                                                .length());
1764:                                                                        element5
1765:                                                                                .setAttribute(
1766:                                                                                        "relative-url",
1767:                                                                                        Validator
1768:                                                                                                .escapeQuestionMark(newUrl));
1769:                                                                    }
1770:                                                                }
1771:                                                            }
1772:                                                        }
1773:                                                    }
1774:                                                }
1775:                                            }
1776:                                        }
1777:                                    }
1778:                                }
1779:                            }
1780:                            // merge synoptic tool
1781:                            else if (element2.getTagName()
1782:                                    .equals(SYNOPTIC_TOOL)) {
1783:                                Site site = m_siteService.getSite(siteId);
1784:                                ToolConfiguration synTool = site
1785:                                        .getToolForCommonId("sakai.synoptic."
1786:                                                + getLabel());
1787:                                Properties synProps = synTool
1788:                                        .getPlacementConfig();
1789:
1790:                                NodeList synPropNodes = element2
1791:                                        .getChildNodes();
1792:                                for (int props = 0; props < synPropNodes
1793:                                        .getLength(); props++) {
1794:                                    Node propsNode = synPropNodes.item(props);
1795:                                    if (propsNode.getNodeType() == Node.ELEMENT_NODE) {
1796:                                        Element synPropEl = (Element) propsNode;
1797:                                        if (synPropEl.getTagName().equals(
1798:                                                PROPERTIES)) {
1799:                                            NodeList synProperties = synPropEl
1800:                                                    .getChildNodes();
1801:                                            for (int p = 0; p < synProperties
1802:                                                    .getLength(); p++) {
1803:                                                Node propertyNode = synProperties
1804:                                                        .item(p);
1805:                                                if (propertyNode.getNodeType() == Node.ELEMENT_NODE) {
1806:                                                    Element propEl = (Element) propertyNode;
1807:                                                    if (propEl.getTagName()
1808:                                                            .equals(PROPERTY)) {
1809:                                                        String propName = propEl
1810:                                                                .getAttribute(NAME);
1811:                                                        String propValue = propEl
1812:                                                                .getAttribute(VALUE);
1813:
1814:                                                        if (propName != null
1815:                                                                && propName
1816:                                                                        .length() > 0
1817:                                                                && propValue != null
1818:                                                                && propValue
1819:                                                                        .length() > 0) {
1820:                                                            if (propName
1821:                                                                    .equals(CHANNEL_PROP)) {
1822:                                                                int index = propValue
1823:                                                                        .lastIndexOf("/");
1824:                                                                propValue = propValue
1825:                                                                        .substring(index + 1);
1826:                                                                if (propValue != null
1827:                                                                        && propValue
1828:                                                                                .length() > 0) {
1829:                                                                    String synChannelRef = channelReference(
1830:                                                                            siteId,
1831:                                                                            propValue);
1832:                                                                    try {
1833:                                                                        MessageChannelEdit c = editChannel(synChannelRef);
1834:                                                                        synProps
1835:                                                                                .setProperty(
1836:                                                                                        propName,
1837:                                                                                        channelRef
1838:                                                                                                .toString());
1839:                                                                    } catch (IdUnusedException e) {
1840:                                                                        // do not add channel b/c it does not exist in tool
1841:                                                                        M_log
1842:                                                                                .warn("Synoptic Tool Channel option not added- "
1843:                                                                                        + synChannelRef
1844:                                                                                        + ":"
1845:                                                                                        + e);
1846:                                                                    }
1847:                                                                }
1848:                                                            } else {
1849:                                                                synProps
1850:                                                                        .setProperty(
1851:                                                                                propName,
1852:                                                                                propValue);
1853:                                                            }
1854:                                                        }
1855:                                                    }
1856:                                                }
1857:                                            }
1858:                                        }
1859:                                    }
1860:                                }
1861:                                Session session = m_sessionManager
1862:                                        .getCurrentSession();
1863:                                ToolSession toolSession = session
1864:                                        .getToolSession(synTool.getId());
1865:                                if (toolSession.getAttribute(STATE_UPDATE) == null) {
1866:                                    toolSession.setAttribute(STATE_UPDATE,
1867:                                            STATE_UPDATE);
1868:                                }
1869:                                m_siteService.save(site);
1870:                            }
1871:                        }
1872:                    }
1873:
1874:                    // one more pass to update reply-to (now we have a complte id mapping),
1875:                    // and we are ready then to create the message
1876:                    children2 = root.getChildNodes();
1877:                    length2 = children2.getLength();
1878:                    for (int i2 = 0; i2 < length2; i2++) {
1879:                        Node child2 = children2.item(i2);
1880:                        if (child2.getNodeType() == Node.ELEMENT_NODE) {
1881:                            Element element2 = (Element) child2;
1882:
1883:                            // get the "channel" child
1884:                            if (element2.getTagName().equals("channel")) {
1885:                                NodeList children3 = element2.getChildNodes();
1886:                                final int length3 = children3.getLength();
1887:                                for (int i3 = 0; i3 < length3; i3++) {
1888:                                    Node child3 = children3.item(i3);
1889:                                    if (child3.getNodeType() == Node.ELEMENT_NODE) {
1890:                                        Element element3 = (Element) child3;
1891:
1892:                                        // for "message" children
1893:                                        if (element3.getTagName().equals(
1894:                                                "message")) {
1895:                                            // a flag showing if continuing merging the message
1896:                                            boolean goAhead = true;
1897:
1898:                                            // get the header child
1899:                                            NodeList children4 = element3
1900:                                                    .getChildNodes();
1901:                                            final int length4 = children4
1902:                                                    .getLength();
1903:                                            for (int i4 = 0; i4 < length4; i4++) {
1904:                                                Node child4 = children4
1905:                                                        .item(i4);
1906:                                                if (child4.getNodeType() == Node.ELEMENT_NODE) {
1907:                                                    Element element4 = (Element) child4;
1908:
1909:                                                    // for "header" children
1910:                                                    if (element4.getTagName()
1911:                                                            .equals("header")) {
1912:                                                        // adjust the replyTo
1913:                                                        String oldReplyTo = element4
1914:                                                                .getAttribute("replyTo");
1915:                                                        if ((oldReplyTo != null)
1916:                                                                && (oldReplyTo
1917:                                                                        .length() > 0)) {
1918:                                                            String newReplyTo = (String) ids
1919:                                                                    .get(oldReplyTo);
1920:                                                            if (newReplyTo != null) {
1921:                                                                element4
1922:                                                                        .setAttribute(
1923:                                                                                "replyTo",
1924:                                                                                newReplyTo);
1925:                                                            }
1926:                                                        }
1927:
1928:                                                        // only merge this message when the userId has the right role
1929:                                                        String fUserId = element4
1930:                                                                .getAttribute("from");
1931:                                                        if (!fUserId
1932:                                                                .equalsIgnoreCase("postmaster")
1933:                                                                && !userSet
1934:                                                                        .contains(element4
1935:                                                                                .getAttribute("from"))) {
1936:                                                            goAhead = false;
1937:                                                        }
1938:                                                        // TODO: reall want a draft? -ggolden
1939:                                                        // set draft status based upon property setting
1940:                                                        if ("false"
1941:                                                                .equalsIgnoreCase(m_serverConfigurationService
1942:                                                                        .getString("import.importAsDraft"))) {
1943:                                                            String draftAttribute = element4
1944:                                                                    .getAttribute("draft");
1945:                                                            if (draftAttribute
1946:                                                                    .equalsIgnoreCase("true")
1947:                                                                    || draftAttribute
1948:                                                                            .equalsIgnoreCase("false"))
1949:                                                                element4
1950:                                                                        .setAttribute(
1951:                                                                                "draft",
1952:                                                                                draftAttribute);
1953:                                                            else
1954:                                                                element4
1955:                                                                        .setAttribute(
1956:                                                                                "draft",
1957:                                                                                "true");
1958:                                                        } else {
1959:                                                            element4
1960:                                                                    .setAttribute(
1961:                                                                            "draft",
1962:                                                                            "true");
1963:                                                        }
1964:                                                    }
1965:                                                }
1966:                                            }
1967:
1968:                                            // merge if ok
1969:                                            if (goAhead) {
1970:                                                // create a new message in the channel
1971:                                                MessageEdit edit = channel
1972:                                                        .mergeMessage(element3);
1973:                                                // commit the new message without notification
1974:                                                channel
1975:                                                        .commitMessage(
1976:                                                                edit,
1977:                                                                NotificationService.NOTI_NONE);
1978:                                                count++;
1979:                                            }
1980:                                        }
1981:                                    }
1982:                                }
1983:                            }
1984:                        }
1985:                    }
1986:                } catch (Exception any) {
1987:                    M_log.warn("mergeMessages(): exception in handling "
1988:                            + serviceName() + " : ", any);
1989:                }
1990:
1991:                results.append("merging " + getLabel() + " channel "
1992:                        + channelRef + " (" + count + ") messages.\n");
1993:                return results.toString();
1994:
1995:            } // merge
1996:
1997:            /**
1998:             * Import the synoptic tool options from another site
1999:             * 
2000:             * @param fromContext
2001:             * @param toContext
2002:             */
2003:            public void transferSynopticOptions(String fromContext,
2004:                    String toContext) {
2005:                try {
2006:                    // transfer the synoptic tool options
2007:                    Site fromSite = m_siteService.getSite(fromContext);
2008:                    ToolConfiguration fromSynTool = fromSite
2009:                            .getToolForCommonId("sakai.synoptic." + getLabel());
2010:                    Properties fromSynProp = fromSynTool.getPlacementConfig();
2011:
2012:                    Site toSite = m_siteService.getSite(toContext);
2013:                    ToolConfiguration toSynTool = toSite
2014:                            .getToolForCommonId("sakai.synoptic." + getLabel());
2015:                    Properties toSynProp = toSynTool.getPlacementConfig();
2016:
2017:                    if (fromSynProp != null && !fromSynProp.isEmpty()) {
2018:                        Set synPropSet = fromSynProp.keySet();
2019:                        Iterator propIter = synPropSet.iterator();
2020:                        while (propIter.hasNext()) {
2021:                            String propName = ((String) propIter.next());
2022:                            String propValue = fromSynProp
2023:                                    .getProperty(propName);
2024:                            if (propValue != null && propValue.length() > 0) {
2025:                                if (propName.equals(CHANNEL_PROP)) {
2026:                                    int index = propValue.lastIndexOf("/");
2027:                                    propValue = propValue.substring(index + 1);
2028:                                    if (propValue != null
2029:                                            && propValue.length() > 0) {
2030:                                        String channelRef = channelReference(
2031:                                                toContext, propValue);
2032:                                        toSynProp.setProperty(propName,
2033:                                                channelRef.toString());
2034:                                    }
2035:                                } else {
2036:                                    toSynProp.setProperty(propName, propValue);
2037:                                }
2038:                            }
2039:                        }
2040:
2041:                        Session session = m_sessionManager.getCurrentSession();
2042:                        ToolSession toolSession = session
2043:                                .getToolSession(toSynTool.getId());
2044:                        if (toolSession.getAttribute(STATE_UPDATE) == null) {
2045:                            toolSession
2046:                                    .setAttribute(STATE_UPDATE, STATE_UPDATE);
2047:                        }
2048:
2049:                        m_siteService.save(toSite);
2050:                    }
2051:                } catch (PermissionException pe) {
2052:                    M_log.warn(
2053:                            "PermissionException transferring synoptic options for "
2054:                                    + serviceName() + ':', pe);
2055:                } catch (IdUnusedException e) {
2056:                    M_log.warn("Channel " + fromContext + " cannot be found. ");
2057:                } catch (Exception e) {
2058:                    M_log.warn(
2059:                            "transferSynopticOptions(): exception in handling "
2060:                                    + serviceName() + " : ", e);
2061:                }
2062:            }
2063:
2064:            /**
2065:             * Handle the extra "categtories" stuff in the channel part of the merge xml.
2066:             * 
2067:             * @param element
2068:             *        The "channel" node child.
2069:             * @param channelRef
2070:             *        The message channel ref.
2071:             */
2072:            protected void parseMergeChannelExtra(Element element3,
2073:                    String channelRef) {
2074:            }
2075:
2076:            /**
2077:             * Setup a main message channel for a site.
2078:             * 
2079:             * @param siteId
2080:             *        The site id.
2081:             */
2082:            protected void enableMessageChannel(String siteId) {
2083:                // form the channel name
2084:                String channelRef = channelReference(siteId,
2085:                        SiteService.MAIN_CONTAINER);
2086:
2087:                // see if there's a channel
2088:                try {
2089:                    getChannel(channelRef);
2090:                } catch (IdUnusedException un) {
2091:                    try {
2092:                        // create a channel
2093:                        MessageChannelEdit edit = addChannel(channelRef);
2094:                        commitChannel(edit);
2095:                    } catch (IdUsedException e) {
2096:                    } catch (IdInvalidException e) {
2097:                    } catch (PermissionException e) {
2098:                    }
2099:                } catch (PermissionException e) {
2100:                }
2101:            }
2102:
2103:            /**
2104:             * Remove the main message channel for a site.
2105:             * 
2106:             * @param site
2107:             *        The site.
2108:             */
2109:            protected void disableMessageChannel(String siteId) {
2110:                // TODO: we do nothing now - channels hang around after the tool is removed from the site or the site is deleted -ggolden
2111:            }
2112:
2113:            /**********************************************************************************************************************************************************************************************************************************************************
2114:             * MessageChannel implementation
2115:             *********************************************************************************************************************************************************************************************************************************************************/
2116:
2117:            public class BaseMessageChannelEdit extends Observable implements 
2118:                    MessageChannelEdit, SessionBindingListener {
2119:                /** The context in which this channel exists. */
2120:                protected String m_context = null;
2121:
2122:                /** The channel id, unique within the context. */
2123:                protected String m_id = null;
2124:
2125:                /** The properties. */
2126:                protected ResourcePropertiesEdit m_properties = null;
2127:
2128:                /** When true, the channel has been removed. */
2129:                protected boolean m_isRemoved = false;
2130:
2131:                /** The event code for this edit. */
2132:                protected String m_event = null;
2133:
2134:                /** Active flag. */
2135:                protected boolean m_active = false;
2136:
2137:                /**
2138:                 * Construct with a reference.
2139:                 * 
2140:                 * @param ref
2141:                 *        The channel reference.
2142:                 */
2143:                public BaseMessageChannelEdit(String ref) {
2144:                    // set the ids
2145:                    Reference r = m_entityManager.newReference(ref);
2146:                    m_context = r.getContext();
2147:                    m_id = r.getId();
2148:
2149:                    // setup for properties
2150:                    m_properties = new BaseResourcePropertiesEdit();
2151:
2152:                } // BaseMessageChannelEdit
2153:
2154:                /**
2155:                 * Construct as a copy of another.
2156:                 * 
2157:                 * @param id
2158:                 *        The other to copy.
2159:                 */
2160:                public BaseMessageChannelEdit(MessageChannel other) {
2161:                    // set the ids
2162:                    m_context = other.getContext();
2163:                    m_id = other.getId();
2164:
2165:                    // setup for properties
2166:                    m_properties = new BaseResourcePropertiesEdit();
2167:                    m_properties.addAll(other.getProperties());
2168:
2169:                } // BaseMessageChannelEdit
2170:
2171:                /**
2172:                 * Construct from a channel (and possibly messages) already defined in XML in a DOM tree. The Channel is added to storage.
2173:                 * 
2174:                 * @param el
2175:                 *        The XML DOM element defining the channel.
2176:                 */
2177:                public BaseMessageChannelEdit(Element el) {
2178:                    // setup for properties
2179:                    m_properties = new BaseResourcePropertiesEdit();
2180:
2181:                    // read the ids
2182:                    m_id = el.getAttribute("id");
2183:                    m_context = el.getAttribute("context");
2184:
2185:                    // the children (properties, ignore messages)
2186:                    NodeList children = el.getChildNodes();
2187:                    final int length = children.getLength();
2188:                    for (int i = 0; i < length; i++) {
2189:                        Node child = children.item(i);
2190:                        if (child.getNodeType() != Node.ELEMENT_NODE)
2191:                            continue;
2192:                        Element element = (Element) child;
2193:
2194:                        // look for properties (ignore possible "message" entries)
2195:                        if (element.getTagName().equals("properties")) {
2196:                            // re-create properties
2197:                            m_properties = new BaseResourcePropertiesEdit(
2198:                                    element);
2199:                        }
2200:                    }
2201:
2202:                } // BaseMessageChannelEdit
2203:
2204:                /**
2205:                 * Clean up.
2206:                 */
2207:                protected void finalize() {
2208:                    deleteObservers();
2209:
2210:                    // catch the case where an edit was made but never resolved
2211:                    if (m_active) {
2212:                        cancelChannel(this );
2213:                    }
2214:
2215:                } // finalize
2216:
2217:                /**
2218:                 * Set the channel as removed.
2219:                 * 
2220:                 * @param event
2221:                 *        The tracking event associated with this action.
2222:                 */
2223:                public void setRemoved(Event event) {
2224:                    m_isRemoved = true;
2225:
2226:                    // channel notification
2227:                    notify(event);
2228:
2229:                    // now clear observers
2230:                    deleteObservers();
2231:
2232:                } // setRemoved
2233:
2234:                /**
2235:                 * Access the context of the resource.
2236:                 * 
2237:                 * @return The context.
2238:                 */
2239:                public String getContext() {
2240:                    return m_context;
2241:
2242:                } // getContext
2243:
2244:                /**
2245:                 * Access the id of the resource.
2246:                 * 
2247:                 * @return The id.
2248:                 */
2249:                public String getId() {
2250:                    return m_id;
2251:
2252:                } // getId
2253:
2254:                /**
2255:                 * Access the URL which can be used to access the resource.
2256:                 * 
2257:                 * @return The URL which can be used to access the resource.
2258:                 */
2259:                public String getUrl() {
2260:                    return m_serverConfigurationService.getAccessUrl()
2261:                            + getReference();
2262:
2263:                } // getUrl
2264:
2265:                /**
2266:                 * Access the internal reference which can be used to access the resource from within the system.
2267:                 * 
2268:                 * @return The the internal reference which can be used to access the resource from within the system.
2269:                 */
2270:                public String getReference() {
2271:                    return channelReference(m_context, m_id);
2272:
2273:                } // getReference
2274:
2275:                /**
2276:                 * @inheritDoc
2277:                 */
2278:                public String getReference(String rootProperty) {
2279:                    return getReference();
2280:                }
2281:
2282:                /**
2283:                 * @inheritDoc
2284:                 */
2285:                public String getUrl(String rootProperty) {
2286:                    return getUrl();
2287:                }
2288:
2289:                /**
2290:                 * Access the channel's properties.
2291:                 * 
2292:                 * @return The channel's properties.
2293:                 */
2294:                public ResourceProperties getProperties() {
2295:                    return m_properties;
2296:
2297:                } // getProperties
2298:
2299:                /**
2300:                 * check permissions for getMessages() or getMessage().
2301:                 * 
2302:                 * @return true if the user is allowed to get messages from this channel, false if not.
2303:                 */
2304:                public boolean allowGetMessages() {
2305:                    return unlockCheck(SECURE_READ, getReference());
2306:
2307:                } // allowGetMessages
2308:
2309:                /**
2310:                 * @inheritDoc
2311:                 */
2312:                public Collection getGroupsAllowGetMessage() {
2313:                    return getGroupsAllowFunction(SECURE_READ);
2314:                }
2315:
2316:                /**
2317:                 * Return a list of all or filtered messages in the channel. The order in which the messages will be found in the iteration is by date, oldest first if ascending is true, newest first if ascending is false.
2318:                 * 
2319:                 * @param filter
2320:                 *        A filtering object to accept messages, or null if no filtering is desired.
2321:                 * @param ascending
2322:                 *        Order of messages, ascending if true, descending if false
2323:                 * @return a list on channel Message objects or specializations of Message objects (may be empty).
2324:                 * @exception PermissionException
2325:                 *            if the user does not have read permission to the channel.
2326:                 */
2327:                public List getMessages(Filter filter, boolean ascending)
2328:                        throws PermissionException {
2329:                    // check security on the channel (throws if not permitted)
2330:                    unlock(SECURE_READ, getReference());
2331:                    // track event
2332:                    // m_eventTrackingService.post(m_eventTrackingService.newEvent(eventId(SECURE_READ), getReference(), false));
2333:
2334:                    return findFilterMessages(filter, ascending);
2335:
2336:                } // getMessages
2337:
2338:                /**
2339:                 * Return a specific channel message, as specified by message name.
2340:                 * 
2341:                 * @param messageId
2342:                 *        The id of the message to get.
2343:                 * @return the Message that has the specified id.
2344:                 * @exception IdUnusedException
2345:                 *            If this name is not a defined message in this channel.
2346:                 * @exception PermissionException
2347:                 *            If the user does not have any permissions to read the message.
2348:                 */
2349:                public Message getMessage(String messageId)
2350:                        throws IdUnusedException, PermissionException {
2351:                    // check security on the message
2352:                    if (!allowGetMessage(getReference(), messageReference(
2353:                            getReference(), messageId))) {
2354:                        throw new PermissionException(m_sessionManager
2355:                                .getCurrentSessionUserId(),
2356:                                eventId(SECURE_READ), messageReference(
2357:                                        getReference(), messageId));
2358:                    }
2359:
2360:                    Message m = findMessage(messageId);
2361:
2362:                    if (m == null)
2363:                        throw new IdUnusedException(messageId);
2364:
2365:                    // track event
2366:                    // m_eventTrackingService.post(m_eventTrackingService.newEvent(eventId(SECURE_READ), m.getReference(), false));
2367:
2368:                    return m;
2369:
2370:                } // getMessage
2371:
2372:                /**
2373:                 * check permissions for editMessage()
2374:                 * 
2375:                 * @param id
2376:                 *        The message id.
2377:                 * @return true if the user is allowed to update the message, false if not.
2378:                 */
2379:                public boolean allowEditMessage(String messageId) {
2380:                    Message m = findMessage(messageId);
2381:                    if (m == null)
2382:                        return false;
2383:
2384:                    return allowEditMessage(m, SECURE_UPDATE_OWN,
2385:                            SECURE_UPDATE_ANY);
2386:
2387:                } // allowEditMessage
2388:
2389:                /**
2390:                 * check permissions for the message, to be able to edit or save it.
2391:                 * 
2392:                 * @param m
2393:                 *        The message.
2394:                 * @return true if the user is allowed to update the message, false if not.
2395:                 */
2396:                protected boolean allowEditMessage(Message m, String fOwn,
2397:                        String fAny) {
2398:                    // is this the user's own?
2399:                    if (m.getHeader().getFrom().getId().equals(
2400:                            m_sessionManager.getCurrentSessionUserId())) {
2401:                        // own or any
2402:                        return unlockCheck2(fOwn, fAny, m.getReference());
2403:                    }
2404:
2405:                    else {
2406:                        // just any
2407:                        return unlockCheck(fAny, m.getReference());
2408:                    }
2409:                }
2410:
2411:                /**
2412:                 * Return a specific channel message, as specified by message name, locked for update. Must commitEdit() to make official, or cancelEdit() when done!
2413:                 * 
2414:                 * @param messageId
2415:                 *        The id of the message to get.
2416:                 * @return the Message that has the specified id.
2417:                 * @exception IdUnusedException
2418:                 *            If this name is not a defined message in this channel.
2419:                 * @exception PermissionException
2420:                 *            If the user does not have any permissions to read the message.
2421:                 * @exception InUseException
2422:                 *            if the current user does not have permission to mess with this user.
2423:                 */
2424:                public MessageEdit editMessage(String messageId)
2425:                        throws IdUnusedException, PermissionException,
2426:                        InUseException {
2427:                    Message m = findMessage(messageId);
2428:                    if (m == null)
2429:                        throw new IdUnusedException(messageId);
2430:
2431:                    // pick the security function
2432:                    String function = null;
2433:                    if (m.getHeader().getFrom().getId().equals(
2434:                            m_sessionManager.getCurrentSessionUserId())) {
2435:                        // own or any
2436:                        function = SECURE_UPDATE_OWN;
2437:                    } else {
2438:                        // just any
2439:                        function = SECURE_UPDATE_ANY;
2440:                    }
2441:
2442:                    // security check
2443:                    if (!allowEditMessage(messageId)) {
2444:                        throw new PermissionException(m_sessionManager
2445:                                .getCurrentSessionUserId(), eventId(function),
2446:                                m.getReference());
2447:                    }
2448:
2449:                    // ignore the cache - get the message with a lock from the info store
2450:                    MessageEdit msg = m_storage.editMessage(this , messageId);
2451:                    if (msg == null)
2452:                        throw new InUseException(messageId);
2453:
2454:                    ((BaseMessageEdit) msg).setEvent(function);
2455:
2456:                    return msg;
2457:
2458:                } // editMessage
2459:
2460:                /**
2461:                 * {@inheritDoc}
2462:                 */
2463:                public void commitMessage(MessageEdit edit) {
2464:                    commitMessage(edit, NotificationService.NOTI_OPTIONAL);
2465:
2466:                } // commitMessage
2467:
2468:                /**
2469:                 * Commit the changes made to a MessageEdit object, and release the lock. The MessageEdit is disabled, and not to be used after this call.
2470:                 * 
2471:                 * @param user
2472:                 *        The UserEdit object to commit.
2473:                 * @param priority
2474:                 *        The notification priority for this commit.
2475:                 */
2476:                public void commitMessage(MessageEdit edit, int priority) {
2477:                    commitMessage(edit, priority, "");
2478:                }
2479:
2480:                /**
2481:                 * Commit the changes made to a MessageEdit object, and release the lock. The MessageEdit is disabled, and not to be used after this call.
2482:                 * 
2483:                 * @param user
2484:                 *        The UserEdit object to commit.
2485:                 * @param priority
2486:                 *        The notification priority for this commit.
2487:                 * @param invoker
2488:                 * 		  The object to be called if a scheduled notification is used.
2489:                 */
2490:                public void commitMessage(MessageEdit edit, int priority,
2491:                        String invokee) {
2492:                    // check for closed edit
2493:                    if (!edit.isActiveEdit()) {
2494:                        try {
2495:                            throw new Exception();
2496:                        } catch (Exception e) {
2497:                            M_log.warn("commitEdit(): closed MessageEdit", e);
2498:                        }
2499:                        return;
2500:                    }
2501:
2502:                    // update the properties
2503:                    // addLiveUpdateProperties(edit.getPropertiesEdit());//%%%
2504:
2505:                    // if this message had a future invocation before, delete it because
2506:                    // this modification changed the date of release so either it will notify it now
2507:                    // or set a new future notification
2508:                    ScheduledInvocationManager scheduledInvocationManager = (ScheduledInvocationManager) ComponentManager
2509:                            .get(org.sakaiproject.api.app.scheduler.ScheduledInvocationManager.class);
2510:
2511:                    if (edit.getProperties().getProperty(SCHED_INV_UUID) != null) {
2512:                        scheduledInvocationManager.deleteDelayedInvocation(edit
2513:                                .getProperties().getProperty(SCHED_INV_UUID));
2514:                        edit.getPropertiesEdit().removeProperty(SCHED_INV_UUID);
2515:
2516:                        Event event = m_eventTrackingService.newEvent(
2517:                                SCHINV_DELETE_EVENT, edit.getReference(), true,
2518:                                priority);
2519:                        m_eventTrackingService.post(event);
2520:                    }
2521:
2522:                    // For Scheduled Notification, compare header date with now to deterine
2523:                    // if an immediate notification is needed or a scheduled one
2524:                    // Put here since need to store uuid for notification just in case need to
2525:                    // delete/modify
2526:                    Time now = m_timeService.newTime();
2527:                    boolean transientNotification = true;
2528:
2529:                    if (now.before(edit.getHeader().getDate())
2530:                            && priority != NotificationService.NOTI_NONE) {
2531:                        final String uuid = scheduledInvocationManager
2532:                                .createDelayedInvocation(edit.getHeader()
2533:                                        .getDate(), invokee, edit
2534:                                        .getReference());
2535:
2536:                        final ResourcePropertiesEdit editProps = edit
2537:                                .getPropertiesEdit();
2538:
2539:                        editProps.addProperty(SCHED_INV_UUID, uuid);
2540:
2541:                        transientNotification = false;
2542:                    }
2543:
2544:                    // complete the edit
2545:                    m_storage.commitMessage(this , edit);
2546:
2547:                    // clear out any thread local caching of this message, since it has just changed
2548:                    m_threadLocalManager.set(edit.getReference(), null);
2549:
2550:                    // track event
2551:                    Event event = m_eventTrackingService.newEvent(
2552:                            eventId(((BaseMessageEdit) edit).getEvent()), edit
2553:                                    .getReference(), true, priority);
2554:                    m_eventTrackingService.post(event);
2555:
2556:                    // channel notification
2557:                    if (transientNotification)
2558:                        notify(event);
2559:
2560:                    // close the edit object
2561:                    ((BaseMessageEdit) edit).closeEdit();
2562:
2563:                } // commitMessage
2564:
2565:                /**
2566:                 * Cancel the changes made to a MessageEdit object, and release the lock. The MessageEdit is disabled, and not to be used after this call.
2567:                 * 
2568:                 * @param user
2569:                 *        The UserEdit object to commit.
2570:                 */
2571:                public void cancelMessage(MessageEdit edit) {
2572:                    // check for closed edit
2573:                    if (!edit.isActiveEdit()) {
2574:                        try {
2575:                            throw new Exception();
2576:                        } catch (Exception e) {
2577:                            M_log.warn("commitEdit(): closed MessageEdit", e);
2578:                        }
2579:                        return;
2580:                    }
2581:
2582:                    // release the edit lock
2583:                    m_storage.cancelMessage(this , edit);
2584:
2585:                    // if an add, remove the message
2586:                    if (SECURE_ADD.equals(((BaseMessageEdit) edit).getEvent())) {
2587:                        m_storage.removeMessage(this , edit);
2588:                    }
2589:
2590:                    // close the edit object
2591:                    ((BaseMessageEdit) edit).closeEdit();
2592:
2593:                } // cancelMessage
2594:
2595:                /**
2596:                 * check permissions for addMessage().
2597:                 * 
2598:                 * @return true if the user is allowed to addMessage(...), false if not.
2599:                 */
2600:                public boolean allowAddMessage() {
2601:                    // checking allow at the channel (site) level
2602:                    if (allowAddChannelMessage())
2603:                        return true;
2604:
2605:                    // if not, see if the user has any groups to which adds are allowed
2606:                    return (!getGroupsAllowAddMessage().isEmpty());
2607:
2608:                } // allowAddMessage
2609:
2610:                /**
2611:                 * @inheritDoc
2612:                 */
2613:                public boolean allowAddDraftMessage() {
2614:                    // checking for permission for allow adding any message
2615:                    if (!allowAddMessage())
2616:                        return false;
2617:
2618:                    // if allow to add message, one can save it as draft if only he can modify the draft afterwards.
2619:                    return (unlockCheck2(SECURE_UPDATE_ANY, SECURE_UPDATE_OWN,
2620:                            getReference()));
2621:
2622:                } // allowAddDraftMessage
2623:
2624:                /**
2625:                 * @inheritDoc
2626:                 */
2627:                public boolean allowAddChannelMessage() {
2628:                    // check for messages that will be channel-wide:
2629:                    // base the check for SECURE_ADD on the site and the channel only (not the groups).
2630:
2631:                    // check security on the channel (throws if not permitted)
2632:                    return unlockCheck(SECURE_ADD, getReference());
2633:                }
2634:
2635:                /**
2636:                 * @inheritDoc
2637:                 */
2638:                public Collection getGroupsAllowAddMessage() {
2639:                    return getGroupsAllowFunction(SECURE_ADD);
2640:                }
2641:
2642:                /**
2643:                 * @inheritDoc
2644:                 */
2645:                public Collection getGroupsAllowRemoveMessage(boolean own) {
2646:                    return getGroupsAllowFunction(own ? SECURE_REMOVE_OWN
2647:                            : SECURE_REMOVE_ANY);
2648:                }
2649:
2650:                /**
2651:                 * Add a new message to this channel. Must commitEdit() to make official, or cancelEdit() when done!
2652:                 * 
2653:                 * @return The newly added message.
2654:                 * @exception PermissionException
2655:                 *            If the user does not have write permission to the channel.
2656:                 */
2657:                public MessageEdit addMessage() throws PermissionException {
2658:                    // security check
2659:                    if (!allowAddMessage()) {
2660:                        throw new PermissionException(m_sessionManager
2661:                                .getCurrentSessionUserId(),
2662:                                eventId(SECURE_ADD), getReference());
2663:                    }
2664:
2665:                    String id = null;
2666:                    // allocate a new unique message id
2667:                    id = m_idManager.createUuid();
2668:
2669:                    // get a new message in the info store
2670:                    MessageEdit msg = m_storage.putMessage(this , id);
2671:
2672:                    ((BaseMessageEdit) msg).setEvent(SECURE_ADD);
2673:
2674:                    return msg;
2675:
2676:                } // addMessage
2677:
2678:                /**
2679:                 * Merge in a new message as defined in the xml. Must commitEdit() to make official, or cancelEdit() when done!
2680:                 * 
2681:                 * @param el
2682:                 *        The message information in XML in a DOM element.
2683:                 * @return The newly added message, locked for update.
2684:                 * @exception PermissionException
2685:                 *            If the user does not have write permission to the channel.
2686:                 * @exception IdUsedException
2687:                 *            if the user id is already used.
2688:                 */
2689:                public MessageEdit mergeMessage(Element el)
2690:                        throws PermissionException, IdUsedException {
2691:                    // check security on the channel (throws if not permitted)
2692:                    unlock(SECURE_ADD, getReference());
2693:
2694:                    Message msgFromXml = (Message) newResource(this , el);
2695:
2696:                    // reserve a message with this id from the info store - if it's in use, this will return null
2697:                    MessageEdit msg = m_storage.putMessage(this , msgFromXml
2698:                            .getId());
2699:                    if (msg == null) {
2700:                        throw new IdUsedException(msgFromXml.getId());
2701:                    }
2702:
2703:                    // transfer from the XML read object to the Edit
2704:                    ((BaseMessageEdit) msg).set(msgFromXml);
2705:
2706:                    // clear the groups and mark the message as channel
2707:                    // TODO: might be better done in merge(), but easier here -ggolden
2708:                    if (MessageHeader.MessageAccess.GROUPED == msg.getHeader()
2709:                            .getAccess()) {
2710:                        ((BaseMessageHeaderEdit) msg.getHeaderEdit()).m_access = MessageHeader.MessageAccess.CHANNEL;
2711:                        ((BaseMessageHeaderEdit) msg.getHeaderEdit()).m_groups = new Vector();
2712:                    }
2713:
2714:                    ((BaseMessageEdit) msg).setEvent(SECURE_ADD);
2715:
2716:                    return msg;
2717:
2718:                } // mergeMessage
2719:
2720:                /**
2721:                 * check permissions for removeMessage().
2722:                 * 
2723:                 * @param m
2724:                 *        The message from this channel to remove.
2725:                 * @return true if the user is allowed to removeMessage(...), false if not.
2726:                 */
2727:                public boolean allowRemoveMessage(Message m) {
2728:                    // this is true if we can remove it due to any of our group membership
2729:                    boolean allowed = allowEditMessage(m, SECURE_REMOVE_OWN,
2730:                            SECURE_REMOVE_ANY);
2731:
2732:                    // but we must also assure, that for grouped messages, we can remove it from ALL of the groups
2733:                    if (allowed
2734:                            && (m.getHeader().getAccess() == MessageHeader.MessageAccess.GROUPED)) {
2735:                        boolean own = (m.getHeader().getFrom() == null) ? true
2736:                                : m.getHeader().getFrom().getId().equals(
2737:                                        m_sessionManager
2738:                                                .getCurrentSessionUserId());
2739:                        allowed = EntityCollections
2740:                                .isContainedEntityRefsToEntities(m.getHeader()
2741:                                        .getGroups(),
2742:                                        getGroupsAllowRemoveMessage(own));
2743:                    }
2744:
2745:                    return allowed;
2746:
2747:                } // allowRemoveMessage
2748:
2749:                /**
2750:                 * @inheritDoc
2751:                 */
2752:                public void removeMessage(String messageId)
2753:                        throws PermissionException {
2754:                    // ignore the cache - get the message with a lock from the info store
2755:                    MessageEdit message = m_storage
2756:                            .editMessage(this , messageId);
2757:                    if (message == null) {
2758:                        try {
2759:                            throw new Exception();
2760:                        } catch (Exception e) {
2761:                            M_log.warn("removeMessage(String): null edit ", e);
2762:                        }
2763:                        return;
2764:                    }
2765:
2766:                    removeMessage(message);
2767:
2768:                } // removeMessage
2769:
2770:                /**
2771:                 * Remove a message from the channel - it must be locked from editMessage().
2772:                 * 
2773:                 * @param message
2774:                 *        The message from this channel to remove.
2775:                 * @exception PermissionException
2776:                 *            if the user does not have permission to remove the message.
2777:                 */
2778:                public void removeMessage(MessageEdit message)
2779:                        throws PermissionException {
2780:                    // check for closed edit
2781:                    if (!message.isActiveEdit()) {
2782:                        try {
2783:                            throw new Exception();
2784:                        } catch (Exception e) {
2785:                            M_log
2786:                                    .warn(
2787:                                            "removeMessage(): closed MessageEdit",
2788:                                            e);
2789:                        }
2790:                        return;
2791:                    }
2792:
2793:                    // pick the security function
2794:                    String function = null;
2795:                    if (message.getHeader().getFrom().getId().equals(
2796:                            m_sessionManager.getCurrentSessionUserId())) {
2797:                        // own or any
2798:                        function = SECURE_REMOVE_OWN;
2799:                    } else {
2800:                        // just any
2801:                        function = SECURE_REMOVE_ANY;
2802:                    }
2803:
2804:                    // securityCheck
2805:                    if (!allowRemoveMessage(message)) {
2806:                        cancelMessage(message);
2807:                        throw new PermissionException(m_sessionManager
2808:                                .getCurrentSessionUserId(), eventId(function),
2809:                                message.getReference());
2810:                    }
2811:
2812:                    m_storage.removeMessage(this , message);
2813:
2814:                    // track event
2815:                    Event event = m_eventTrackingService.newEvent(
2816:                            eventId(function), message.getReference(), true);
2817:                    m_eventTrackingService.post(event);
2818:
2819:                    // channel notification
2820:                    notify(event);
2821:
2822:                    // close the edit object
2823:                    ((BaseMessageEdit) message).closeEdit();
2824:
2825:                    // remove any realm defined for this resource
2826:                    try {
2827:                        m_authzGroupService.removeAuthzGroup(message
2828:                                .getReference());
2829:                    } catch (AuthzPermissionException e) {
2830:                        M_log.warn("removeMessage: removing realm for : "
2831:                                + message.getReference() + " : " + e);
2832:                    }
2833:
2834:                } // removeMessage
2835:
2836:                /**
2837:                 * Serialize the resource into XML, adding an element to the doc under the top of the stack element.
2838:                 * 
2839:                 * @param doc
2840:                 *        The DOM doc to contain the XML (or null for a string return).
2841:                 * @param stack
2842:                 *        The DOM elements, the top of which is the containing element of the new "resource" element.
2843:                 * @return The newly added element.
2844:                 */
2845:                public Element toXml(Document doc, Stack stack) {
2846:                    Element channel = doc.createElement("channel");
2847:
2848:                    if (stack.isEmpty()) {
2849:                        doc.appendChild(channel);
2850:                    } else {
2851:                        ((Element) stack.peek()).appendChild(channel);
2852:                    }
2853:
2854:                    stack.push(channel);
2855:
2856:                    channel.setAttribute("context", getContext());
2857:                    channel.setAttribute("id", getId());
2858:
2859:                    // properties
2860:                    m_properties.toXml(doc, stack);
2861:
2862:                    stack.pop();
2863:
2864:                    return channel;
2865:
2866:                } // toXml
2867:
2868:                /**
2869:                 * Notify the channel that it has changed (i.e. when a message has changed)
2870:                 * 
2871:                 * @param event
2872:                 *        The event that caused the update.
2873:                 */
2874:                public void notify(Event event) {
2875:                    // notify observers, sending the tracking event to identify the change
2876:                    setChanged();
2877:                    notifyObservers(event);
2878:
2879:                } // notify
2880:
2881:                /**
2882:                 * Find the message, in cache or info store - cache it if newly found.
2883:                 * 
2884:                 * @param messageId
2885:                 *        The id of the message.
2886:                 * @return The message, if found.
2887:                 */
2888:                protected Message findMessage(String messageId) {
2889:                    if ((!m_caching) || (m_channelCache == null)
2890:                            || (m_channelCache.disabled())) {
2891:                        // if we have "cached" the entire set of messages in the thread, get that and find our message there
2892:                        List msgs = (List) m_threadLocalManager
2893:                                .get(getReference() + ".msgs");
2894:                        if (msgs != null) {
2895:                            for (Iterator i = msgs.iterator(); i.hasNext();) {
2896:                                Message m = (Message) i.next();
2897:                                if (m.getId().equals(messageId)) {
2898:                                    return m;
2899:                                }
2900:                            }
2901:                        }
2902:
2903:                        // if we have this one message cached, get that
2904:                        Message m = (Message) m_threadLocalManager
2905:                                .get(messageReference(getReference(), messageId));
2906:
2907:                        // if not, get from storage and cache
2908:                        if (m == null) {
2909:                            m = m_storage.getMessage(this , messageId);
2910:
2911:                            // if we got one, cache it in the thread
2912:                            if (m != null) {
2913:                                m_threadLocalManager.set(m.getReference(), m);
2914:                            }
2915:                        }
2916:
2917:                        return m;
2918:                    }
2919:
2920:                    // use the cache
2921:                    Message m = null;
2922:
2923:                    // messages are cached with the full reference as key
2924:                    String key = messageReference(m_context, m_id, messageId);
2925:
2926:                    // find the message cache
2927:                    Cache msgCache = (Cache) m_messageCaches
2928:                            .get(getReference());
2929:                    if (msgCache == null) {
2930:                        synchronized (m_messageCaches) {
2931:                            // check again
2932:                            msgCache = (Cache) m_messageCaches
2933:                                    .get(getReference());
2934:
2935:                            // if still not there, make one
2936:                            if (msgCache == null) {
2937:                                msgCache = m_memoryService.newCache(service(),
2938:                                        messageReference(m_context, m_id, ""));
2939:                                m_messageCaches.put(getReference(), msgCache);
2940:                            }
2941:                        }
2942:                    }
2943:
2944:                    // if we have it cached, use it (even if it's cached as a null, a miss)
2945:                    if (msgCache.containsKey(key)) {
2946:                        m = (Message) msgCache.get(key);
2947:                    }
2948:
2949:                    // if not in the cache, see if we have it in our info store
2950:                    else {
2951:                        m = m_storage.getMessage(this , messageId);
2952:
2953:                        // if so, cache it, even misses
2954:                        msgCache.put(key, m);
2955:                    }
2956:
2957:                    return m;
2958:
2959:                } // findMessage
2960:
2961:                /**
2962:                 * Find all messages.
2963:                 * 
2964:                 * @return a List of all messages in the channel.
2965:                 */
2966:                protected List findMessages() {
2967:                    if ((!m_caching) || (m_channelCache == null)
2968:                            || (m_channelCache.disabled())) {
2969:                        // if we have done this already in this thread, use that
2970:                        List msgs = (List) m_threadLocalManager
2971:                                .get(getReference() + ".msgs");
2972:                        if (msgs == null) {
2973:                            msgs = m_storage.getMessages(this );
2974:
2975:                            // "cache" the mesasge in the current service in case they are needed again in this thread...
2976:                            m_threadLocalManager.set(getReference() + ".msgs",
2977:                                    msgs);
2978:                        }
2979:
2980:                        return msgs;
2981:                    }
2982:
2983:                    // use the cache
2984:                    List msgs = new Vector();
2985:
2986:                    // find the message cache
2987:                    Cache msgCache = (Cache) m_messageCaches
2988:                            .get(getReference());
2989:                    if (msgCache == null) {
2990:                        synchronized (m_messageCaches) {
2991:                            // check again
2992:                            msgCache = (Cache) m_messageCaches
2993:                                    .get(getReference());
2994:
2995:                            // if still not there, make one
2996:                            if (msgCache == null) {
2997:                                msgCache = m_memoryService.newCache(service(),
2998:                                        messageReference(m_context, m_id, ""));
2999:                                m_messageCaches.put(getReference(), msgCache);
3000:                            }
3001:                        }
3002:                    }
3003:
3004:                    // if the cache is complete, use it
3005:                    if (msgCache.isComplete()) {
3006:                        // get just this channel's messages
3007:                        msgs = msgCache.getAll();
3008:                    }
3009:
3010:                    // otherwise get all the msgs from storage
3011:                    else {
3012:                        // Note: while we are getting from storage, storage might change. These can be processed
3013:                        // after we get the storage entries, and put them in the cache, and mark the cache complete.
3014:                        // -ggolden
3015:                        synchronized (msgCache) {
3016:                            // if we were waiting and it's now complete...
3017:                            if (msgCache.isComplete()) {
3018:                                // get just this channel's messages
3019:                                msgs = msgCache.getAll();
3020:                            } else {
3021:                                // cache up any events to the cache until we get past this load
3022:                                msgCache.holdEvents();
3023:
3024:                                msgs = m_storage.getMessages(this );
3025:                                // update the cache, and mark it complete
3026:                                for (int i = 0; i < msgs.size(); i++) {
3027:                                    Message msg = (Message) msgs.get(i);
3028:                                    msgCache.put(msg.getReference(), msg);
3029:                                }
3030:
3031:                                msgCache.setComplete();
3032:
3033:                                // now we are complete, process any cached events
3034:                                msgCache.processEvents();
3035:                            }
3036:                        }
3037:                    }
3038:
3039:                    return msgs;
3040:
3041:                } // findMessages
3042:
3043:                /**
3044:                 * Find messages, sort, filter by group and the provided filter.
3045:                 * 
3046:                 * @param filter
3047:                 *        A filtering object to accept messages, or null if no filtering is desired.
3048:                 * @param ascending
3049:                 *        Order of messages, ascending if true, descending if false
3050:                 * @return All messages, sorted and filtered.
3051:                 */
3052:                public List findFilterMessages(Filter filter, boolean ascending) {
3053:                    List msgs = findMessages();
3054:                    if (msgs.size() == 0)
3055:                        return msgs;
3056:
3057:                    // sort - natural order is date ascending
3058:                    Collections.sort(msgs);
3059:
3060:                    // reverse, if not ascending
3061:                    if (!ascending) {
3062:                        Collections.reverse(msgs);
3063:                    }
3064:
3065:                    // filter out
3066:                    List filtered = new Vector();
3067:
3068:                    // check for the allowed groups of the current end use if we need it, and only once
3069:                    Collection allowedGroups = null;
3070:
3071:                    for (int i = 0; i < msgs.size(); i++) {
3072:                        Message msg = (Message) msgs.get(i);
3073:
3074:                        // if grouped, check that the end user has get access to any of this message's groups; reject if not
3075:                        if (msg.getHeader().getAccess() == MessageHeader.MessageAccess.GROUPED) {
3076:                            // check the message's groups to the allowed (get) groups for the current user
3077:                            Collection msgGroups = msg.getHeader().getGroups();
3078:
3079:                            // we need the allowed groups, so get it if we have not done so yet
3080:                            if (allowedGroups == null) {
3081:                                allowedGroups = getGroupsAllowGetMessage();
3082:                            }
3083:
3084:                            // reject if there is no intersection
3085:                            if (!isIntersectionGroupRefsToGroups(msgGroups,
3086:                                    allowedGroups))
3087:                                continue;
3088:                        }
3089:
3090:                        // reject if the filter rejects
3091:                        if ((filter != null) && (!filter.accept(msg)))
3092:                            continue;
3093:
3094:                        // if not rejected, keep
3095:                        filtered.add(msg);
3096:                    }
3097:
3098:                    return filtered;
3099:
3100:                } // findFilterMessages
3101:
3102:                /**
3103:                 * See if the collection of group reference strings has at least one group that is in the collection of Group objects.
3104:                 * 
3105:                 * @param groupRefs
3106:                 *        The collection (String) of group references.
3107:                 * @param groups
3108:                 *        The collection (Group) of group objects.
3109:                 * @return true if there is interesection, false if not.
3110:                 */
3111:                protected boolean isIntersectionGroupRefsToGroups(
3112:                        Collection groupRefs, Collection groups) {
3113:                    for (Iterator iRefs = groupRefs.iterator(); iRefs.hasNext();) {
3114:                        String findThisGroupRef = (String) iRefs.next();
3115:                        for (Iterator iGroups = groups.iterator(); iGroups
3116:                                .hasNext();) {
3117:                            String this GroupRef = ((Group) iGroups.next())
3118:                                    .getReference();
3119:                            if (this GroupRef.equals(findThisGroupRef)) {
3120:                                return true;
3121:                            }
3122:                        }
3123:                    }
3124:
3125:                    return false;
3126:                }
3127:
3128:                /**
3129:                 * Test a collection of Group object for the specified group reference
3130:                 * @param groups The collection (Group) of groups
3131:                 * @param groupRef The string group reference to find.
3132:                 * @return true if found, false if not.
3133:                 */
3134:                protected boolean groupCollectionContainsRefString(
3135:                        Collection groups, String groupRef) {
3136:                    for (Iterator i = groups.iterator(); i.hasNext();) {
3137:                        Group group = (Group) i.next();
3138:                        if (group.getReference().equals(groupRef))
3139:                            return true;
3140:                    }
3141:
3142:                    return false;
3143:                }
3144:
3145:                /**
3146:                 * Access the event code for this edit.
3147:                 * 
3148:                 * @return The event code for this edit.
3149:                 */
3150:                protected String getEvent() {
3151:                    return m_event;
3152:                }
3153:
3154:                /**
3155:                 * Set the event code for this edit.
3156:                 * 
3157:                 * @param event
3158:                 *        The event code for this edit.
3159:                 */
3160:                protected void setEvent(String event) {
3161:                    m_event = event;
3162:                }
3163:
3164:                /**
3165:                 * Access the resource's properties for modification
3166:                 * 
3167:                 * @return The resource's properties.
3168:                 */
3169:                public ResourcePropertiesEdit getPropertiesEdit() {
3170:                    return m_properties;
3171:
3172:                } // getPropertiesEdit
3173:
3174:                /**
3175:                 * Enable editing.
3176:                 */
3177:                public void activate() {
3178:                    m_active = true;
3179:
3180:                } // activate
3181:
3182:                /**
3183:                 * Check to see if the edit is still active, or has already been closed.
3184:                 * 
3185:                 * @return true if the edit is active, false if it's been closed.
3186:                 */
3187:                public boolean isActiveEdit() {
3188:                    return m_active;
3189:
3190:                } // isActiveEdit
3191:
3192:                /**
3193:                 * Close the edit object - it cannot be used after this.
3194:                 */
3195:                protected void closeEdit() {
3196:                    m_active = false;
3197:
3198:                } // closeEdit
3199:
3200:                /**
3201:                 * Get the groups of this channel's contex-site that the end user has permission to "function" in.
3202:                 * 
3203:                 * @param function
3204:                 *        The function to check
3205:                 * @return The Collection (Group) of groups defined for the context of this channel that the end user has specified permissions in, empty if none.
3206:                 */
3207:                protected Collection getGroupsAllowFunction(String function) {
3208:                    Collection rv = new Vector();
3209:
3210:                    try {
3211:                        // get the channel's site's groups
3212:                        Site site = m_siteService.getSite(m_context);
3213:                        Collection groups = site.getGroups();
3214:
3215:                        // if the user has SECURE_ALL_GROUPS in the context (site), and the function for the channel (channel,site), or is super, select all site groups
3216:                        if (m_securityService.isSuperUser()
3217:                                || (m_authzGroupService.isAllowed(
3218:                                        m_sessionManager
3219:                                                .getCurrentSessionUserId(),
3220:                                        eventId(SECURE_ALL_GROUPS),
3221:                                        m_siteService.siteReference(m_context)) && unlockCheck(
3222:                                        function, getReference()))) {
3223:                            return groups;
3224:                        }
3225:
3226:                        // otherwise, check the groups for function
3227:
3228:                        // get a list of the group refs, which are authzGroup ids
3229:                        Collection groupRefs = new Vector();
3230:                        for (Iterator i = groups.iterator(); i.hasNext();) {
3231:                            Group group = (Group) i.next();
3232:                            groupRefs.add(group.getReference());
3233:                        }
3234:
3235:                        // ask the authzGroup service to filter them down based on function
3236:                        groupRefs = m_authzGroupService
3237:                                .getAuthzGroupsIsAllowed(m_sessionManager
3238:                                        .getCurrentSessionUserId(),
3239:                                        eventId(function), groupRefs);
3240:
3241:                        // pick the Group objects from the site's groups to return, those that are in the groupRefs list
3242:                        for (Iterator i = groups.iterator(); i.hasNext();) {
3243:                            Group group = (Group) i.next();
3244:                            if (groupRefs.contains(group.getReference())) {
3245:                                rv.add(group);
3246:                            }
3247:                        }
3248:                    } catch (IdUnusedException e) {
3249:                    }
3250:
3251:                    return rv;
3252:                }
3253:
3254:                /******************************************************************************************************************************************************************************************************************************************************
3255:                 * SessionBindingListener implementation
3256:                 *****************************************************************************************************************************************************************************************************************************************************/
3257:
3258:                public void valueBound(SessionBindingEvent event) {
3259:                }
3260:
3261:                public void valueUnbound(SessionBindingEvent event) {
3262:                    if (M_log.isDebugEnabled())
3263:                        M_log.debug("valueUnbound()");
3264:
3265:                    // catch the case where an edit was made but never resolved
3266:                    if (m_active) {
3267:                        cancelChannel(this );
3268:                    }
3269:
3270:                } // valueUnbound
3271:
3272:            } // class BaseMessageChannel
3273:
3274:            /**********************************************************************************************************************************************************************************************************************************************************
3275:             * MessageEdit implementation
3276:             *********************************************************************************************************************************************************************************************************************************************************/
3277:
3278:            public class BaseMessageEdit implements  MessageEdit,
3279:                    SessionBindingListener {
3280:                /** The event code for this edit. */
3281:                protected String m_event = null;
3282:
3283:                /** Active flag. */
3284:                protected boolean m_active = false;
3285:
3286:                /** The message header. */
3287:                protected MessageHeaderEdit m_header = null;
3288:
3289:                /** The message body. */
3290:                protected String m_body = null;
3291:
3292:                /** The properties. */
3293:                protected ResourcePropertiesEdit m_properties = null;
3294:
3295:                /** A transient backpointer to the channel */
3296:                protected MessageChannel m_channel = null;
3297:
3298:                /**
3299:                 * Construct.
3300:                 * 
3301:                 * @param id
3302:                 *        The message id.
3303:                 */
3304:                public BaseMessageEdit(MessageChannel channel, String id) {
3305:                    // store the channel
3306:                    m_channel = channel;
3307:
3308:                    // store the id in a new (appropriate typed) header
3309:                    m_header = newMessageHeader(this , id);
3310:
3311:                    // setup for properties
3312:                    m_properties = new BaseResourcePropertiesEdit();
3313:
3314:                } // BaseMessageEdit
3315:
3316:                /**
3317:                 * Construct as a copy of another message.
3318:                 * 
3319:                 * @param other
3320:                 *        The other message to copy.
3321:                 */
3322:                public BaseMessageEdit(MessageChannel channel, Message other) {
3323:                    // store the channel
3324:                    m_channel = channel;
3325:
3326:                    setAll(other);
3327:
3328:                } // BaseMessageEdit
3329:
3330:                /**
3331:                 * Construct from an existing definition, in xml.
3332:                 * 
3333:                 * @param channel
3334:                 *        The channel in which this message lives.
3335:                 * @param el
3336:                 *        The message in XML in a DOM element.
3337:                 */
3338:                public BaseMessageEdit(MessageChannel channel, Element el) {
3339:                    this (channel, "");
3340:
3341:                    m_body = FormattedText.decodeFormattedTextAttribute(el,
3342:                            "body");
3343:
3344:                    // the children (header, body)
3345:                    NodeList children = el.getChildNodes();
3346:                    final int length = children.getLength();
3347:                    for (int i = 0; i < length; i++) {
3348:                        Node child = children.item(i);
3349:                        if (child.getNodeType() == Node.ELEMENT_NODE) {
3350:                            Element element = (Element) child;
3351:
3352:                            // look for a header
3353:                            if (element.getTagName().equals("header")) {
3354:                                // re-create a header
3355:                                m_header = newMessageHeader(this , element);
3356:                            }
3357:
3358:                            // or look for a body (old style of encoding)
3359:                            else if (element.getTagName().equals("body")) {
3360:                                if ((element.getChildNodes() != null)
3361:                                        && (element.getChildNodes().item(0) != null)) {
3362:                                    // convert from plaintext messages to formatted text messages
3363:                                    m_body = element.getChildNodes().item(0)
3364:                                            .getNodeValue();
3365:                                    if (m_body != null)
3366:                                        m_body = FormattedText
3367:                                                .convertPlaintextToFormattedText(m_body);
3368:                                }
3369:                                if (m_body == null) {
3370:                                    m_body = "";
3371:                                }
3372:                            }
3373:
3374:                            // or look for properties
3375:                            else if (element.getTagName().equals("properties")) {
3376:                                // re-create properties
3377:                                m_properties = new BaseResourcePropertiesEdit(
3378:                                        element);
3379:                            }
3380:                        }
3381:                    }
3382:
3383:                } // BaseMessageEdit
3384:
3385:                /**
3386:                 * Take all values from this object.
3387:                 * 
3388:                 * @param user
3389:                 *        The other object to take values from.
3390:                 */
3391:                protected void setAll(Message other) {
3392:                    // copy the header
3393:                    m_header = newMessageHeader(this , other.getHeader());
3394:
3395:                    // body
3396:                    m_body = other.getBody();
3397:
3398:                    // setup for properties
3399:                    m_properties = new BaseResourcePropertiesEdit();
3400:                    m_properties.addAll(other.getProperties());
3401:
3402:                } // setAll
3403:
3404:                /**
3405:                 * Clean up.
3406:                 */
3407:                protected void finalize() {
3408:                    // catch the case where an edit was made but never resolved
3409:                    if ((m_active) && (m_channel != null)) {
3410:                        m_channel.cancelMessage(this );
3411:                    }
3412:
3413:                    m_channel = null;
3414:
3415:                } // finalize
3416:
3417:                /**
3418:                 * Access the message header.
3419:                 * 
3420:                 * @return The message header.
3421:                 */
3422:                public MessageHeader getHeader() {
3423:                    return m_header;
3424:
3425:                } // getHeader
3426:
3427:                /**
3428:                 * Access the id of the resource.
3429:                 * 
3430:                 * @return The id.
3431:                 */
3432:                public String getId() {
3433:                    return m_header.getId();
3434:
3435:                } // getId
3436:
3437:                /**
3438:                 * Access the URL which can be used to access the resource.
3439:                 * 
3440:                 * @return The URL which can be used to access the resource.
3441:                 */
3442:                public String getUrl() {
3443:                    if (m_channel == null)
3444:                        return "";
3445:                    return m_serverConfigurationService.getAccessUrl()
3446:                            + getReference();
3447:
3448:                } // getUrl
3449:
3450:                /**
3451:                 * Access the internal reference which can be used to access the resource from within the system.
3452:                 * 
3453:                 * @return The the internal reference which can be used to access the resource from within the system.
3454:                 */
3455:                public String getReference() {
3456:                    if (m_channel == null)
3457:                        return "";
3458:                    return messageReference(m_channel.getContext(), m_channel
3459:                            .getId(), getId());
3460:
3461:                } // getReference
3462:
3463:                /**
3464:                 * @inheritDoc
3465:                 */
3466:                public String getReference(String rootProperty) {
3467:                    return getReference();
3468:                }
3469:
3470:                /**
3471:                 * @inheritDoc
3472:                 */
3473:                public String getUrl(String rootProperty) {
3474:                    return getUrl();
3475:                }
3476:
3477:                /**
3478:                 * Access the channel's properties.
3479:                 * 
3480:                 * @return The channel's properties.
3481:                 */
3482:                public ResourceProperties getProperties() {
3483:                    return m_properties;
3484:
3485:                } // getProperties
3486:
3487:                /**
3488:                 * Access the body text, as a string.
3489:                 * 
3490:                 * @return The body text, as a string.
3491:                 */
3492:                public String getBody() {
3493:                    return ((m_body == null) ? "" : m_body);
3494:
3495:                } // getBodyText
3496:
3497:                /**
3498:                 * Serialize the resource into XML, adding an element to the doc under the top of the stack element.
3499:                 * 
3500:                 * @param doc
3501:                 *        The DOM doc to contain the XML (or null for a string return).
3502:                 * @param stack
3503:                 *        The DOM elements, the top of which is the containing element of the new "resource" element.
3504:                 * @return The newly added element.
3505:                 */
3506:                public Element toXml(Document doc, Stack stack) {
3507:                    Element message = doc.createElement("message");
3508:
3509:                    if (stack.isEmpty()) {
3510:                        doc.appendChild(message);
3511:                    } else {
3512:                        ((Element) stack.peek()).appendChild(message);
3513:                    }
3514:
3515:                    stack.push(message);
3516:
3517:                    m_header.toXml(doc, stack);
3518:
3519:                    FormattedText.encodeFormattedTextAttribute(message, "body",
3520:                            getBody());
3521:
3522:                    /*
3523:                     * // Note: the old way to set the body - CDATA is too sensitive to the characters within -ggolden Element body = doc.createElement("body"); message.appendChild(body); body.appendChild(doc.createCDATASection(getBody()));
3524:                     */
3525:
3526:                    // properties
3527:                    m_properties.toXml(doc, stack);
3528:
3529:                    stack.pop();
3530:
3531:                    return message;
3532:
3533:                } // toXml
3534:
3535:                /**
3536:                 * Compare this object with the specified object for order.
3537:                 * 
3538:                 * @return A negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.
3539:                 */
3540:                public int compareTo(Object obj) {
3541:                    if (!(obj instanceof  Message))
3542:                        throw new ClassCastException();
3543:
3544:                    // if the object are the same, say so
3545:                    if (obj == this )
3546:                        return 0;
3547:
3548:                    // compare the header's date
3549:                    int compare = getHeader().getDate().compareTo(
3550:                            ((Message) obj).getHeader().getDate());
3551:
3552:                    return compare;
3553:
3554:                } // compareTo
3555:
3556:                /**
3557:                 * Are these objects equal? If they are both Message objects, and they have matching id's, they are.
3558:                 * 
3559:                 * @return true if they are equal, false if not.
3560:                 */
3561:                public boolean equals(Object obj) {
3562:                    if (!(obj instanceof  Message))
3563:                        return false;
3564:                    return ((Message) obj).getId().equals(getId());
3565:
3566:                } // equals
3567:
3568:                /**
3569:                 * Make a hash code that reflects the equals() logic as well. We want two objects, even if different instances, if they have the same id to hash the same.
3570:                 */
3571:                public int hashCode() {
3572:                    return getId().hashCode();
3573:
3574:                } // hashCode
3575:
3576:                /**
3577:                 * Replace the body, as a string.
3578:                 * 
3579:                 * @param body
3580:                 *        The body, as a string.
3581:                 */
3582:                public void setBody(String body) {
3583:                    m_body = body;
3584:
3585:                } // setBody
3586:
3587:                /**
3588:                 * Take all values from this object.
3589:                 * 
3590:                 * @param user
3591:                 *        The other object to take values from.
3592:                 */
3593:                protected void set(Message other) {
3594:                    setAll(other);
3595:
3596:                } // set
3597:
3598:                /**
3599:                 * Access the message header.
3600:                 * 
3601:                 * @return The message header.
3602:                 */
3603:                public MessageHeaderEdit getHeaderEdit() {
3604:                    return m_header;
3605:
3606:                } // getHeaderEdit
3607:
3608:                /**
3609:                 * Access the event code for this edit.
3610:                 * 
3611:                 * @return The event code for this edit.
3612:                 */
3613:                protected String getEvent() {
3614:                    return m_event;
3615:                }
3616:
3617:                /**
3618:                 * Set the event code for this edit.
3619:                 * 
3620:                 * @param event
3621:                 *        The event code for this edit.
3622:                 */
3623:                protected void setEvent(String event) {
3624:                    m_event = event;
3625:                }
3626:
3627:                /**
3628:                 * Access the resource's properties for modification
3629:                 * 
3630:                 * @return The resource's properties.
3631:                 */
3632:                public ResourcePropertiesEdit getPropertiesEdit() {
3633:                    return m_properties;
3634:
3635:                } // getPropertiesEdit
3636:
3637:                /**
3638:                 * Enable editing.
3639:                 */
3640:                public void activate() {
3641:                    m_active = true;
3642:
3643:                } // activate
3644:
3645:                /**
3646:                 * Check to see if the edit is still active, or has already been closed.
3647:                 * 
3648:                 * @return true if the edit is active, false if it's been closed.
3649:                 */
3650:                public boolean isActiveEdit() {
3651:                    return m_active;
3652:
3653:                } // isActiveEdit
3654:
3655:                /**
3656:                 * Close the edit object - it cannot be used after this.
3657:                 */
3658:                protected void closeEdit() {
3659:                    m_active = false;
3660:
3661:                } // closeEdit
3662:
3663:                /******************************************************************************************************************************************************************************************************************************************************
3664:                 * SessionBindingListener implementation
3665:                 *****************************************************************************************************************************************************************************************************************************************************/
3666:
3667:                public void valueBound(SessionBindingEvent event) {
3668:                }
3669:
3670:                public void valueUnbound(SessionBindingEvent event) {
3671:                    if (M_log.isDebugEnabled())
3672:                        M_log.debug("valueUnbound()");
3673:
3674:                    // catch the case where an edit was made but never resolved
3675:                    if ((m_active) && (m_channel != null)) {
3676:                        m_channel.cancelMessage(this );
3677:                    }
3678:
3679:                } // valueUnbound
3680:
3681:            } // BaseMessageEdit
3682:
3683:            /**********************************************************************************************************************************************************************************************************************************************************
3684:             * MessageHeaderEdit implementation
3685:             *********************************************************************************************************************************************************************************************************************************************************/
3686:
3687:            public class BaseMessageHeaderEdit implements  MessageHeaderEdit {
3688:                /** The unique (within the channel) message id. */
3689:                protected String m_id = null;
3690:
3691:                /** The date/time the message was sent to the channel. */
3692:                protected Time m_date = null;
3693:
3694:                /** The User who sent the message to the channel. */
3695:                protected User m_from = null;
3696:
3697:                /** The attachments - dereferencer objects. */
3698:                protected List m_attachments = null;
3699:
3700:                /** The draft status for the message. */
3701:                protected boolean m_draft = false;
3702:
3703:                /** The Collection of groups (authorization group id strings, i.e. group refs). */
3704:                protected Collection m_groups = new Vector();
3705:
3706:                /** The message access. */
3707:                protected MessageAccess m_access = MessageAccess.CHANNEL;
3708:
3709:                /** A transient backpointer to the message. */
3710:                protected Message m_message = null;
3711:
3712:                /**
3713:                 * Construct. Time and From set automatically.
3714:                 * 
3715:                 * @param id
3716:                 *        The message id.
3717:                 */
3718:                public BaseMessageHeaderEdit(Message msg, String id) {
3719:                    m_message = msg;
3720:                    m_id = id;
3721:                    m_date = m_timeService.newTime();
3722:                    try {
3723:                        m_from = m_userDirectoryService
3724:                                .getUser(m_sessionManager
3725:                                        .getCurrentSessionUserId());
3726:                    } catch (UserNotDefinedException e) {
3727:                        m_from = m_userDirectoryService.getAnonymousUser();
3728:                    }
3729:
3730:                    // init the AttachmentContainer
3731:                    m_attachments = m_entityManager.newReferenceList();
3732:
3733:                } // BaseMessageHeaderEdit
3734:
3735:                /**
3736:                 * Construct as a copy of another header.
3737:                 * 
3738:                 * @param other
3739:                 *        The other message header to copy.
3740:                 */
3741:                public BaseMessageHeaderEdit(Message msg, MessageHeader other) {
3742:                    m_message = msg;
3743:                    m_id = other.getId();
3744:                    m_date = m_timeService.newTime(other.getDate().getTime());
3745:                    m_from = other.getFrom();
3746:                    m_draft = other.getDraft();
3747:                    m_access = other.getAccess();
3748:
3749:                    m_attachments = m_entityManager.newReferenceList();
3750:                    replaceAttachments(other.getAttachments());
3751:
3752:                    m_groups = new Vector(other.getGroups());
3753:
3754:                } // BaseMessageHeaderEdit
3755:
3756:                /**
3757:                 * Construct, from an already existing XML DOM element.
3758:                 * 
3759:                 * @param el
3760:                 *        The header in XML in a DOM element.
3761:                 */
3762:                public BaseMessageHeaderEdit(Message msg, Element el) {
3763:                    m_message = msg;
3764:                    m_id = el.getAttribute("id");
3765:                    try {
3766:                        m_from = m_userDirectoryService.getUser(el
3767:                                .getAttribute("from"));
3768:                    } catch (UserNotDefinedException e) {
3769:                        m_from = m_userDirectoryService.getAnonymousUser();
3770:                    }
3771:                    m_date = m_timeService.newTimeGmt(el.getAttribute("date"));
3772:                    try {
3773:                        m_draft = new Boolean(el.getAttribute("draft"))
3774:                                .booleanValue();
3775:                    } catch (Throwable any) {
3776:                    }
3777:
3778:                    // attachments and groups
3779:                    m_attachments = m_entityManager.newReferenceList();
3780:
3781:                    NodeList children = el.getChildNodes();
3782:                    final int length = children.getLength();
3783:                    for (int i = 0; i < length; i++) {
3784:                        Node child = children.item(i);
3785:                        if (child.getNodeType() == Node.ELEMENT_NODE) {
3786:                            Element element = (Element) child;
3787:
3788:                            // look for an attachment
3789:                            if (element.getTagName().equals("attachment")) {
3790:                                m_attachments.add(m_entityManager
3791:                                        .newReference(element
3792:                                                .getAttribute("relative-url")));
3793:                            }
3794:
3795:                            // look for an group
3796:                            else if (element.getTagName().equals("group")) {
3797:                                m_groups
3798:                                        .add(element.getAttribute("authzGroup"));
3799:                            }
3800:                        }
3801:                    }
3802:
3803:                    // extract access
3804:                    MessageAccess access = MessageAccess.fromString(el
3805:                            .getAttribute("access"));
3806:                    if (access != null) {
3807:                        m_access = access;
3808:                    }
3809:
3810:                } // BaseMessageHeaderEdit
3811:
3812:                /**
3813:                 * Access the unique (within the channel) message id.
3814:                 * 
3815:                 * @return The unique (within the channel) message id.
3816:                 */
3817:                public String getId() {
3818:                    return m_id;
3819:
3820:                } // getId
3821:
3822:                /**
3823:                 * Access the date/time the message was sent to the channel.
3824:                 * 
3825:                 * @return The date/time the message was sent to the channel.
3826:                 */
3827:                public Time getDate() {
3828:                    return m_date;
3829:
3830:                } // getDate
3831:
3832:                /**
3833:                 * Access the User who sent the message to the channel.
3834:                 * 
3835:                 * @return The User who sent the message to the channel.
3836:                 */
3837:                public User getFrom() {
3838:                    return m_from;
3839:
3840:                } // getFrom
3841:
3842:                /**
3843:                 * Access the draft status of the message.
3844:                 * 
3845:                 * @return True if the message is a draft, false if not.
3846:                 */
3847:                public boolean getDraft() {
3848:                    return m_draft;
3849:
3850:                } // getDraft
3851:
3852:                /**
3853:                 * Set the draft status of the message.
3854:                 * 
3855:                 * @param draft
3856:                 *        True if the message is a draft, false if not.
3857:                 */
3858:                public void setDraft(boolean draft) {
3859:                    m_draft = draft;
3860:
3861:                } // setDraft
3862:
3863:                /**
3864:                 * @inheritDoc
3865:                 */
3866:                public Collection getGroups() {
3867:                    return new Vector(m_groups);
3868:                }
3869:
3870:                /**
3871:                 * {@inheritDoc}
3872:                 */
3873:                public Collection getGroupObjects() {
3874:                    Vector rv = new Vector();
3875:                    if (m_groups != null) {
3876:                        for (Iterator i = m_groups.iterator(); i.hasNext();) {
3877:                            String groupId = (String) i.next();
3878:                            Group group = m_siteService.findGroup(groupId);
3879:                            if (group != null) {
3880:                                rv.add(group);
3881:                            }
3882:                        }
3883:                    }
3884:
3885:                    return rv;
3886:                }
3887:
3888:                /**
3889:                 * @inheritDoc
3890:                 */
3891:                public MessageAccess getAccess() {
3892:                    return m_access;
3893:                }
3894:
3895:                /**
3896:                 * @inheritDoc
3897:                 */
3898:                public void setGroupAccess(Collection groups)
3899:                        throws PermissionException {
3900:                    // convenience (and what else are we going to do?)
3901:                    if ((groups == null) || (groups.size() == 0)) {
3902:                        clearGroupAccess();
3903:                        return;
3904:                    }
3905:
3906:                    // is there any change?  If we are already grouped, and the group list is the same, ignore the call
3907:                    if ((m_access == MessageAccess.GROUPED)
3908:                            && (EntityCollections.isEqualEntityRefsToEntities(
3909:                                    m_groups, groups)))
3910:                        return;
3911:
3912:                    // there should not be a case where there's no message or a message with no channel... -ggolden
3913:                    if ((m_message == null)
3914:                            || ((BaseMessageEdit) m_message).m_channel == null) {
3915:                        M_log
3916:                                .warn("setGroupAccess() called with null message: "
3917:                                        + m_message.toString()
3918:                                        + " or channel: "
3919:                                        + ((m_message == null) ? ""
3920:                                                : ((BaseMessageEdit) m_message).m_channel
3921:                                                        .toString()));
3922:                        throw new PermissionException(m_sessionManager
3923:                                .getCurrentSessionUserId(), "access:channel",
3924:                                ((m_message == null) ? ""
3925:                                        : ((BaseMessageEdit) m_message)
3926:                                                .getReference()));
3927:                    }
3928:
3929:                    // isolate any groups that would be removed or added
3930:                    Collection addedGroups = new Vector();
3931:                    Collection removedGroups = new Vector();
3932:                    EntityCollections
3933:                            .computeAddedRemovedEntityRefsFromNewEntitiesOldRefs(
3934:                                    addedGroups, removedGroups, groups,
3935:                                    m_groups);
3936:
3937:                    // verify that the user has permission to remove
3938:                    if (removedGroups.size() > 0) {
3939:                        // is this the user's own?
3940:                        boolean own = (getFrom() == null) ? true : getFrom()
3941:                                .getId().equals(
3942:                                        m_sessionManager
3943:                                                .getCurrentSessionUserId());
3944:
3945:                        // the Group objects the user has remove permission
3946:                        Collection allowedGroups = ((BaseMessageEdit) m_message).m_channel
3947:                                .getGroupsAllowRemoveMessage(own);
3948:
3949:                        for (Iterator i = removedGroups.iterator(); i.hasNext();) {
3950:                            String ref = (String) i.next();
3951:
3952:                            // is ref a group the user can remove from?
3953:                            if (!EntityCollections
3954:                                    .entityCollectionContainsRefString(
3955:                                            allowedGroups, ref)) {
3956:                                throw new PermissionException(m_sessionManager
3957:                                        .getCurrentSessionUserId(),
3958:                                        "access:group:remove", ref);
3959:                            }
3960:                        }
3961:                    }
3962:
3963:                    // verify that the user has permission to add in those contexts
3964:                    if (addedGroups.size() > 0) {
3965:                        // the Group objects the user has add permission
3966:                        Collection allowedGroups = ((BaseMessageEdit) m_message).m_channel
3967:                                .getGroupsAllowAddMessage();
3968:
3969:                        for (Iterator i = addedGroups.iterator(); i.hasNext();) {
3970:                            String ref = (String) i.next();
3971:
3972:                            // is ref a group the user can remove from?
3973:                            if (!EntityCollections
3974:                                    .entityCollectionContainsRefString(
3975:                                            allowedGroups, ref)) {
3976:                                throw new PermissionException(m_sessionManager
3977:                                        .getCurrentSessionUserId(),
3978:                                        "access:group:add", ref);
3979:                            }
3980:                        }
3981:                    }
3982:
3983:                    // we are clear to perform this
3984:                    m_access = MessageAccess.GROUPED;
3985:                    EntityCollections.setEntityRefsFromEntities(m_groups,
3986:                            groups);
3987:                }
3988:
3989:                /**
3990:                 * @inheritDoc
3991:                 */
3992:                public void clearGroupAccess() throws PermissionException {
3993:                    // is there any change?  If we are already channel, ignore the call
3994:                    if (m_access == MessageAccess.CHANNEL)
3995:                        return;
3996:
3997:                    // there should not be a case where there's no message or a message with no channel... -ggolden
3998:                    if ((m_message == null)
3999:                            || ((BaseMessageEdit) m_message).m_channel == null) {
4000:                        M_log
4001:                                .warn("clearGroupAccess() called with null message: "
4002:                                        + m_message.toString()
4003:                                        + " or channel: "
4004:                                        + ((m_message == null) ? ""
4005:                                                : ((BaseMessageEdit) m_message).m_channel
4006:                                                        .toString()));
4007:                        throw new PermissionException(m_sessionManager
4008:                                .getCurrentSessionUserId(), "access:channel",
4009:                                ((m_message == null) ? ""
4010:                                        : ((BaseMessageEdit) m_message)
4011:                                                .getReference()));
4012:                    }
4013:
4014:                    // verify that the user has permission to add in the channel context
4015:                    boolean allowed = (m_message != null)
4016:                            && (((BaseMessageEdit) m_message).m_channel)
4017:                                    .allowAddChannelMessage();
4018:                    if (!allowed) {
4019:                        throw new PermissionException(m_sessionManager
4020:                                .getCurrentSessionUserId(), "access:channel",
4021:                                ((BaseMessageEdit) m_message).getReference());
4022:                    }
4023:
4024:                    // we are clear to perform this
4025:                    m_access = MessageAccess.CHANNEL;
4026:                    m_groups.clear();
4027:                }
4028:
4029:                /**
4030:                 * Serialize the resource into XML, adding an element to the doc under the top of the stack element.
4031:                 * 
4032:                 * @param doc
4033:                 *        The DOM doc to contain the XML (or null for a string return).
4034:                 * @param stack
4035:                 *        The DOM elements, the top of which is the containing element of the new "resource" element.
4036:                 * @return The newly added element.
4037:                 */
4038:                public Element toXml(Document doc, Stack stack) {
4039:                    Element header = doc.createElement("header");
4040:                    ((Element) stack.peek()).appendChild(header);
4041:                    header.setAttribute("id", getId());
4042:                    header.setAttribute("from", getFrom().getId());
4043:                    header.setAttribute("date", getDate().toString());
4044:                    if ((m_attachments != null) && (m_attachments.size() > 0)) {
4045:                        for (int i = 0; i < m_attachments.size(); i++) {
4046:                            Reference attch = (Reference) m_attachments.get(i);
4047:                            Element attachment = doc
4048:                                    .createElement("attachment");
4049:                            header.appendChild(attachment);
4050:                            attachment.setAttribute("relative-url", attch
4051:                                    .getReference());
4052:                        }
4053:                    }
4054:
4055:                    // add groups
4056:                    if ((m_groups != null) && (m_groups.size() > 0)) {
4057:                        for (Iterator i = m_groups.iterator(); i.hasNext();) {
4058:                            String group = (String) i.next();
4059:                            Element sect = doc.createElement("group");
4060:                            header.appendChild(sect);
4061:                            sect.setAttribute("authzGroup", group);
4062:                        }
4063:                    }
4064:
4065:                    // add access
4066:                    header.setAttribute("access", m_access.toString());
4067:
4068:                    return header;
4069:
4070:                } // toXml
4071:
4072:                /**
4073:                 * Set the date/time the message was sent to the channel.
4074:                 * 
4075:                 * @param date
4076:                 *        The date/time the message was sent to the channel.
4077:                 */
4078:                public void setDate(Time date) {
4079:                    if (!date.equals(m_date)) {
4080:                        m_date.setTime(date.getTime());
4081:                    }
4082:
4083:                } // setDate
4084:
4085:                /**
4086:                 * Set the User who sent the message to the channel.
4087:                 * 
4088:                 * @param user
4089:                 *        The User who sent the message to the channel.
4090:                 */
4091:                public void setFrom(User user) {
4092:                    if (!user.equals(m_from)) {
4093:                        m_from = user;
4094:                    }
4095:
4096:                } // setFrom
4097:
4098:                /******************************************************************************************************************************************************************************************************************************************************
4099:                 * AttachmentContainer implementation
4100:                 *****************************************************************************************************************************************************************************************************************************************************/
4101:
4102:                /**
4103:                 * Access the attachments of the event.
4104:                 * 
4105:                 * @return An copy of the set of attachments (a ReferenceVector containing Reference objects) (may be empty).
4106:                 */
4107:                public List getAttachments() {
4108:                    return m_entityManager.newReferenceList(m_attachments);
4109:
4110:                } // getAttachments
4111:
4112:                /**
4113:                 * Add an attachment.
4114:                 * 
4115:                 * @param ref
4116:                 *        The attachment Reference.
4117:                 */
4118:                public void addAttachment(Reference ref) {
4119:                    m_attachments.add(ref);
4120:
4121:                } // addAttachment
4122:
4123:                /**
4124:                 * Remove an attachment.
4125:                 * 
4126:                 * @param ref
4127:                 *        The attachment Reference to remove (the one removed will equal this, they need not be ==).
4128:                 */
4129:                public void removeAttachment(Reference ref) {
4130:                    m_attachments.remove(ref);
4131:
4132:                } // removeAttachment
4133:
4134:                /**
4135:                 * Replace the attachment set.
4136:                 * 
4137:                 * @param attachments
4138:                 *        A vector of Reference objects that will become the new set of attachments.
4139:                 */
4140:                public void replaceAttachments(List attachments) {
4141:                    m_attachments.clear();
4142:
4143:                    if (attachments != null) {
4144:                        Iterator it = attachments.iterator();
4145:                        while (it.hasNext()) {
4146:                            m_attachments.add(it.next());
4147:                        }
4148:                    }
4149:
4150:                } // replaceAttachments
4151:
4152:                /**
4153:                 * Clear all attachments.
4154:                 */
4155:                public void clearAttachments() {
4156:                    m_attachments.clear();
4157:
4158:                } // clearAttachments
4159:
4160:            } // BasicMessageHeaderEdit
4161:
4162:            /**********************************************************************************************************************************************************************************************************************************************************
4163:             * Storage implementation
4164:             *********************************************************************************************************************************************************************************************************************************************************/
4165:
4166:            protected interface Storage {
4167:                /**
4168:                 * Open and read.
4169:                 */
4170:                public void open();
4171:
4172:                /**
4173:                 * Write and Close.
4174:                 */
4175:                public void close();
4176:
4177:                /**
4178:                 * Return the identified channel, or null if not found.
4179:                 */
4180:                public MessageChannel getChannel(String ref);
4181:
4182:                /**
4183:                 * Return true if the identified channel exists.
4184:                 */
4185:                public boolean checkChannel(String ref);
4186:
4187:                /**
4188:                 * Get a list of all channels
4189:                 */
4190:                public List getChannels();
4191:
4192:                /**
4193:                 * Keep a new channel.
4194:                 */
4195:                public MessageChannelEdit putChannel(String ref);
4196:
4197:                /**
4198:                 * Get a channel locked for update
4199:                 */
4200:                public MessageChannelEdit editChannel(String ref);
4201:
4202:                /**
4203:                 * Commit a channel edit.
4204:                 */
4205:                public void commitChannel(MessageChannelEdit edit);
4206:
4207:                /**
4208:                 * Cancel a channel edit.
4209:                 */
4210:                public void cancelChannel(MessageChannelEdit edit);
4211:
4212:                /**
4213:                 * Forget about a channel.
4214:                 */
4215:                public void removeChannel(MessageChannelEdit channel);
4216:
4217:                /**
4218:                 * Get a message from a channel.
4219:                 */
4220:                public Message getMessage(MessageChannel channel,
4221:                        String messageId);
4222:
4223:                /**
4224:                 * Get a message from a channel locked for update
4225:                 */
4226:                public MessageEdit editMessage(MessageChannel channel,
4227:                        String messageId);
4228:
4229:                /**
4230:                 * Commit an edit.
4231:                 */
4232:                public void commitMessage(MessageChannel channel,
4233:                        MessageEdit edit);
4234:
4235:                /**
4236:                 * Cancel an edit.
4237:                 */
4238:                public void cancelMessage(MessageChannel channel,
4239:                        MessageEdit edit);
4240:
4241:                /**
4242:                 * Does this messages exist in a channel?
4243:                 */
4244:                public boolean checkMessage(MessageChannel channel,
4245:                        String messageId);
4246:
4247:                /**
4248:                 * Get the messages from a channel
4249:                 */
4250:                public List getMessages(MessageChannel channel);
4251:
4252:                /**
4253:                 * Make and lock a new message.
4254:                 */
4255:                public MessageEdit putMessage(MessageChannel channel, String id);
4256:
4257:                /**
4258:                 * Forget about a message.
4259:                 */
4260:                public void removeMessage(MessageChannel channel,
4261:                        MessageEdit edit);
4262:
4263:                /**
4264:                 * Get messages filtered by date and count and drafts, in descending (latest first) order
4265:                 * 
4266:                 * @param afterDate
4267:                 *        if null, no date limit, else limited to only messages after this date.
4268:                 * @param limitedToLatest
4269:                 *        if 0, no count limit, else limited to only the latest this number of messages.
4270:                 * @param draftsForId
4271:                 *        how to handle drafts: null means no drafts, "*" means all, otherwise drafts only if created by this userId.
4272:                 * @param pubViewOnly
4273:                 *        if true, include only messages marked pubview, else include any.
4274:                 * @return A list of Message objects that meet the criteria; may be empty
4275:                 */
4276:                public List getMessages(MessageChannel channel, Time afterDate,
4277:                        int limitedToLatest, String draftsForId,
4278:                        boolean pubViewOnly);
4279:
4280:                /**
4281:                 * Access a list of channel ids from channels with refs that start with (match) context.
4282:                 * 
4283:                 * @param context
4284:                 *        The root channel ref to match.
4285:                 * @return A List (String) of channel id for channels within the context.
4286:                 */
4287:                public List getChannelIdsMatching(String root);
4288:
4289:            } // Storage
4290:
4291:            /**********************************************************************************************************************************************************************************************************************************************************
4292:             * CacheRefresher implementation (no container)
4293:             *********************************************************************************************************************************************************************************************************************************************************/
4294:
4295:            /**
4296:             * Get a new value for this key whose value has already expired in the cache.
4297:             * 
4298:             * @param key
4299:             *        The key whose value has expired and needs to be refreshed.
4300:             * @param oldValue
4301:             *        The old exipred value of the key.
4302:             * @param event
4303:             *        The event which triggered this refresh.
4304:             * @return a new value for use in the cache for this key; if null, the entry will be removed.
4305:             */
4306:            public Object refresh(Object key, Object oldValue, Event event) {
4307:                Object rv = null;
4308:
4309:                // key is a reference
4310:                Reference ref = m_entityManager.newReference((String) key);
4311:
4312:                // get from storage only (not cache!)
4313:
4314:                // for a message
4315:                if (REF_TYPE_MESSAGE.equals(ref.getSubType())) {
4316:                    if (M_log.isDebugEnabled())
4317:                        M_log.debug("refresh(): key " + key + " channel id : "
4318:                                + ref.getContext() + "/" + ref.getContainer()
4319:                                + " message id : " + ref.getId());
4320:
4321:                    // get channel (Note: from the cache is ok)
4322:                    MessageChannel channel = findChannel(channelReference(ref
4323:                            .getContext(), ref.getContainer()));
4324:
4325:                    // get the message (Note: not from cache! but only from storage)
4326:                    if (channel != null) {
4327:                        rv = m_storage.getMessage(channel, ref.getId());
4328:                    }
4329:                }
4330:
4331:                // for a channel
4332:                else {
4333:                    if (M_log.isDebugEnabled())
4334:                        M_log.debug("refresh(): key " + key + " channel id : "
4335:                                + ref.getReference());
4336:
4337:                    // return the channel from channel getId() (Note: not from cache! but only from storage)
4338:                    rv = m_storage.getChannel(ref.getReference());
4339:                }
4340:
4341:                return rv;
4342:
4343:            } // refresh
4344:
4345:            /**********************************************************************************************************************************************************************************************************************************************************
4346:             * filter
4347:             *********************************************************************************************************************************************************************************************************************************************************/
4348:
4349:            protected class MessagePermissionFilter implements  Filter {
4350:                /**
4351:                 * Does this object satisfy the criteria of the filter?
4352:                 * 
4353:                 * @param o
4354:                 *        The object to test.
4355:                 * @return true if the object is accepted by the filter, false if not.
4356:                 */
4357:                public boolean accept(Object o) {
4358:                    // we want to test only messages
4359:                    if (!(o instanceof  Message)) {
4360:                        return false;
4361:                    }
4362:
4363:                    // if the item cannot be read, reject it
4364:                    if (!unlockCheck(SECURE_READ, ((Message) o).getReference())) {
4365:                        return false;
4366:                    }
4367:
4368:                    // accept this one
4369:                    return true;
4370:
4371:                } // accept
4372:
4373:            } // MessagePermissionFilter
4374:
4375:            protected class MessageSelectionFilter implements  Filter {
4376:                protected Time m_afterDate = null;
4377:
4378:                protected String m_draftsForId = null;
4379:
4380:                protected boolean m_pubViewOnly = false;
4381:
4382:                public MessageSelectionFilter(Time afterDate,
4383:                        String draftsForId, boolean pubViewOnly) {
4384:                    m_afterDate = afterDate;
4385:                    m_draftsForId = draftsForId;
4386:                    m_pubViewOnly = pubViewOnly;
4387:                }
4388:
4389:                /**
4390:                 * Does this object satisfy the criteria of the filter?
4391:                 * 
4392:                 * @param o
4393:                 *        The object to test.
4394:                 * @return true if the object is accepted by the filter, false if not.
4395:                 */
4396:                public boolean accept(Object o) {
4397:                    // we want to test only messages
4398:                    if (!(o instanceof  Message)) {
4399:                        return false;
4400:                    }
4401:
4402:                    if (m_afterDate != null) {
4403:                        if (!((Message) o).getHeader().getDate().after(
4404:                                m_afterDate)) {
4405:                            return false;
4406:                        }
4407:                    }
4408:
4409:                    // if we want pub view only
4410:                    if (m_pubViewOnly) {
4411:                        if (((Entity) o).getProperties().getProperty(
4412:                                ResourceProperties.PROP_PUBVIEW) == null) {
4413:                            return false;
4414:                        }
4415:                    }
4416:
4417:                    // if we don't want all drafts
4418:                    if (!"*".equals(m_draftsForId)) {
4419:                        if (isDraft((Entity) o)) {
4420:                            // reject all drafts?
4421:                            if ((m_draftsForId == null)
4422:                                    || (!getOwnerId((Entity) o).equals(
4423:                                            m_draftsForId))) {
4424:                                return false;
4425:                            }
4426:                        }
4427:                    }
4428:
4429:                    // accept this one
4430:                    return true;
4431:                }
4432:            }
4433:
4434:            protected String getSummaryFromHeader(Message item,
4435:                    MessageHeader header) {
4436:                String newtext;
4437:                String body = item.getBody();
4438:                if (body.length() > 50)
4439:                    body = body.substring(1, 49);
4440:                String newText = body + ", "
4441:                        + header.getFrom().getDisplayName() + ", "
4442:                        + header.getDate().toStringLocalFull();
4443:                return newText;
4444:            }
4445:
4446:            /**********************************************************************************************************************************************************************************************************************************************************
4447:             * getSummary implementation
4448:             *********************************************************************************************************************************************************************************************************************************************************/
4449:            public Map getSummary(String channel, int items, int days)
4450:                    throws org.sakaiproject.exception.IdUsedException,
4451:                    org.sakaiproject.exception.IdInvalidException,
4452:                    org.sakaiproject.exception.PermissionException {
4453:                long startTime = System.currentTimeMillis()
4454:                        - (days * 24l * 60l * 60l * 1000l);
4455:
4456:                List messages = getMessages(channel, m_timeService
4457:                        .newTime(startTime), items, false, false, false);
4458:                Iterator iMsg = messages.iterator();
4459:                Time pubDate = null;
4460:                String summaryText = null;
4461:                Map m = new HashMap();
4462:                while (iMsg.hasNext()) {
4463:                    Message item = (Message) iMsg.next();
4464:                    MessageHeader header = item.getHeader();
4465:                    Time newTime = header.getDate();
4466:                    if (pubDate == null || newTime.before(pubDate))
4467:                        pubDate = newTime;
4468:                    String newText = getSummaryFromHeader(item, header);
4469:                    if (summaryText == null) {
4470:                        summaryText = newText;
4471:                    } else {
4472:                        summaryText = summaryText + "<br>\r\n" + newText;
4473:                    }
4474:                }
4475:                if (pubDate != null) {
4476:                    m.put(Summary.PROP_PUBDATE, pubDate.toStringRFC822Local());
4477:                }
4478:                if (summaryText != null) {
4479:                    m.put(Summary.PROP_DESCRIPTION, summaryText);
4480:                    return m;
4481:                }
4482:                return null;
4483:            }
4484:
4485:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.