001: package com.sun.portal.providers.notes;
002:
003: import java.net.URL;
004: import java.net.URLEncoder;
005:
006: import java.util.Map;
007: import java.util.Date;
008: import java.util.List;
009: import java.util.Locale;
010: import java.util.Iterator;
011: import java.util.TimeZone;
012: import java.util.ArrayList;
013: import java.util.ResourceBundle;
014: import java.util.StringTokenizer;
015: import java.util.NoSuchElementException;
016: import java.util.logging.*;
017: import java.nio.channels.FileChannel;
018: import java.nio.channels.FileLock;
019:
020: import java.io.File;
021: import java.io.IOException;
022: import java.io.PrintWriter;
023: import java.io.BufferedReader;
024: import java.io.BufferedWriter;
025: import java.io.FileInputStream;
026: import java.io.FileOutputStream;
027: import java.io.InputStreamReader;
028: import java.io.OutputStreamWriter;
029: import java.io.FileNotFoundException;
030: import java.io.UnsupportedEncodingException;
031:
032: import java.lang.Thread.*;
033: import java.util.Hashtable;
034: import java.util.ArrayList;
035:
036: import java.text.DateFormat;
037: import java.text.MessageFormat;
038:
039: import javax.servlet.http.HttpServletRequest;
040: import javax.servlet.http.HttpServletResponse;
041:
042: import com.sun.portal.desktop.util.I18n;
043: import com.sun.portal.desktop.util.Integers;
044:
045: import com.sun.portal.providers.ProviderException;
046: import com.sun.portal.providers.ProfileProviderAdapter;
047: import com.sun.portal.providers.InvalidEditFormDataException;
048: import com.sun.portal.providers.context.ProviderContextException;
049: import com.sun.portal.providers.context.ProviderContext;
050:
051: import com.sun.portal.desktop.context.ContextException;
052: import com.sun.portal.log.common.PortalLogger;
053:
054: public class NotesProvider extends ProfileProviderAdapter {
055: private File motdFile = null;
056: private String editContainer = null;
057: private String container = null;
058: private String fileLockingLogger = "com.org.portal.locklogger";
059: private static Logger logger = PortalLogger
060: .getLogger(NotesProvider.class);
061:
062: // Schema attribute names for user's first & last name in dsame
063: // Full name is used to log the notes
064: private static final String COMMON_NAME = "cn";
065: private ResourceBundle bundle = null;
066:
067: public void init(String n, HttpServletRequest req)
068: throws ProviderException {
069: super .init(n, req);
070: }
071:
072: public NotesProvider() {
073: }
074:
075: /*protected File getLock() throws ProviderException {
076:
077: // Lock the notes file using external file lock to handle multi-jvm synch
078: int timeout = 100;
079: File lockFile = new File(getStringProperty("location")+".lock");
080: //This solution will work when two threads wait for same instance of notesProvider
081: //When two threads hold lock on different instances of notesProvider , this will not work as
082: // notifyAll wakes up all threads that are waiting on this object's monitor
083: //Use of static class variable can be an option
084: //Above problem is solved for multi jvm , by FileLock
085:
086: try {
087: while (lockFile.createNewFile() == false) {
088: synchronized (this){
089: this.wait(timeout);
090: }
091: }
092: } catch (IOException ioe) {
093: // Under heavy load, this is logging numerous error messages in the log file, which creates
094: // a performance problem. Hence the error messages are not logged any more
095: // and a null will be returned.
096: return null;
097:
098: } catch (InterruptedException ie) {
099: getProviderContext().debugError("NotesProvider.getContent()", ie);
100: throw new ProviderException(" Error while waiting to read the notes file");
101: }
102:
103: // Got the lock
104:
105: // Set delete on exit
106: lockFile.deleteOnExit();
107: return lockFile;
108: }*/
109: protected FileLock getLock(String filename, File lockFile)
110: throws ProviderException {
111: //For locking notes file, one can use external file lock to handle multi-jvm synch but that is
112: // not reliable solution.So, nio fileChannel.lock() is used but that takes care of only multi-jvm
113: // synch and fails when the notes file need to be synchronised in case of multiple user requests
114: // to read/ write the notes. So, to resolve this , one can use static conditional flags but that
115: // does not take care of requests coming from multi portal instances on the same server instance.
116: // So, to cover all the conditions other than multi-jvm sync, Loggers are used which are objects
117: // which can be shared by all applications within the single jvm.
118: int timeout = 100;
119: FileLock filelock = null;
120: try {
121: FileOutputStream fo = new FileOutputStream(lockFile);
122: FileChannel fc = fo.getChannel();
123: filelock = fc.lock(); //for multi jvm synch
124: if (filelock != null) { //for multi web app sync + multi user request for single web app
125: while (true) {
126: while (checkForMyHandler(filename)) {
127: Thread.sleep(timeout);
128: }
129: synchronized (Logger.getLogger(fileLockingLogger)) {
130: if (addMyHandler(filename))
131: break;
132: }
133: }
134: }
135: } catch (IOException ioe) {
136: if (logger.isLoggable(Level.FINE)) {
137: LogRecord record = new LogRecord(Level.FINE,
138: "PSDT_CSPPN0001");
139: record.setLoggerName(logger.getName());
140: record.setParameters(new Object[] { filename });
141: record.setThrown(ioe);
142: logger.log(record);
143: }
144: //do cleanup
145: Release(filelock, filename, lockFile);
146: synchronized (Logger.getLogger(fileLockingLogger)) {
147: removeMyHandler(filename);
148: }
149: throw new ProviderException(ioe.getMessage());
150:
151: } catch (InterruptedException ie) {
152: if (logger.isLoggable(Level.FINEST)) {
153: LogRecord record = new LogRecord(Level.FINEST,
154: "PSDT_CSPPN0001");
155: record.setLoggerName(logger.getName());
156: record.setParameters(new Object[] { filename });
157: record.setThrown(ie);
158: logger.log(record);
159: }
160: //do cleanup
161: Release(filelock, filename, lockFile);
162: throw new ProviderException(
163: " Error while waiting to read the notes file");
164: }
165: return filelock;
166:
167: }
168:
169: public boolean isPresentable(HttpServletRequest request) {
170: return true;
171: }
172:
173: public StringBuffer getContent(HttpServletRequest req,
174: HttpServletResponse res) throws ProviderException {
175: //To handle the condition when we have some data in correct format
176: //and some in incorrect format as per BugId: 5018671
177: //So the idea is to capture all the error conditions in an arraylist and not to throw the ProviderException
178: //if we have even one line with correct data format, so that that line will be displayed rather than the error
179: //errList is an ArrayList which will hold all the errors and can be used in debugging.
180: //isFormatCorrect is a boolean which will be set true once we get one line with correct data format.
181: ArrayList errList = new ArrayList();
182: boolean isFormatCorrect = false;
183: StringBuffer content = new StringBuffer();
184: ProviderContext pc = getProviderContext();
185: String filename = getStringProperty("location");
186: File lockFile = new File(filename + ".lock");
187: motdFile = new File(filename);
188:
189: try {
190: motdFile.createNewFile();
191: } catch (IOException ioe) {
192: // Under heavy load, this is logging numerous error messages in the log file, which creates
193: // a performance problem. Hence the error messages are not logged any more
194: // and a null will be returned.
195: return null;
196: }
197:
198: if (motdFile.exists() && motdFile.length() == 0) {
199: logger.log(Level.INFO, "PSDT_CSPPN0007");
200: return getTemplate("noContent.template", new Hashtable());
201: }
202:
203: String readLines = getStringProperty("lines");
204: int lines = Integer.parseInt(readLines);
205:
206: Locale locale = null;
207: locale = pc.getLocale();
208:
209: String timezone = getStringProperty("timezone");
210:
211: TimeZone tz = TimeZone.getTimeZone(timezone);
212: // Get Resource Bundle if it is null
213: if (bundle == null) {
214: bundle = getResourceBundle();
215: }
216:
217: // Lock the notes file using nio channel locks and logger api.
218: FileLock filelock = getLock(filename, lockFile);
219: if (filelock != null) {
220: BufferedReader in = null;
221: try {
222: in = new BufferedReader(new InputStreamReader(
223: new FileInputStream(motdFile),
224: I18n.DEFAULT_CHARSET));
225: } catch (UnsupportedEncodingException uec) {
226: logger.log(Level.FINE, "PSDT_CSPPN0002", uec);
227: //do cleanup
228: Release(filelock, filename, lockFile);
229: throw new ProviderException(
230: "error creating input stream", uec);
231: } catch (FileNotFoundException fnfe) {
232: logger.log(Level.FINE, "PSDT_CSPPN0002", fnfe);
233: //do cleanup
234: Release(filelock, filename, lockFile);
235: throw new ProviderException(
236: "error creating input stream", fnfe);
237: }
238:
239: Hashtable tagTable = new Hashtable();
240: tagTable.put("fontface", getStringProperty("fontFace1"));
241:
242: StringBuffer linebreak = getTemplate("linebreak.template");
243: StringBuffer msgSuffix = getTemplate("msgSuffix.template");
244:
245: int nonEmptyLines = 0;
246: try {
247: MessageFormat mf = new MessageFormat("");
248:
249: String textColor = bundle.getString("textColor");
250: String darkColor = bundle.getString("darkColor");
251: String lightColor = bundle.getString("lightColor");
252:
253: String line = null;
254: for (int i = 0; i < lines; i++) {
255: line = in.readLine();
256: if (line != null) {
257: nonEmptyLines++;
258:
259: String bgColor = (i % 2 == 0) ? lightColor
260: : darkColor;
261:
262: StringTokenizer tok = new StringTokenizer(line,
263: "|");
264:
265: String dateMessage = bundle
266: .getString("dateMessage");
267: mf.setLocale(locale);
268: mf.applyPattern(dateMessage);
269: ((DateFormat) mf.getFormats()[0])
270: .setTimeZone(tz);
271:
272: String user = null;
273: String date = null;
274: String mesg = null;
275:
276: tagTable.put("textcolor", textColor);
277:
278: try {
279: user = (String) tok.nextElement();
280:
281: String time = (String) tok.nextElement();
282:
283: // convert time since epoch to locale-specific date
284: long ms = new Long(time).longValue();
285: Date d = new Date(ms);
286:
287: // don't use I18n class formatting methods here since
288: // they re-create the mf class each time.
289:
290: date = mf.format(new Object[] { d },
291: new StringBuffer(128), null)
292: .toString();
293:
294: mesg = (String) tok.nextElement();
295: } catch (NoSuchElementException nse) {
296: logger.log(Level.FINER, "PSDT_CSPPN0003",
297: nse);
298: }
299:
300: String escapedUser = null;
301: String escapedDate = null;
302: String escapedMesg = null;
303: boolean canProceed = true;
304: try {
305: escapedUser = pc.escape(user);
306: escapedDate = pc.escape(date);
307: escapedMesg = pc.escape(mesg);
308: isFormatCorrect = true;
309: } catch (ProviderContextException e) {
310: //add an instance of error in error vector and line number
311: errList.add(new String(
312: "Data in incorrect format in line "
313: + i));
314: canProceed = false;
315: } catch (Exception e) {
316: errList.add(new String(
317: "Data in incorrect format in line "
318: + i));
319: canProceed = false;
320: }
321: if (canProceed) {
322: StringBuffer separator = getTemplate(
323: "separator.template", tagTable);
324:
325: tagTable.put("rowcolor", bgColor);
326: StringBuffer msgPrefix = getTemplate(
327: "msgPrefix.template", tagTable);
328: tagTable.remove("rowcolor");
329:
330: StringBuffer row = new StringBuffer()
331: .append(msgPrefix).append(
332: escapedUser).append(
333: separator).append(
334: escapedDate).append(
335: separator).append(
336: escapedMesg).append(
337: msgSuffix);
338:
339: if (i < lines - 1)
340: row.append(linebreak);
341:
342: content.append(row);
343: }
344: }
345: }
346: //if all the lines in the notes are in incorrect format then throw the ProviderException
347: if (!isFormatCorrect) {
348: logger.log(Level.FINEST, "PSDT_CSPPN0004");
349: throw new ProviderException(
350: "NotesProvider.getContent(): Error escaping data");
351: }
352:
353: } catch (IOException ioe) {
354: logger.log(Level.FINEST, "PSDT_CSPPN0005", ioe);
355: // Under heavy load, this is logging numerous error messages in the log file, which creates
356: // a performance problem. Hence the error messages are not logged any more
357: // and a null will be returned.
358: //do cleanup
359: Release(filelock, filename, lockFile);
360: return null;
361:
362: } finally {
363: if (in != null) {
364: try {
365: in.close();
366: } catch (IOException ioe) {
367: // Under heavy load, this is logging numerous error messages in the log file, which creates
368: // a performance problem. Hence the error messages are not logged any more
369: // and a null will be returned.
370:
371: }
372: }
373: //do cleanup
374: Release(filelock, filename, lockFile);
375: }
376:
377: tagTable.clear();
378:
379: if (nonEmptyLines > 0) {
380: tagTable.put("note", content.toString());
381: tagTable.put("count", Integer.toString(nonEmptyLines));
382: content = getTemplate("content.template", tagTable);
383: } else {
384: content = getTemplate("noContent.template",
385: new Hashtable());
386: }
387: }
388: return content;
389: }
390:
391: public StringBuffer getEdit(HttpServletRequest req,
392: HttpServletResponse res) throws ProviderException {
393:
394: StringBuffer content = new StringBuffer();
395: editContainer = req.getParameter("provider");
396: container = req.getParameter("containerName");
397:
398: // Get Resource Bundle if it is null
399: if (bundle == null) {
400: bundle = getResourceBundle();
401: }
402:
403: String newMessage = bundle.getString("newMessage");
404: String lines = "";
405: String message = "";
406: if (req.getParameter("error") != null) {
407: if (req.getParameter("lines") != null) {
408: lines = req.getParameter("lines");
409: }
410: if (req.getParameter("message") != null) {
411: message = req.getParameter("message");
412: }
413: } else {
414: lines = getStringProperty("lines");
415: }
416: String fontFace = getStringProperty("fontFace1");
417: Integer maxLines = Integers.get(Integer
418: .parseInt(getStringProperty("maxLines")));
419: String linesToDisplay = bundle.getString("linesToDisplay");
420: MessageFormat mf = new MessageFormat(linesToDisplay);
421: Locale locale = null;
422: locale = getProviderContext().getLocale();
423:
424: mf.setLocale(locale);
425: String formattedLinesToDisplay = mf.format(
426: new Object[] { maxLines }, new StringBuffer(), null)
427: .toString();
428:
429: Hashtable tagTable = new Hashtable();
430: tagTable.put("fontface", fontFace);
431: tagTable.put("messagelabel", newMessage);
432: tagTable.put("message", message);
433: tagTable.put("lineslabel", formattedLinesToDisplay);
434: tagTable.put("numlines", lines);
435:
436: return (getTemplate("edit.template", tagTable));
437: }
438:
439: public URL processEdit(HttpServletRequest req,
440: HttpServletResponse res) throws ProviderException {
441: getProviderContext().contentChanged(getName());
442:
443: String msg = req.getParameter("message");
444: String lines = req.getParameter("lines");
445: String maxLines = getStringProperty("maxLines");
446: editContainer = req.getParameter("provider");
447: container = req.getParameter("containerName");
448: // Get Resource Bundle if it is null
449: if (bundle == null) {
450: bundle = getResourceBundle();
451: }
452:
453: // make sure they haven't exceeded the number of lines to display
454: //
455: if (lines != null && !lines.trim().equals("")) {
456: try {
457: if (Integer.parseInt(lines) > Integer
458: .parseInt(maxLines)) {
459: String err = bundle.getString("maxLinesExceeded");
460: StringBuffer nextURLString = new StringBuffer();
461: URL next = null;
462: if (container != null && editContainer != null) {
463: nextURLString
464: .append(
465: getProviderContext()
466: .getDesktopURL(req))
467: .append("?action=edit&provider=")
468: .append(
469: URLEncoder
470: .encode(editContainer))
471: .append("&targetprovider=").append(
472: URLEncoder.encode(getName()))
473: .append("&containerName=").append(
474: URLEncoder.encode(container))
475: .append("&lines=").append(
476: URLEncoder.encode(lines))
477: .append("&message=").append(
478: URLEncoder.encode(msg)).append(
479: "&error=").append(
480: URLEncoder.encode(err + " : "
481: + maxLines));
482: next = new URL(nextURLString.toString());
483: }
484: return next;
485: }
486: } catch (NumberFormatException e) {
487: logger.log(Level.FINEST, "PSDT_CSPPN0006", e);
488: String err = bundle.getString("linesNAN");
489: throw new InvalidEditFormDataException(err);
490: } catch (java.net.MalformedURLException me) {
491: logger.log(Level.FINEST, "PSDT_CSPPN0006", me);
492: throw new ProviderException(
493: "NotesProvider.processEdit() : " + me);
494: }
495: setStringProperty("lines", lines);
496: }
497: if ((msg == null) || msg.trim().equals("")) {
498: return null;
499: }
500:
501: String line = null;
502: PrintWriter pw = null;
503: List v = new ArrayList();
504: String filename = getStringProperty("location");
505: motdFile = new File(filename);
506:
507: try {
508: motdFile.createNewFile();
509: } catch (IOException ioe) {
510: logger.log(Level.FINEST, "PSDT_CSPPN0006", ioe);
511: // Under heavy load, this is logging numerous error messages in the log file, which creates
512: // a performance problem. Hence the error messages are not logged any more
513: // and a null will be returned.
514: return null;
515: }
516:
517: File lockFile = new File(filename + ".lock");
518: // Lock the notes file using nio channel locks and logger api.
519: FileLock filelock = getLock(filename, lockFile);
520: if (filelock != null) {
521:
522: BufferedReader in = null;
523: try {
524: in = new BufferedReader(new InputStreamReader(
525: new FileInputStream(motdFile),
526: I18n.DEFAULT_CHARSET));
527: } catch (UnsupportedEncodingException uec) {
528: logger.log(Level.FINEST, "PSDT_CSPPN0006", uec);
529: //do cleanup
530: Release(filelock, filename, lockFile);
531: throw new ProviderException(
532: "error creating input stream", uec);
533: } catch (FileNotFoundException fnfe) {
534: logger.log(Level.FINEST, "PSDT_CSPPN0006", fnfe);
535: //do cleanup
536: Release(filelock, filename, lockFile);
537: throw new ProviderException(
538: "error creating input stream", fnfe);
539: }
540:
541: try {
542: while ((line = in.readLine()) != null) {
543: v.add(line);
544: }
545: } catch (IOException ioe) {
546: logger.log(Level.FINEST, "PSDT_CSPPN0006", ioe);
547: //do cleanup
548: Release(filelock, filename, lockFile);
549: // Under heavy load, this is logging numerous error messages in the log file, which creates
550: // a performance problem. Hence the error messages are not logged any more
551: // and a null will be returned.
552: return null;
553:
554: } finally {
555: if (in != null) {
556: try {
557: in.close();
558: } catch (IOException ioe) {
559: // Under heavy load, this is logging numerous error messages in the log file, which creates
560: // a performance problem. Hence the error messages are not logged any more
561: // and a null will be returned.
562:
563: }
564: }
565:
566: }
567:
568: try {
569: pw = new PrintWriter(new BufferedWriter(
570: new OutputStreamWriter(new FileOutputStream(
571: motdFile.toString()),
572: I18n.DEFAULT_CHARSET)));
573:
574: Date date = new Date();
575: String time = Long.toString(date.getTime());
576:
577: StringBuffer user = new StringBuffer();
578: ProviderContext context = getProviderContext();
579: user.append(context.getStringAttribute(COMMON_NAME,
580: context.getLocale()));
581: pw.println(user.toString() + "|" + time + "|" + msg);
582:
583: for (Iterator i = v.iterator(); i.hasNext();) {
584: line = (String) i.next();
585: pw.println(line);
586: }
587: } catch (UnsupportedEncodingException uec) {
588: logger.log(Level.FINEST, "PSDT_CSPPN0006", uec);
589: throw new ProviderException(
590: "error creating output stream", uec);
591: } catch (FileNotFoundException fnfe) {
592: logger.log(Level.FINEST, "PSDT_CSPPN0006", fnfe);
593: throw new ProviderException(
594: "error creating output stream", fnfe);
595: } finally {
596: if (pw != null) {
597: pw.flush();
598: pw.close();
599: }
600: //do cleanup
601: Release(filelock, filename, lockFile);
602: }
603: }
604: return null;
605: }
606:
607: /*This method returns true if the header of Handler container the specified filename
608: else returns false . Also returns false in case the file handler itself does not exist*/
609: private boolean checkForMyHandler(String fileName) {
610: Logger logger = Logger.getLogger(fileLockingLogger);
611: Handler handlers[] = logger.getHandlers();
612: for (int i = 0; i < handlers.length; i++) {
613: Handler myHandler = handlers[i];
614: if (myHandler.getFormatter().getHead(myHandler).equals(
615: fileName)) //will getFormatter return null ?
616: return true;
617: }
618: return false;
619: }
620:
621: /*This method returns false if handler with this file name in the formatter , already exists */
622: private boolean addMyHandler(final String fileName)
623: throws IOException {
624: if (checkForMyHandler(fileName))
625: return false;
626: Logger logger = Logger.getLogger(fileLockingLogger);
627: Handler myHandler = new Handler() {
628: public void publish(LogRecord rec) {
629: }
630:
631: public void close() {
632: }
633:
634: public void flush() {
635: }
636: };
637: myHandler.setFormatter(new Formatter() {
638: public String getHead(Handler handler) {
639: return fileName;
640: }
641:
642: public String format(LogRecord record) {
643: return null;
644: }
645: });
646: logger.addHandler(myHandler);
647: return true;
648:
649: }
650:
651: /*This method removes the handler whose formatter contains the specified file name */
652: private void removeMyHandler(String fileName) {
653: Logger logger = Logger.getLogger(fileLockingLogger);
654: Handler handlers[] = logger.getHandlers();
655: for (int i = 0; i < handlers.length; i++) {
656: Handler myHandler = handlers[i];
657: if (myHandler.getFormatter().getHead(myHandler).equals(
658: fileName))
659: logger.removeHandler(myHandler);
660: }
661: }
662:
663: /* This method does the clean up for all the acquired resources. */
664: private void Release(FileLock filelock, String filename,
665: File lockFile) {
666: //do cleanup
667: //release file lock
668: if (filelock != null) {
669: try {
670: filelock.release();
671: } catch (IOException e) {
672:
673: }
674: }
675: //delete lockfile
676: lockFile.delete();
677: //clean the header of the formatter used by the handler
678: synchronized (Logger.getLogger(fileLockingLogger)) {
679: removeMyHandler(filename);
680: }
681: }
682:
683: }
|