1 40 41 package org.dspace.app.webui.servlet; 42 43 import java.io.IOException ; 44 import java.sql.SQLException ; 45 import java.text.MessageFormat ; 46 import java.text.ParseException ; 47 import java.util.Date ; 48 import java.util.Iterator ; 49 import java.util.Locale ; 50 import java.util.Map ; 51 import java.util.HashMap ; 52 import java.util.List ; 53 import java.util.ArrayList ; 54 import java.util.ResourceBundle ; 55 import java.util.StringTokenizer ; 56 57 import javax.servlet.ServletException ; 58 import javax.servlet.http.HttpServletRequest ; 59 import javax.servlet.http.HttpServletResponse ; 60 61 import org.apache.log4j.Logger; 62 63 import com.sun.syndication.feed.rss.Channel; 64 import com.sun.syndication.feed.rss.Description; 65 import com.sun.syndication.feed.rss.Image; 66 import com.sun.syndication.feed.rss.TextInput; 67 import com.sun.syndication.io.WireFeedOutput; 68 import com.sun.syndication.io.FeedException; 69 70 import org.dspace.app.webui.util.JSPManager; 71 import org.dspace.authorize.AuthorizeException; 72 import org.dspace.content.DSpaceObject; 73 import org.dspace.content.Collection; 74 import org.dspace.content.Community; 75 import org.dspace.content.Item; 76 import org.dspace.content.Bitstream; 77 import org.dspace.content.DCValue; 78 import org.dspace.content.DCDate; 79 import org.dspace.core.LogManager; 80 import org.dspace.core.ConfigurationManager; 81 import org.dspace.core.Constants; 82 import org.dspace.core.Context; 83 import org.dspace.browse.Browse; 84 import org.dspace.browse.BrowseScope; 85 import org.dspace.handle.HandleManager; 86 import org.dspace.search.Harvest; 87 88 96 public class FeedServlet extends DSpaceServlet 97 { 98 public static final String SITE_FEED_KEY = "site"; 100 101 private static final long HOUR_MSECS = 60 * 60 * 1000; 103 104 private static Logger log = Logger.getLogger(FeedServlet.class); 105 private String clazz = "org.dspace.app.webui.servlet.FeedServlet"; 106 107 108 private static boolean enabled = false; 110 private static int itemCount = 0; 112 private static Map feedCache = null; 114 private static int cacheSize = 0; 116 private static int cacheAge = 0; 118 private static List formats = null; 120 121 private static ResourceBundle labels = null; 123 124 private static String defaultDescriptionFields = "dc.title, dc.contributor.author, dc.contributor.editor, dc.description.abstract, dc.description"; 126 127 128 static 129 { 130 enabled = ConfigurationManager.getBooleanProperty("webui.feed.enable"); 131 } 132 133 public void init() 134 { 135 if (enabled) 137 { 138 String fmtsStr = ConfigurationManager.getProperty("webui.feed.formats"); 139 if ( fmtsStr != null ) 140 { 141 formats = new ArrayList (); 142 String [] fmts = fmtsStr.split(","); 143 for (int i = 0; i < fmts.length; i++) 144 { 145 formats.add(fmts[i]); 146 } 147 } 148 itemCount = ConfigurationManager.getIntProperty("webui.feed.items"); 149 cacheSize = ConfigurationManager.getIntProperty("webui.feed.cache.size"); 150 if (cacheSize > 0) 151 { 152 feedCache = new HashMap (); 153 cacheAge = ConfigurationManager.getIntProperty("webui.feed.cache.age"); 154 } 155 } 156 } 157 158 protected void doDSGet(Context context, HttpServletRequest request, 159 HttpServletResponse response) throws ServletException , IOException , 160 SQLException , AuthorizeException 161 { 162 String path = request.getPathInfo(); 163 String feedType = null; 164 String handle = null; 165 166 if(labels==null) 167 { 168 Locale locale = request.getLocale(); 170 labels = ResourceBundle.getBundle("Messages", locale); 171 } 172 173 if (path != null) 174 { 175 path = path.substring(1); 177 int split = path.indexOf("/"); 178 if (split != -1) 179 { 180 feedType = path.substring(0,split); 181 handle = path.substring(split+1); 182 } 183 } 184 185 DSpaceObject dso = null; 186 187 if(!handle.equals(SITE_FEED_KEY)) 190 { 191 dso = HandleManager.resolveToObject(context, handle); 193 } 194 195 if (! enabled || (dso != null && 196 (dso.getType() != Constants.COLLECTION && dso.getType() != Constants.COMMUNITY)) ) 197 { 198 log.info(LogManager.getHeader(context, "invalid_id", "path=" + path)); 199 JSPManager.showInvalidIDError(request, response, path, -1); 200 return; 201 } 202 203 if( feedType == null || ! formats.contains( feedType ) ) 205 { 206 log.info(LogManager.getHeader(context, "invalid_syndformat", "path=" + path)); 207 JSPManager.showInvalidIDError(request, response, path, -1); 208 return; 209 } 210 211 Channel channel = null; 213 if (feedCache != null) 214 { 215 CacheFeed cFeed = (CacheFeed)feedCache.get(handle); 217 if (cFeed != null) { 219 boolean cacheFeedCurrent = false; 221 if (cFeed.timeStamp + (cacheAge * HOUR_MSECS) < System.currentTimeMillis()) 222 { 223 cacheFeedCurrent = true; 224 } 225 else if ( ! itemsChanged(context, dso, cFeed.timeStamp)) 227 { 228 cFeed.timeStamp = System.currentTimeMillis(); 230 cacheFeedCurrent = true; 231 } 232 if (cacheFeedCurrent) 233 { 234 channel = cFeed.access(); 235 } 236 } 237 } 238 239 if (channel == null) 241 { 242 channel = generateFeed(context, dso); 243 if (feedCache != null) 244 { 245 cache(handle, new CacheFeed(channel)); 246 } 247 } 248 249 channel.setFeedType(feedType); 251 WireFeedOutput feedWriter = new WireFeedOutput(); 252 try 253 { 254 response.setContentType("text/xml; charset=UTF-8"); 255 feedWriter.output(channel, response.getWriter()); 256 } 257 catch( FeedException fex ) 258 { 259 throw new IOException (fex.getMessage()); 260 } 261 } 262 263 private boolean itemsChanged(Context context, DSpaceObject dso, long timeStamp) 264 throws SQLException 265 { 266 DCDate dcStartDate = new DCDate( new Date (timeStamp) ); 268 DCDate dcEndDate = new DCDate( new Date (System.currentTimeMillis()) ); 269 270 String startDate = dcStartDate.toString().substring(0, 10); 272 String endDate = dcEndDate.toString().substring(0, 10); 273 274 try { 276 return (Harvest.harvest(context, dso, startDate, endDate, 277 0, 1, false, false, false).size() > 0); 278 } 279 catch (ParseException pe) 280 { 281 return false; 283 } 284 } 285 286 296 private Channel generateFeed(Context context, DSpaceObject dso) 297 throws IOException , SQLException 298 { 299 String dspaceUrl = ConfigurationManager.getProperty("dspace.url"); 301 String type = null; 302 String description = null; 303 String title = null; 304 Bitstream logo = null; 305 BrowseScope scope = new BrowseScope(context); 307 Channel channel = new Channel(); 309 310 if(dso == null) 313 { 314 channel.setTitle(ConfigurationManager.getProperty("dspace.name")); 315 channel.setLink(dspaceUrl); 316 channel.setDescription(labels.getString(clazz + ".general-feed.description")); 317 } 318 else { 320 if (dso.getType() == Constants.COLLECTION) 321 { 322 type = labels.getString(clazz + ".feed-type.collection"); 323 Collection col = (Collection)dso; 324 description = col.getMetadata("short_description"); 325 title = col.getMetadata("name"); 326 logo = col.getLogo(); 327 scope.setScope(col); 328 } 329 else if (dso.getType() == Constants.COMMUNITY) 330 { 331 type = labels.getString(clazz + ".feed-type.community"); 332 Community comm = (Community)dso; 333 description = comm.getMetadata("short_description"); 334 title = comm.getMetadata("name"); 335 logo = comm.getLogo(); 336 scope.setScope(comm); 337 } 338 339 String objectUrl = ConfigurationManager.getBooleanProperty("webui.feed.localresolve") 340 ? HandleManager.resolveToURL(context, dso.getHandle()) 341 : HandleManager.getCanonicalForm(dso.getHandle()); 342 343 channel.setDescription(description); 345 channel.setLink(objectUrl); 346 String channelTitle = MessageFormat.format(labels.getString(clazz + ".feed.title"), 348 new Object []{type, title}); 349 channel.setTitle(channelTitle); 350 351 if (logo != null) 353 { 354 Image image = new Image(); 357 image.setLink(objectUrl); 358 image.setTitle(labels.getString(clazz + ".logo.title")); 359 image.setUrl(dspaceUrl + "/retrieve/" + logo.getID()); 360 channel.setImage(image); 361 } 362 } 363 364 TextInput input = new TextInput(); 369 input.setLink(dspaceUrl + "/simple-search"); 370 input.setDescription( labels.getString(clazz + ".search.description") ); 371 372 String searchTitle = ""; 373 374 if(type!=null) 376 { 377 searchTitle = MessageFormat.format(labels.getString(clazz + ".search.title"), 378 new Object []{type}); 379 } 380 else { 382 searchTitle = labels.getString(clazz + ".search.title.default"); 383 } 384 385 input.setTitle(searchTitle); 386 input.setName(labels.getString(clazz + ".search.name")); 387 channel.setTextInput(input); 388 389 scope.setTotal(itemCount); 391 List results = Browse.getLastSubmitted(scope); 392 List items = new ArrayList (); 393 for ( int i = 0; i < results.size(); i++ ) 394 { 395 items.add( itemFromDSpaceItem(context, (Item)results.get(i)) ); 396 } 397 channel.setItems(items); 398 399 return channel; 400 } 401 402 411 private com.sun.syndication.feed.rss.Item itemFromDSpaceItem(Context context, 412 Item dspaceItem) 413 throws SQLException 414 { 415 com.sun.syndication.feed.rss.Item rssItem = 416 new com.sun.syndication.feed.rss.Item(); 417 418 String titleField = ConfigurationManager.getProperty("webui.feed.item.title"); 420 if (titleField == null) 421 { 422 titleField = "dc.title"; 423 } 424 425 String dateField = ConfigurationManager.getProperty("webui.feed.item.date"); 426 if (dateField == null) 427 { 428 dateField = "dc.date.issued"; 429 } 430 431 String itHandle = ConfigurationManager.getBooleanProperty("webui.feed.localresolve") 433 ? HandleManager.resolveToURL(context, dspaceItem.getHandle()) 434 : HandleManager.getCanonicalForm(dspaceItem.getHandle()); 435 436 rssItem.setLink(itHandle); 437 438 String title = null; 440 try 441 { 442 title = dspaceItem.getMetadata(titleField)[0].value; 443 444 } 445 catch (ArrayIndexOutOfBoundsException e) 446 { 447 title = labels.getString(clazz + ".notitle"); 448 } 449 rssItem.setTitle(title); 450 451 String descriptionFields = ConfigurationManager 454 .getProperty("webui.feed.item.description"); 455 456 if (descriptionFields == null) 457 { 458 descriptionFields = defaultDescriptionFields; 459 } 460 461 StringBuffer descBuf = new StringBuffer (); 463 StringTokenizer st = new StringTokenizer (descriptionFields, ","); 464 465 while (st.hasMoreTokens()) 466 { 467 String field = st.nextToken().trim(); 468 boolean isDate = false; 469 470 if (field.indexOf("(date)") > 0) 472 { 473 field = field.replaceAll("\\(date\\)", ""); 474 isDate = true; 475 } 476 477 478 DCValue[] values = dspaceItem.getMetadata(field); 480 481 if(values != null && values.length>0) 482 { 483 if(descBuf.length() > 0) 486 { 487 descBuf.append("\n<br/>"); 488 descBuf.append("\n<br/>"); 489 } 490 491 String fieldLabel = null; 492 try 493 { 494 fieldLabel = labels.getString("metadata." + field); 495 } 496 catch(java.util.MissingResourceException e) {} 497 498 if(fieldLabel !=null && fieldLabel.length()>0) 499 descBuf.append(fieldLabel + ": "); 500 501 for(int i=0; i<values.length; i++) 502 { 503 String fieldValue = values[i].value; 504 if(isDate) 505 fieldValue = (new DCDate(fieldValue)).toString(); 506 descBuf.append(fieldValue); 507 if (i < values.length - 1) 508 { 509 descBuf.append("; "); 510 } 511 } 512 } 513 514 } Description descrip = new Description(); 516 descrip.setValue(descBuf.toString()); 517 rssItem.setDescription(descrip); 518 519 520 String dcDate = null; 522 try 523 { 524 dcDate = dspaceItem.getMetadata(dateField)[0].value; 525 526 } 527 catch (ArrayIndexOutOfBoundsException e) 528 { 529 } 530 if (dcDate != null) 531 { 532 rssItem.setPubDate((new DCDate(dcDate)).toDate()); 533 } 534 535 return rssItem; 536 } 537 538 541 542 550 private static void cache(String feedKey, CacheFeed newFeed) 551 { 552 if (feedCache.size() >= cacheSize) 554 { 555 int total = 0; 557 String minKey = null; 558 CacheFeed minFeed = null; 559 CacheFeed maxFeed = null; 560 561 Iterator iter = feedCache.keySet().iterator(); 562 while (iter.hasNext()) 563 { 564 String key = (String )iter.next(); 565 CacheFeed feed = (CacheFeed)feedCache.get(key); 566 if (minKey != null) 567 { 568 if (feed.hits < minFeed.hits) 569 { 570 minKey = key; 571 minFeed = feed; 572 } 573 if (feed.hits >= maxFeed.hits) 574 { 575 maxFeed = feed; 576 } 577 } 578 else 579 { 580 minKey = key; 581 minFeed = maxFeed = feed; 582 } 583 total += feed.hits; 584 } 585 int avg = total / feedCache.size(); 587 String logMsg = "feedCache() - size: " + feedCache.size() + 588 " Hits - total: " + total + " avg: " + avg + 589 " max: " + maxFeed.hits + " min: " + minFeed.hits; 590 log.info(logMsg); 591 feedCache.remove(minKey); 593 } 594 feedCache.put(feedKey, newFeed); 596 } 597 598 601 private class CacheFeed 602 { 603 public long timeStamp = 0L; 605 public int hits = 0; 607 private Channel feed = null; 609 610 public CacheFeed(Channel feed) 611 { 612 this.feed = feed; 613 timeStamp = System.currentTimeMillis(); 614 } 615 616 public Channel access() 617 { 618 ++hits; 619 return feed; 620 } 621 } 622 } 623 | Popular Tags |