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 |