001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/chat/tags/sakai_2-4-1/chat-impl/impl/src/java/org/sakaiproject/chat2/model/impl/ChatEntityProducer.java $
003: * $Id: ChatEntityProducer.java 29100 2007-04-18 21:45:18Z ajpoland@iupui.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2007 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/
021:
022: /**
023: *
024: */package org.sakaiproject.chat2.model.impl;
025:
026: import java.io.ByteArrayOutputStream;
027: import java.io.OutputStream;
028: import java.io.OutputStreamWriter;
029: import java.util.Collection;
030: import java.util.Iterator;
031: import java.util.List;
032: import java.util.Map;
033: import java.util.Properties;
034: import java.util.Set;
035: import java.util.Stack;
036:
037: import javax.servlet.http.HttpServletRequest;
038: import javax.servlet.http.HttpServletResponse;
039:
040: import org.apache.commons.logging.Log;
041: import org.apache.commons.logging.LogFactory;
042: import org.sakaiproject.chat2.model.ChatChannel;
043: import org.sakaiproject.chat2.model.ChatManager;
044: import org.sakaiproject.chat2.model.ChatMessage;
045: import org.sakaiproject.component.cover.ServerConfigurationService;
046: import org.sakaiproject.entity.api.Entity;
047: import org.sakaiproject.entity.api.EntityManager;
048: import org.sakaiproject.entity.api.EntityNotDefinedException;
049: import org.sakaiproject.entity.api.EntityPermissionException;
050: import org.sakaiproject.entity.api.EntityProducer;
051: import org.sakaiproject.entity.api.EntityTransferrer;
052: import org.sakaiproject.entity.api.HttpAccess;
053: import org.sakaiproject.entity.api.Reference;
054: import org.sakaiproject.entity.api.ResourceProperties;
055: import org.sakaiproject.exception.IdUnusedException;
056: import org.sakaiproject.exception.PermissionException;
057: import org.sakaiproject.site.api.Site;
058: import org.sakaiproject.site.api.ToolConfiguration;
059: import org.sakaiproject.site.cover.SiteService;
060: import org.sakaiproject.time.cover.TimeService;
061: import org.sakaiproject.user.cover.UserDirectoryService;
062: import org.sakaiproject.util.StringUtil;
063: import org.sakaiproject.util.Web;
064: import org.w3c.dom.DOMException;
065: import org.w3c.dom.Document;
066: import org.w3c.dom.Element;
067: import org.w3c.dom.Node;
068: import org.w3c.dom.NodeList;
069:
070: /**
071: * @author chrismaurer
072: *
073: */
074: public class ChatEntityProducer implements EntityProducer,
075: EntityTransferrer {
076:
077: protected final Log logger = LogFactory.getLog(getClass());
078: private EntityManager entityManager;
079: private ChatManager chatManager;
080:
081: private static final String ARCHIVE_VERSION = "2.4"; // in case new features are added in future exports
082: private static final String VERSION_ATTR = "version";
083: private static final String CHANNEL_PROP = "channel";
084: private static final String SYNOPTIC_TOOL = "synoptic_tool";
085: private static final String NAME = "name";
086: private static final String VALUE = "value";
087:
088: private static final String PROPERTIES = "properties";
089: private static final String PROPERTY = "property";
090:
091: protected void init() throws Exception {
092: logger.info("init()");
093:
094: try {
095: getEntityManager().registerEntityProducer(this ,
096: ChatManager.REFERENCE_ROOT);
097: } catch (Exception e) {
098: logger.warn("Error registering Chat Entity Producer", e);
099: }
100: }
101:
102: /**
103: * Destroy
104: */
105: protected void destroy() {
106: logger.info("destroy()");
107: }
108:
109: /**
110: * {@inheritDoc}
111: */
112: public String[] myToolIds() {
113: String[] toolIds = { ChatManager.CHAT_TOOL_ID };
114: return toolIds;
115: }
116:
117: public ChatMessage getMessage(Reference reference)
118: throws IdUnusedException, PermissionException {
119: return getChatManager().getMessage(reference.getId());
120: //return null;
121: }
122:
123: public ChatChannel getChannel(Reference reference)
124: throws IdUnusedException, PermissionException {
125: return getChatManager().getChatChannel(reference.getId());
126: //return null;
127: }
128:
129: /**
130: * {@inheritDoc}
131: */
132: public String archive(String siteId, Document doc, Stack stack,
133: String archivePath, List attachments) {
134: //prepare the buffer for the results log
135: StringBuffer results = new StringBuffer();
136: int channelCount = 0;
137:
138: try {
139: // start with an element with our very own (service) name
140: Element element = doc.createElement(getChatManager()
141: .serviceName());
142: element.setAttribute(VERSION_ATTR, ARCHIVE_VERSION);
143: ((Element) stack.peek()).appendChild(element);
144: stack.push(element);
145:
146: Element chat = doc.createElement(ChatManager.CHAT);
147: List channelList = getChatManager().getContextChannels(
148: siteId, true);
149: if (channelList != null && !channelList.isEmpty()) {
150: Iterator channelIterator = channelList.iterator();
151: while (channelIterator.hasNext()) {
152: ChatChannel channel = (ChatChannel) channelIterator
153: .next();
154: Element channelElement = channel.toXml(doc, stack);
155: chat.appendChild(channelElement);
156: channelCount++;
157: }
158: results.append("archiving " + getLabel() + ": ("
159: + channelCount
160: + ") channels archived successfully.\n");
161:
162: } else {
163: results.append("archiving " + getLabel()
164: + ": empty chat room archived.\n");
165: }
166:
167: // archive the chat synoptic tool options
168: archiveSynopticOptions(siteId, doc, chat);
169:
170: ((Element) stack.peek()).appendChild(chat);
171: stack.push(chat);
172:
173: stack.pop();
174: } catch (Exception any) {
175: logger.warn("archive: exception archiving service: "
176: + getChatManager().serviceName());
177: }
178:
179: stack.pop();
180:
181: return results.toString();
182: }
183:
184: /**
185: * try to add synoptic options for this tool to the archive, if they exist
186: * @param siteId
187: * @param doc
188: * @param element
189: */
190: public void archiveSynopticOptions(String siteId, Document doc,
191: Element element) {
192: try {
193: // archive the synoptic tool options
194: Site site = SiteService.getSite(siteId);
195: ToolConfiguration synTool = site
196: .getToolForCommonId("sakai.synoptic." + getLabel());
197: Properties synProp = synTool.getPlacementConfig();
198: if (synProp != null && synProp.size() > 0) {
199: Element synElement = doc.createElement(SYNOPTIC_TOOL);
200: Element synProps = doc.createElement(PROPERTIES);
201:
202: Set synPropSet = synProp.keySet();
203: Iterator propIter = synPropSet.iterator();
204: while (propIter.hasNext()) {
205: String propName = (String) propIter.next();
206: Element synPropEl = doc.createElement(PROPERTY);
207: synPropEl.setAttribute(NAME, propName);
208: synPropEl.setAttribute(VALUE, synProp
209: .getProperty(propName));
210: synProps.appendChild(synPropEl);
211: }
212:
213: synElement.appendChild(synProps);
214: element.appendChild(synElement);
215: }
216: } catch (Exception e) {
217: logger
218: .warn("archive: exception archiving synoptic options for service: "
219: + getChatManager().serviceName());
220: }
221: }
222:
223: /**
224: * {@inheritDoc}
225: */
226: public Entity getEntity(Reference ref) {
227: // 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
228:
229: Entity rv = null;
230:
231: try {
232: // if this is a channel
233: if (ChatManager.REF_TYPE_CHANNEL.equals(ref.getSubType())) {
234: rv = getChatManager()
235: .getChatChannel(ref.getReference());
236: }
237:
238: // otherwise a message
239: else if (ChatManager.REF_TYPE_MESSAGE.equals(ref
240: .getSubType())) {
241: rv = getMessage(ref);
242: }
243:
244: // else try {throw new Exception();} catch (Exception e) {M_log.warn("getResource(): unknown message ref subtype: " + m_subType + " in ref: " + m_reference, e);}
245: else
246: logger
247: .warn("getEntity(): unknown message ref subtype: "
248: + ref.getSubType()
249: + " in ref: "
250: + ref.getReference());
251: } catch (NullPointerException e) {
252: logger.warn("getEntity(): " + e);
253: } catch (IdUnusedException e) {
254: logger.warn("getEntity(): " + e);
255: } catch (PermissionException e) {
256: logger.warn("getEntity(): " + e);
257: }
258:
259: return rv;
260: }
261:
262: /**
263: * {@inheritDoc}
264: */
265: public Collection getEntityAuthzGroups(Reference ref, String userId) {
266: //TODO implement this
267: return null;
268: }
269:
270: /**
271: * {@inheritDoc}
272: */
273: public String getEntityDescription(Reference ref) {
274: // 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
275:
276: String rv = "Message: " + ref.getReference();
277:
278: try {
279: // if this is a channel
280: if (ChatManager.REF_TYPE_CHANNEL.equals(ref.getSubType())) {
281: ChatChannel channel = getChannel(ref);
282: rv = "Channel: " + channel.getId() + " ("
283: + channel.getContext() + ")";
284: }
285: } catch (PermissionException e) {
286: } catch (IdUnusedException e) {
287: } catch (NullPointerException e) {
288: }
289:
290: return rv;
291: }
292:
293: /* (non-Javadoc)
294: * @see org.sakaiproject.entity.api.EntityProducer#getEntityResourceProperties(org.sakaiproject.entity.api.Reference)
295: */
296: public ResourceProperties getEntityResourceProperties(Reference ref) {
297: // TODO Auto-generated method stub
298: return null;
299: }
300:
301: /**
302: * {@inheritDoc}
303: */
304: public String getEntityUrl(Reference ref) {
305: // 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
306:
307: String url = null;
308:
309: try {
310: // if this is a channel
311: if (ChatManager.REF_TYPE_CHANNEL.equals(ref.getSubType())) {
312: ChatChannel channel = getChannel(ref);
313: url = channel.getUrl();
314: }
315:
316: // otherwise a message
317: else if (ChatManager.REF_TYPE_MESSAGE.equals(ref
318: .getSubType())) {
319: ChatMessage message = getMessage(ref);
320: url = message.getUrl();
321: }
322:
323: else
324: logger.warn("getUrl(): unknown message ref subtype: "
325: + ref.getSubType() + " in ref: "
326: + ref.getReference());
327: } catch (PermissionException e) {
328: logger.warn("getUrl(): " + e);
329: } catch (IdUnusedException e) {
330: logger.warn("getUrl(): " + e);
331: } catch (NullPointerException e) {
332: logger.warn("getUrl(): " + e);
333: }
334:
335: return url;
336: }
337:
338: /**
339: * {@inheritDoc}
340: */
341: public HttpAccess getHttpAccess() {
342: return new HttpAccess() {
343:
344: public void handleAccess(HttpServletRequest req,
345: HttpServletResponse res, Reference ref,
346: Collection copyrightAcceptedRefs)
347: throws EntityPermissionException,
348: EntityNotDefinedException {
349: try {
350: //TODO: Isn't there a better way to do this than build out the whole page here??
351:
352: // We need to write to a temporary stream for better speed, plus
353: // so we can get a byte count. Internet Explorer has problems
354: // if we don't make the setContentLength() call.
355: ByteArrayOutputStream outByteStream = new ByteArrayOutputStream();
356: OutputStreamWriter sw = new OutputStreamWriter(
357: outByteStream);
358:
359: String skin = ServerConfigurationService
360: .getString("skin.default");
361: String skinRepo = ServerConfigurationService
362: .getString("skin.repo");
363:
364: ChatMessage message = getMessage(ref);
365: String title = ref.getDescription();
366: //MessageHeader messageHead = message.getHeader();
367: //String date = messageHead.getDate().toStringLocalFullZ();
368: String date = TimeService.newTime(
369: message.getMessageDate().getTime())
370: .toStringLocalFullZ();
371: String from = UserDirectoryService.getUser(
372: message.getOwner()).getDisplayName();
373: //String from = messageHead.getFrom().getDisplayName();
374: String groups = "";
375: //Collection gr = messageHead.getGroups();
376: //for (Iterator i = gr.iterator(); i.hasNext();)
377: //{
378: // groups += "<li>" + i.next() + "</li>";
379: //}
380: String body = Web.escapeHtml(message.getBody());
381:
382: sw
383: .write("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
384: + "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" xml:lang=\"en\">\n"
385: + "<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"
386: + "<link href=\"");
387: sw.write(skinRepo);
388: sw
389: .write("/tool_base.css\" type=\"text/css\" rel=\"stylesheet\" media=\"all\" />\n"
390: + "<link href=\"");
391: sw.write(skinRepo);
392: sw.write("/");
393: sw.write(skin);
394: sw
395: .write("/tool.css\" type=\"text/css\" rel=\"stylesheet\" media=\"all\" />\n"
396: + "<meta http-equiv=\"Content-Style-Type\" content=\"text/css\" />\n"
397: + "<title>");
398: sw.write(title);
399: sw
400: .write("</title></head><body><div class=\"portletBody\">\n"
401: + "<h2>");
402: sw.write(title);
403: sw.write("</h2><ul><li>Date ");
404: sw.write(date);
405: sw.write("</li>");
406: sw.write("<li>From ");
407: sw.write(from);
408: sw.write("</li>");
409: sw.write(groups);
410: sw.write("<ul><p>");
411: sw.write(body);
412: sw.write("</p></div></body></html> ");
413:
414: sw.flush();
415: res.setContentType("text/html");
416: res.setContentLength(outByteStream.size());
417:
418: if (outByteStream.size() > 0) {
419: // Increase the buffer size for more speed.
420: res.setBufferSize(outByteStream.size());
421: }
422:
423: OutputStream out = null;
424: try {
425: out = res.getOutputStream();
426: if (outByteStream.size() > 0) {
427: outByteStream.writeTo(out);
428: }
429: out.flush();
430: out.close();
431: } catch (Throwable ignore) {
432: } finally {
433: if (out != null) {
434: try {
435: out.close();
436: } catch (Throwable ignore) {
437: }
438: }
439: }
440: } catch (PermissionException e) {
441: throw new EntityPermissionException(e.getUser(), e
442: .getLocalizedMessage(), e.getResource());
443: } catch (IdUnusedException e) {
444: throw new EntityNotDefinedException(e.getId());
445: } catch (Throwable t) {
446: throw new RuntimeException(
447: "Faied to find message ", t);
448: }
449: }
450: };
451: }
452:
453: /**
454: * {@inheritDoc}
455: */
456: public String getLabel() {
457: return getChatManager().getLabel();
458: }
459:
460: /**
461: * {@inheritDoc}
462: */
463: public String merge(String siteId, Element root,
464: String archivePath, String fromSiteId, Map attachmentNames,
465: Map userIdTrans, Set userListAllowImport) {
466: logger.debug("trying to merge chat");
467:
468: // buffer for the results log
469: StringBuffer results = new StringBuffer();
470:
471: int count = 0;
472:
473: if (siteId != null && siteId.trim().length() > 0) {
474: try {
475: NodeList allChildrenNodes = root.getChildNodes();
476: int length = allChildrenNodes.getLength();
477: for (int i = 0; i < length; i++) {
478: count++;
479: Node siteNode = allChildrenNodes.item(i);
480: if (siteNode.getNodeType() == Node.ELEMENT_NODE) {
481: Element chatElement = (Element) siteNode;
482: if (chatElement.getTagName().equals(
483: ChatManager.CHAT)) {
484: Site site = SiteService.getSite(siteId);
485: if (site
486: .getToolForCommonId(ChatManager.CHAT_TOOL_ID) != null) {
487:
488: // add the chat rooms and synoptic tool options
489: NodeList chatNodes = chatElement
490: .getChildNodes();
491: int lengthChatNodes = chatNodes
492: .getLength();
493: for (int cn = 0; cn < lengthChatNodes; cn++) {
494: Node chatNode = chatNodes.item(cn);
495: if (chatNode.getNodeType() == Node.ELEMENT_NODE) {
496: Element channelElement = (Element) chatNode;
497: if (channelElement.getTagName()
498: .equals(CHANNEL_PROP)) {
499: ChatChannel channel = ChatChannel
500: .xmlToChatChannel(
501: channelElement,
502: siteId);
503: //save the channel
504: getChatManager()
505: .updateChannel(
506: channel,
507: false);
508: }
509:
510: else if (channelElement
511: .getTagName().equals(
512: SYNOPTIC_TOOL)) {
513: ToolConfiguration synTool = site
514: .getToolForCommonId("sakai.synoptic.chat");
515: Properties synProps = synTool
516: .getPlacementConfig();
517:
518: NodeList synPropNodes = channelElement
519: .getChildNodes();
520: for (int props = 0; props < synPropNodes
521: .getLength(); props++) {
522: Node propsNode = synPropNodes
523: .item(props);
524: if (propsNode
525: .getNodeType() == Node.ELEMENT_NODE) {
526: Element synPropEl = (Element) propsNode;
527: if (synPropEl
528: .getTagName()
529: .equals(
530: PROPERTIES)) {
531: NodeList synProperties = synPropEl
532: .getChildNodes();
533: for (int p = 0; p < synProperties
534: .getLength(); p++) {
535: Node propertyNode = synProperties
536: .item(p);
537: if (propertyNode
538: .getNodeType() == Node.ELEMENT_NODE) {
539: Element propEl = (Element) propertyNode;
540: if (propEl
541: .getTagName()
542: .equals(
543: PROPERTY)) {
544: String propName = propEl
545: .getAttribute(NAME);
546: String propValue = propEl
547: .getAttribute(VALUE);
548:
549: if (propName != null
550: && propName
551: .length() > 0
552: && propValue != null
553: && propValue
554: .length() > 0) {
555: synProps
556: .setProperty(
557: propName,
558: propValue);
559: }
560: }
561: }
562: }
563: }
564: }
565: }
566: }
567: }
568: }
569: SiteService.save(site);
570: }
571: }
572: }
573: }
574:
575: results.append("merging chat " + siteId + " (" + count
576: + ") chat items.\n");
577: } catch (DOMException e) {
578: logger.error(e.getMessage(), e);
579: results.append("merging " + getLabel()
580: + " failed during xml parsing.\n");
581: } catch (Exception e) {
582: logger.error(e.getMessage(), e);
583: results.append("merging " + getLabel() + " failed.\n");
584: }
585: }
586:
587: return results.toString();
588:
589: } // merge
590:
591: /**
592: * {@inheritDoc}
593: */
594: public boolean parseEntityReference(String reference, Reference ref) {
595: if (reference.startsWith(ChatManager.REFERENCE_ROOT)) {
596: String[] parts = StringUtil.split(reference,
597: Entity.SEPARATOR);
598:
599: String id = null;
600: String subType = null;
601: String context = null;
602: String container = null;
603:
604: // the first part will be null, then next the service, the third will be "msg" or "channel"
605: if (parts.length > 2) {
606: subType = parts[2];
607: if (ChatManager.REF_TYPE_CHANNEL.equals(subType)) {
608: // next is the context id
609: if (parts.length > 3) {
610: context = parts[3];
611:
612: // next is the channel id
613: if (parts.length > 4) {
614: id = parts[4];
615: }
616: }
617: } else if (ChatManager.REF_TYPE_MESSAGE.equals(subType)) {
618: // next three parts are context, channel (container) and mesage id
619: if (parts.length > 5) {
620: context = parts[3];
621: container = parts[4];
622: id = parts[5];
623: }
624: } else
625: logger.warn("parse(): unknown message subtype: "
626: + subType + " in ref: " + reference);
627: }
628:
629: ref.set(ChatManager.APPLICATION_ID, subType, id, container,
630: context);
631:
632: return true;
633: }
634:
635: return false;
636: }
637:
638: /**
639: * {@inheritDoc}
640: */
641: public boolean willArchiveMerge() {
642: return true;
643: }
644:
645: /**
646: * {@inheritDoc}
647: */
648: public void transferCopyEntities(String fromContext,
649: String toContext, List ids) {
650: try {
651: // retrieve all of the chat rooms
652: List channels = getChatManager().getContextChannels(
653: fromContext, true);
654: if (channels != null && !channels.isEmpty()) {
655: Iterator channelIterator = channels.iterator();
656: while (channelIterator.hasNext()) {
657: ChatChannel oldChannel = (ChatChannel) channelIterator
658: .next();
659: ChatChannel newChannel = getChatManager()
660: .createNewChannel(toContext,
661: oldChannel.getTitle(), false, false);
662: newChannel.setDescription(oldChannel
663: .getDescription());
664: newChannel
665: .setFilterType(oldChannel.getFilterType());
666: newChannel.setFilterParam(oldChannel
667: .getFilterParam());
668: newChannel.setContextDefaultChannel(oldChannel
669: .isContextDefaultChannel());
670: try {
671: getChatManager().updateChannel(newChannel,
672: false);
673: } catch (Exception e) {
674: logger
675: .warn("Exception while creating channel: "
676: + newChannel.getTitle()
677: + ": "
678: + e);
679: }
680:
681: }
682: }
683:
684: transferSynopticOptions(fromContext, toContext);
685: }
686:
687: catch (Exception any) {
688: logger.warn(
689: ".transferCopyEntities(): exception in handling "
690: + getChatManager().serviceName() + " : ",
691: any);
692: }
693: }
694:
695: /**
696: * Import the synoptic tool options from another site
697: *
698: * @param fromContext
699: * @param toContext
700: */
701: protected void transferSynopticOptions(String fromContext,
702: String toContext) {
703: try {
704: // transfer the synoptic tool options
705: Site fromSite = SiteService.getSite(fromContext);
706: ToolConfiguration fromSynTool = fromSite
707: .getToolForCommonId("sakai.synoptic." + getLabel());
708: Properties fromSynProp = fromSynTool.getPlacementConfig();
709:
710: Site toSite = SiteService.getSite(toContext);
711: ToolConfiguration toSynTool = toSite
712: .getToolForCommonId("sakai.synoptic." + getLabel());
713: Properties toSynProp = toSynTool.getPlacementConfig();
714:
715: if (fromSynProp != null && !fromSynProp.isEmpty()) {
716: Set synPropSet = fromSynProp.keySet();
717: Iterator propIter = synPropSet.iterator();
718: while (propIter.hasNext()) {
719: String propName = ((String) propIter.next());
720: String propValue = fromSynProp
721: .getProperty(propName);
722: if (propValue != null && propValue.length() > 0) {
723: toSynProp.setProperty(propName, propValue);
724: }
725: }
726:
727: SiteService.save(toSite);
728: }
729: } catch (PermissionException pe) {
730: logger.warn(
731: "PermissionException transferring synoptic options for "
732: + getChatManager().serviceName() + ':', pe);
733: } catch (IdUnusedException e) {
734: logger
735: .warn("Channel " + fromContext
736: + " cannot be found. ");
737: } catch (Exception e) {
738: logger
739: .warn(
740: "transferSynopticOptions(): exception in handling "
741: + getChatManager().serviceName()
742: + " : ", e);
743: }
744: }
745:
746: public EntityManager getEntityManager() {
747: return entityManager;
748: }
749:
750: public void setEntityManager(EntityManager entityManager) {
751: this .entityManager = entityManager;
752: }
753:
754: public ChatManager getChatManager() {
755: return chatManager;
756: }
757:
758: public void setChatManager(ChatManager chatManager) {
759: this.chatManager = chatManager;
760: }
761:
762: }
|