File Source: AtomServlet.java

     1  /*
     2  * Licensed to the Apache Software Foundation (ASF) under one or more
     3  *  contributor license agreements.  The ASF licenses this file to You
     4  * under the Apache License, Version 2.0 (the "License"); you may not
     5  * use this file except in compliance with the License.
     6  * You may obtain a copy of the License at
     7  *
     8  *     http://www.apache.org/licenses/LICENSE-2.0
     9  *
    10  * Unless required by applicable law or agreed to in writing, software
    11  * distributed under the License is distributed on an "AS IS" BASIS,
    12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  * See the License for the specific language governing permissions and
    14  * limitations under the License.  For additional information regarding
    15  * copyright in this work, please see the NOTICE file in the top level
    16  * directory of this distribution.
    17  */
    18  package org.apache.roller.weblogger.webservices.atomprotocol; 
    19  
    20  import java.io.IOException;
    21  import java.io.InputStreamReader;
    22  import java.io.Reader;
    23  import java.io.Writer;
    24  import java.util.ArrayList;
    25  import java.util.Iterator;
    26  import java.util.List;
    27  import javax.servlet.ServletException;
    28  import javax.servlet.http.HttpServlet;
    29  import javax.servlet.http.HttpServletRequest;
    30  import javax.servlet.http.HttpServletResponse;
    31  import org.apache.commons.lang.StringUtils;
    32  import org.apache.commons.logging.Log;
    33  import org.apache.commons.logging.LogFactory;
    34  import org.jdom.Document;
    35  import org.jdom.Element;
    36  import org.jdom.JDOMException;
    37  import org.jdom.input.SAXBuilder;
    38  import org.jdom.output.Format;
    39  import org.jdom.output.XMLOutputter;
    40  import com.sun.syndication.feed.atom.Entry;
    41  import com.sun.syndication.feed.atom.Feed;
    42  import com.sun.syndication.feed.atom.Link;
    43  import com.sun.syndication.io.FeedException;
    44  import com.sun.syndication.io.WireFeedInput;
    45  import com.sun.syndication.io.WireFeedOutput;
    46  import java.io.BufferedReader;
    47  import java.io.StringWriter;
    48  import org.jdom.Namespace;
    49  import org.apache.roller.weblogger.config.WebloggerConfig;
    50  import org.apache.roller.weblogger.util.Utilities;
    51  
    52  /**
    53   * Atom Servlet implements Atom by calling a Roller independent handler.
    54   * @web.servlet name="AtomServlet"
    55   * @web.servlet-mapping url-pattern="/roller-services/app/*"
    56   * @author David M Johnson
    57   */
         /* 
    P/P   *  Method: void org.apache.roller.weblogger.webservices.atomprotocol.AtomServlet()
          */
    58  public class AtomServlet extends HttpServlet {
    59      public static final String FEED_TYPE = "atom_1.0";
    60      
             /* 
    P/P       *  Method: org.apache.roller.weblogger.webservices.atomprotocol.AtomServlet__static_init
              * 
              *  Presumptions:
              *    org.apache.commons.logging.LogFactory:getFactory(...)@61 != null
              * 
              *  Postconditions:
              *    init'ed(log)
              */
    61      private static Log log =
    62              LogFactory.getFactory().getInstance(AtomServlet.class);
    63      
    64      //-----------------------------------------------------------------------------
    65      /**
    66       * Create an Atom request handler.
    67       * TODO: make AtomRequestHandler implementation configurable.
    68       */
    69      private AtomHandler createAtomRequestHandler(HttpServletRequest request) 
    70      throws ServletException {
                 /* 
    P/P           *  Method: AtomHandler createAtomRequestHandler(HttpServletRequest)
                  * 
                  *  Preconditions:
                  *    log != null
                  *    (soft) org/apache/roller/weblogger/webservices/atomprotocol/RollerAtomHandler.log != null
                  *    (soft) request != null
                  * 
                  *  Presumptions:
                  *    org.apache.roller.weblogger.business.WebloggerFactory:getWeblogger(...)@138 != null
                  *    org.apache.roller.weblogger.config.WebloggerConfig:getBooleanProperty(...)@72 == 1
                  * 
                  *  Postconditions:
                  *    return_value == &new RollerAtomHandler(createAtomRequestHandler#1)
                  *    new RollerAtomHandler(createAtomRequestHandler#1) num objects == 1
                  *    init'ed(return_value.atomURL)
                  *    return_value.maxEntries == 20
                  *    return_value.roller != null
                  *    init'ed(return_value.user)
                  */
    71          log.debug("Creating Atom handler");
    72          boolean enabled = WebloggerConfig.getBooleanProperty(
    73              "webservices.atomprotocol.enabled");
    74          if (!enabled) {
    75              throw new ServletException("ERROR: Atom protocol not enabled");
    76          }
    77          return new RollerAtomHandler(request);
    78      }
    79      
    80      //-----------------------------------------------------------------------------
    81      /**
    82       * Handles an Atom GET by calling handler and writing results to response.
    83       */
    84      protected void doGet(HttpServletRequest req, HttpServletResponse res)
    85      throws ServletException, IOException { 
                 /* 
    P/P           *  Method: void doGet(HttpServletRequest, HttpServletResponse)
                  * 
                  *  Preconditions:
                  *    log != null
                  *    res != null
                  *    (soft) init'ed(org/apache/roller/weblogger/webservices/atomprotocol/AtomService.ATOM_FORMAT)
                  *    (soft) init'ed(org/apache/roller/weblogger/webservices/atomprotocol/AtomService.ATOM_PROTOCOL)
                  *    (soft) org/apache/roller/weblogger/webservices/atomprotocol/RollerAtomHandler.log != null
                  *    (soft) req != null
                  * 
                  *  Presumptions:
                  *    javax.servlet.http.HttpServletResponse:getOutputStream(...)@134 != null
                  *    javax.servlet.http.HttpServletResponse:getOutputStream(...)@135 != null
                  *    javax.servlet.http.HttpServletResponse:getWriter(...)@111 != null
                  *    javax.servlet.http.HttpServletResponse:getWriter(...)@123 != null
                  *    javax.servlet.http.HttpServletResponse:getWriter(...)@97 != null
                  *    ...
                  * 
                  *  Test Vectors:
                  *    pathInfo.length@90: {3..232-1}, {0}
                  */
    86          log.debug("Entering");
    87          AtomHandler handler = createAtomRequestHandler(req);
    88          String userName = handler.getAuthenticatedUsername();
    89          if (userName != null) {
    90              String[] pathInfo = getPathInfo(req);
    91              try {
    92                  if (handler.isIntrospectionURI(pathInfo)) {
    93                      // return an Atom Service document
    94                      AtomService service = handler.getIntrospection();
    95                      Document doc = AtomService.serviceToDocument(service);
    96                      res.setContentType("application/atomsvc+xml; charset=utf-8");
    97                      Writer writer = res.getWriter();
    98                      XMLOutputter outputter = new XMLOutputter();
    99                      outputter.setFormat(Format.getPrettyFormat());
   100                      outputter.output(doc, writer);
   101                      writer.close();
   102                      res.setStatus(HttpServletResponse.SC_OK);
   103                  } 
   104                  else if (handler.isCollectionURI(pathInfo)) {
   105                      // return a collection
+  106                      Feed col = handler.getCollection(pathInfo);
   107                      col.setFeedType(FEED_TYPE);
   108                      WireFeedOutput wireFeedOutput = new WireFeedOutput();
   109                      Document feedDoc = wireFeedOutput.outputJDom(col);
   110                      res.setContentType("application/atom+xml; charset=utf-8");
   111                      Writer writer = res.getWriter();
   112                      XMLOutputter outputter = new XMLOutputter();
   113                      outputter.setFormat(Format.getPrettyFormat());
   114                      outputter.output(feedDoc, writer);
   115                      writer.close();
   116                      res.setStatus(HttpServletResponse.SC_OK);
   117                  } 
   118                  else if (handler.isEntryURI(pathInfo)) {
   119                      // return an entry
+  120                      Entry entry = handler.getEntry(pathInfo);
+  121                      if (entry != null) {
   122                          res.setContentType("application/atom+xml; charset=utf-8");
   123                          Writer writer = res.getWriter();
   124                          serializeEntry(entry, writer);
   125                          writer.close();
   126                      } else {
+  127                          res.setStatus(HttpServletResponse.SC_NOT_FOUND);
   128                      }
   129                  } else if (handler.isMediaEditURI(pathInfo)) {
   130                      AtomMediaResource entry = handler.getMediaResource(pathInfo);
   131                      res.setContentType(entry.getContentType());
   132                      res.setContentLength(entry.getContentLength());
   133                      Utilities.copyInputToOutput(entry.getInputStream(), res.getOutputStream());
   134                      res.getOutputStream().flush();
   135                      res.getOutputStream().close();                
   136                  } else {
   137                      res.setStatus(HttpServletResponse.SC_NOT_FOUND);
   138                  }
   139              } catch (AtomException ae) {
   140                  res.sendError(ae.getStatus(), ae.getMessage());
   141              } catch (Exception e) {
   142                  res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
   143                  log.debug(e);
   144              }
   145          } else {
   146              res.setHeader("WWW-Authenticate", "BASIC realm=\"Roller\"");
   147              res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
   148          }
   149          log.debug("Exiting");
   150      }
   151      
   152      //-----------------------------------------------------------------------------
   153      /**
   154       * Handles an Atom POST by calling handler to identify URI, reading/parsing
   155       * data, calling handler and writing results to response.
   156       */
   157      protected void doPost(HttpServletRequest req, HttpServletResponse res)
   158      throws ServletException, IOException {
                 /* 
    P/P           *  Method: void doPost(HttpServletRequest, HttpServletResponse)
                  * 
                  *  Preconditions:
                  *    log != null
                  *    res != null
                  *    (soft) org/apache/roller/weblogger/webservices/atomprotocol/RollerAtomHandler.log != null
                  *    (soft) init'ed(org/apache/roller/weblogger/webservices/atomprotocol/RollerAtomHandler.throttle)
                  *    (soft) req != null
                  * 
                  *  Presumptions:
                  *    com.sun.syndication.feed.atom.Entry:getContents(...)@203 != null
                  *    com.sun.syndication.feed.atom.Entry:getOtherLinks(...)@178 != null
                  *    com.sun.syndication.feed.atom.Entry:getOtherLinks(...)@207 != null
                  *    com.sun.syndication.feed.atom.Link:getRel(...)@181 != null
                  *    com.sun.syndication.feed.atom.Link:getRel(...)@210 != null
                  *    ...
                  * 
                  *  Test Vectors:
                  *    com.sun.syndication.feed.atom.Link:getRel(...)@181: Inverse{null}, Addr_Set{null}
                  *    com.sun.syndication.feed.atom.Link:getRel(...)@210: Inverse{null}, Addr_Set{null}
                  *    java.lang.String:equals(...)@181: {1}, {0}
                  *    java.lang.String:equals(...)@210: {1}, {0}
                  *    java.lang.String:startsWith(...)@167: {0}, {1}
                  *    java.util.Iterator:hasNext(...)@179: {0}, {1}
                  *    java.util.Iterator:hasNext(...)@208: {0}, {1}
                  *    javax.servlet.http.HttpServletRequest:getContentType(...)@193: Addr_Set{null}, Inverse{null}
                  */
   159          log.debug("Entering");
   160          AtomHandler handler = createAtomRequestHandler(req);
   161          String userName = handler.getAuthenticatedUsername();
   162          if (userName != null) {
   163              String[] pathInfo = getPathInfo(req);
   164              try {
   165                  if (handler.isCollectionURI(pathInfo)) {
   166                      
   167                      if (req.getContentType().startsWith("application/atom+xml")) {
   168  
   169                          // parse incoming entry
   170                          Entry unsavedEntry = parseEntry(new BufferedReader(
   171                              new InputStreamReader(
   172                                  req.getInputStream(), "UTF-8")));
   173  
   174                          // call handler to post it
   175                          Entry savedEntry = handler.postEntry(pathInfo, unsavedEntry);
   176                          
   177                          // return member entry URI as location header
   178                          Iterator links = savedEntry.getOtherLinks().iterator();
   179                          while (links.hasNext()) {
   180                              Link link = (Link) links.next();
   181                              if (link.getRel().equals("edit") || link.getRel() == null) {
   182                                  res.addHeader("Location", link.getHref());
   183                                  break;
   184                              }
   185                          }
   186                          // write entry back out to response
   187                          res.setStatus(HttpServletResponse.SC_CREATED);
   188                          res.setContentType("application/atom+xml; charset=utf-8");
   189                          Writer writer = res.getWriter();
   190                          serializeEntry(savedEntry, writer);
   191                          writer.close(); 
   192                      
   193                      } else if (req.getContentType() != null) {
   194                          // get incoming title and slug from HTTP header
   195                          String title = req.getHeader("Title");
   196                          String slug = req.getHeader("Slug");
   197  
   198                          // hand input stream off to hander to post file
   199                          Entry resource = handler.postMedia(
   200                              pathInfo, title, slug, req.getContentType(), req.getInputStream());
   201                          
   202                          res.setStatus(HttpServletResponse.SC_CREATED);
+  203                          com.sun.syndication.feed.atom.Content content = 
   204                              (com.sun.syndication.feed.atom.Content)resource.getContents().get(0);
   205  
   206                          // return member entry URI as location header
   207                          Iterator links = resource.getOtherLinks().iterator();
   208                          while (links.hasNext()) {
   209                              Link link = (Link) links.next();
   210                              if (link.getRel().equals("edit") || link.getRel() == null) {
   211                                  res.addHeader("Location", link.getHref());
   212                                  break;
   213                              }
   214                          }
   215                          Writer writer = res.getWriter();
   216                          serializeEntry(resource, writer);
   217                          writer.close(); 
   218                      } else {
   219                          res.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE,
   220                                  "No content-type specified in request");
   221                      }
   222                      
   223                  } else {
   224                      res.sendError(HttpServletResponse.SC_NOT_FOUND,
   225                              "Invalid collection specified in request");
   226                  }
   227              } catch (AtomException ae) {
   228                  res.sendError(ae.getStatus(), ae.getMessage());
   229              } catch (Exception e) {
   230                  res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
   231                  log.debug(e);
   232              }
   233          } else {
   234              res.setHeader("WWW-Authenticate", "BASIC realm=\"Roller\"");
   235              res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
   236          }
   237          log.debug("Exiting");
   238      }
   239      
   240      //-----------------------------------------------------------------------------
   241      /**
   242       * Handles an Atom PUT by calling handler to identify URI, reading/parsing
   243       * data, calling handler and writing results to response.
   244       */
   245      protected void doPut(HttpServletRequest req, HttpServletResponse res)
   246      throws ServletException, IOException {
                 /* 
    P/P           *  Method: void doPut(HttpServletRequest, HttpServletResponse)
                  * 
                  *  Preconditions:
                  *    log != null
                  *    res != null
                  *    (soft) org/apache/roller/weblogger/webservices/atomprotocol/RollerAtomHandler.log != null
                  *    (soft) init'ed(org/apache/roller/weblogger/webservices/atomprotocol/RollerAtomHandler.throttle)
                  *    (soft) req != null
                  * 
                  *  Presumptions:
                  *    java.util.List:get(...)@405 != null
                  *    javax.servlet.http.HttpServletResponse:getWriter(...)@265 != null
                  *    javax.servlet.http.HttpServletResponse:getWriter(...)@278 != null
                  *    org.apache.commons.lang.StringUtils:split(...)@338 != null
                  *    pathInfo.length@251 == 3
                  */
   247          log.debug("Entering");
   248          AtomHandler handler = createAtomRequestHandler(req);
   249          String userName = handler.getAuthenticatedUsername();
   250          if (userName != null) {
   251              String[] pathInfo = getPathInfo(req);
   252              try {
   253                  if (handler.isEntryURI(pathInfo)) {
   254                      
   255                      // parse incoming entry
   256                      Entry unsavedEntry = parseEntry(new BufferedReader(
   257                          new InputStreamReader(
   258                              req.getInputStream(), "UTF-8")));
   259                      
   260                      // call handler to put entry
   261                      Entry updatedEntry = handler.putEntry(pathInfo, unsavedEntry);
   262                      
   263                      // write entry back out to response
   264                      res.setContentType("application/atom+xml; charset=utf-8");
   265                      Writer writer = res.getWriter();
   266                      serializeEntry(updatedEntry, writer);
   267                      res.setStatus(HttpServletResponse.SC_OK);
   268                      writer.close();
   269                      
   270                  } else if (handler.isMediaEditURI(pathInfo)) {
   271                      
   272                      // hand input stream to handler
   273                      Entry updatedEntry = handler.putMedia(
   274                          pathInfo, req.getContentType(), req.getInputStream());
   275                                          
   276                      // write entry back out to response
   277                      res.setContentType("application/atom+xml; charset=utf-8");
   278                      Writer writer = res.getWriter();
   279                      serializeEntry(updatedEntry, writer);
   280                      writer.close();
   281                      res.setStatus(HttpServletResponse.SC_OK);
   282                      
   283                  } else {
   284                      res.setStatus(HttpServletResponse.SC_NOT_FOUND);
   285                  }
   286              } catch (AtomException ae) {
   287                  res.sendError(ae.getStatus(), ae.getMessage());
   288              } catch (Exception e) {
   289                  res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
   290                  log.debug(e);
   291              }
   292          } else {
   293              res.setHeader("WWW-Authenticate", "BASIC realm=\"Roller\"");
   294              res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
   295          }
   296          log.debug("Exiting");
   297      }
   298      
   299      //-----------------------------------------------------------------------------
   300      /**
   301       * Handle Atom DELETE by calling appropriate handler.
   302       */
   303      protected void doDelete(HttpServletRequest req, HttpServletResponse res)
   304      throws ServletException, IOException {
                 /* 
    P/P           *  Method: void doDelete(HttpServletRequest, HttpServletResponse)
                  * 
                  *  Preconditions:
                  *    log != null
                  *    res != null
                  *    (soft) org/apache/roller/weblogger/webservices/atomprotocol/RollerAtomHandler.log != null
                  *    (soft) req != null
                  * 
                  *  Presumptions:
                  *    org.apache.commons.lang.StringUtils:split(...)@338 != null
                  *    pathInfo.length@309 in 3..232-1
                  */
   305          log.debug("Entering");
   306          AtomHandler handler = createAtomRequestHandler(req);
   307          String userName = handler.getAuthenticatedUsername();
   308          if (userName != null) {
   309              String[] pathInfo = getPathInfo(req);
   310              try {
   311                  if (handler.isEntryURI(pathInfo)) {
+  312                      handler.deleteEntry(pathInfo);
   313                      res.setStatus(HttpServletResponse.SC_OK);
   314                  } 
   315                  else {
   316                      res.setStatus(HttpServletResponse.SC_NOT_FOUND);
   317                  }
   318              } catch (AtomException ae) {
   319                  res.sendError(ae.getStatus(), ae.getMessage());
   320              } catch (Exception e) {
   321                  res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
   322                  log.debug(e);
   323              }
   324          } else {
   325              res.setHeader("WWW-Authenticate", "BASIC realm=\"Roller\"");
   326              res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
   327          }
   328          log.debug("Exiting");
   329      }
   330      
   331      //-----------------------------------------------------------------------------
   332      /**
   333       * Convenience method to return the PathInfo from the request.
   334       */
   335      protected String[] getPathInfo(HttpServletRequest request) {
                 /* 
    P/P           *  Method: String[] getPathInfo(HttpServletRequest)
                  * 
                  *  Preconditions:
                  *    request != null
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  */
   336          String mPathInfo = request.getPathInfo();
   337          mPathInfo = (mPathInfo!=null) ? mPathInfo : "";
   338          return StringUtils.split(mPathInfo,"/");
   339      }
   340      
   341      /**
   342       * Serialize entry to writer.
   343       */
   344      public static void serializeEntry(Entry entry, Writer writer)
   345      throws IllegalArgumentException, FeedException, IOException {
   346          // Build a feed containing only the entry
                 /* 
    P/P           *  Method: void serializeEntry(Entry, Writer)
                  * 
                  *  Preconditions:
                  *    log != null
                  *    (soft) writer != null
                  * 
                  *  Presumptions:
                  *    com.sun.syndication.io.WireFeedOutput:outputJDom(...)@355 != null
                  *    java.util.List:get(...)@358 != null
                  *    org.jdom.Document:getRootElement(...)@358 != null
                  *    org.jdom.Element:getChildren(...)@358 != null
                  * 
                  *  Test Vectors:
                  *    org.apache.commons.logging.Log:isDebugEnabled(...)@368: {0}, {1}
                  */
   347          List entries = new ArrayList();
   348          entries.add(entry);
   349          Feed feed1 = new Feed();
   350          feed1.setFeedType(AtomServlet.FEED_TYPE);
   351          feed1.setEntries(entries);
   352          
   353          // Get Rome to output feed as a JDOM document
   354          WireFeedOutput wireFeedOutput = new WireFeedOutput();
   355          Document feedDoc = wireFeedOutput.outputJDom(feed1);
   356          
   357          // Grab entry element from feed and get JDOM to serialize it
   358          Element entryElement= (Element)feedDoc.getRootElement().getChildren().get(0);
   359          
   360          Element rollerElement = new Element("atom-draft", 
   361              "http://roller.apache.org/namespaces/app");
   362          rollerElement.setText("14");
   363          entryElement.addContent(rollerElement);
   364          
   365          XMLOutputter outputter = new XMLOutputter();
   366          outputter.setFormat(Format.getPrettyFormat());
   367          
   368          if (log.isDebugEnabled()) {
   369              StringWriter sw = new StringWriter();
   370              outputter.output(entryElement, sw); 
   371              log.debug(sw.toString());
   372              writer.write(sw.toString()); 
   373          } else {
   374              outputter.output(entryElement, writer);
   375          } 
   376      }
   377      
   378      /**
   379       * Parse entry from reader.
   380       */
   381      public static Entry parseEntry(Reader rd) 
   382          throws JDOMException, IOException, IllegalArgumentException, FeedException {
   383          // Parse entry into JDOM tree
                 /* 
    P/P           *  Method: Entry parseEntry(Reader)
                  * 
                  *  Preconditions:
                  *    (soft) log != null
                  * 
                  *  Presumptions:
                  *    com.sun.syndication.feed.atom.Feed:getEntries(...)@405 != null
                  *    com.sun.syndication.io.WireFeedInput:build(...)@404 != null
                  *    com.sun.syndication.io.WireFeedOutput:outputJDom(...)@393 != null
                  *    org.jdom.Document:getRootElement(...)@386 != null
                  *    org.jdom.Document:getRootElement(...)@394 != null
                  *    ...
                  * 
                  *  Postconditions:
                  *    init'ed(return_value)
                  * 
                  *  Test Vectors:
                  *    org.jdom.Element:getChild(...)@398: Inverse{null}, Addr_Set{null}
                  */
   384          SAXBuilder builder = new SAXBuilder();
   385          Document entryDoc = builder.build(rd);
   386          Element fetchedEntryElement = entryDoc.getRootElement();
   387          fetchedEntryElement.detach();
   388          
   389          // Put entry into a JDOM document with 'feed' root so that Rome can handle it
   390          Feed feed = new Feed();
   391          feed.setFeedType(FEED_TYPE);
   392          WireFeedOutput wireFeedOutput = new WireFeedOutput();
   393          Document feedDoc = wireFeedOutput.outputJDom(feed);
   394          feedDoc.getRootElement().addContent(fetchedEntryElement);
   395          
   396          Namespace ns = Namespace.getNamespace(
   397              "http://roller.apache.org/namespaces/app");
   398          Element rollerElement = fetchedEntryElement.getChild("atom-draft", ns);
   399          if (rollerElement == null) {
   400              log.debug("Client is NOT preserving foreign markup");
   401          }
   402                  
   403          WireFeedInput input = new WireFeedInput();
   404          Feed parsedFeed = (Feed)input.build(feedDoc);
   405          return (Entry)parsedFeed.getEntries().get(0);
   406      }
   407  }








SofCheck Inspector Build Version : 2.18479
AtomServlet.java 2009-Jan-02 14:24:50
AtomServlet.class 2009-Sep-04 03:12:46