001: /**
002: * Copyright (c) 2003-2007, David A. Czarnecki
003: * All rights reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions are met:
007: *
008: * Redistributions of source code must retain the above copyright notice, this list of conditions and the
009: * following disclaimer.
010: * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
011: * following disclaimer in the documentation and/or other materials provided with the distribution.
012: * Neither the name of "David A. Czarnecki" and "blojsom" nor the names of its contributors may be used to
013: * endorse or promote products derived from this software without specific prior written permission.
014: * Products derived from this software may not be called "blojsom", nor may "blojsom" appear in their name,
015: * without prior written permission of David A. Czarnecki.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
018: * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
019: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
020: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
021: * EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
022: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
025: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
026: * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
027: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
028: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
029: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
030: */package org.blojsom.extension.xmlrpc;
031:
032: import org.apache.commons.logging.Log;
033: import org.apache.commons.logging.LogFactory;
034: import org.apache.xmlrpc.XmlRpc;
035: import org.apache.xmlrpc.XmlRpcServer;
036: import org.blojsom.blog.Blog;
037: import org.blojsom.extension.xmlrpc.handler.APIHandler;
038: import org.blojsom.fetcher.Fetcher;
039: import org.blojsom.fetcher.FetcherException;
040: import org.blojsom.servlet.ServletConfigFactoryBean;
041: import org.blojsom.util.BlojsomConstants;
042: import org.blojsom.util.BlojsomUtils;
043: import org.springframework.context.support.ClassPathXmlApplicationContext;
044:
045: import javax.servlet.ServletConfig;
046: import javax.servlet.ServletException;
047: import javax.servlet.http.HttpServlet;
048: import javax.servlet.http.HttpServletRequest;
049: import javax.servlet.http.HttpServletResponse;
050: import java.io.IOException;
051: import java.io.OutputStream;
052: import java.io.PrintWriter;
053: import java.io.UnsupportedEncodingException;
054: import java.net.HttpURLConnection;
055: import java.util.Iterator;
056: import java.util.Properties;
057:
058: /**
059: * Blojsom XML-RPC Servlet
060: * <p/>
061: * This servlet uses the Jakarta XML-RPC Library (http://ws.apache.org/xmlrpc)
062: *
063: * @author Mark Lussier
064: * @author David Czarnecki
065: * @since blojsom 3.0
066: * @version $Id: BlojsomXMLRPCServlet.java,v 1.5 2007/01/17 02:35:07 czarneckid Exp $
067: */
068: public class BlojsomXMLRPCServlet extends HttpServlet {
069:
070: protected Log _logger = LogFactory
071: .getLog(BlojsomXMLRPCServlet.class);
072:
073: protected String[] BLOJSOM_CONFIGURATION_FILES = { "blojsom-xmlrpc.xml" };
074:
075: protected static final int XMLRPC_DISABLED = 4000;
076: protected static final String XMLRPC_DISABLED_MESSAGE = "XML-RPC disabled for the requested blog";
077: protected static final String XMLRPC_ACCEPTS_ONLY_POSTS_MESSAGE = "XML-RPC server only accepts POST requests.";
078:
079: protected ClassPathXmlApplicationContext _classPathXmlApplicationContext;
080:
081: /**
082: * Construct a new Blojsom XML-RPC servlet instance
083: */
084: public BlojsomXMLRPCServlet() {
085: }
086:
087: /**
088: * Initialize the blojsom XML-RPC servlet
089: *
090: * @param servletConfig Servlet configuration information
091: * @throws ServletException If there is an error initializing the servlet
092: */
093: public void init(ServletConfig servletConfig)
094: throws ServletException {
095: super .init(servletConfig);
096:
097: ServletConfigFactoryBean.setServletConfig(servletConfig);
098:
099: _classPathXmlApplicationContext = new ClassPathXmlApplicationContext(
100: BLOJSOM_CONFIGURATION_FILES);
101: servletConfig.getServletContext().setAttribute(
102: BlojsomConstants.BLOJSOM_XMLRPC_APPLICATION_CONTEXT,
103: _classPathXmlApplicationContext);
104:
105: // Set the default encoding for the XmlRpc classes to UTF-8
106: XmlRpc.setEncoding(BlojsomConstants.UTF8);
107:
108: if (_logger.isDebugEnabled()) {
109: _logger
110: .debug("blojsom XML-RPC: All Your Blog Are Belong To Us");
111: }
112: }
113:
114: /**
115: * Configure the XML-RPC server for the given blog
116: *
117: * @param httpServletRequest {@link HttpServletRequest}
118: * @param httpServletResponse {@link HttpServletResponse}
119: * @param blogId Blog ID
120: * @return {@link XmlRpcServer} instance with handlers configured for the given blog
121: */
122: protected XmlRpcServer configureXMLRPCServer(
123: HttpServletRequest httpServletRequest,
124: HttpServletResponse httpServletResponse, String blogId) {
125: XmlRpcServer xmlRpcServer = new XmlRpcServer();
126:
127: Fetcher fetcher = (Fetcher) _classPathXmlApplicationContext
128: .getBean("fetcher");
129: Properties defaultProperties = (Properties) _classPathXmlApplicationContext
130: .getBean("defaultProperties");
131:
132: Blog blog;
133: try {
134: blog = fetcher.loadBlog(blogId);
135: } catch (FetcherException e) {
136: if (_logger.isErrorEnabled()) {
137: _logger.error(e);
138: }
139:
140: return null;
141: }
142:
143: if ("true".equals(blog
144: .getProperty(BlojsomConstants.USE_DYNAMIC_BLOG_URLS))) {
145: String servletPath = defaultProperties
146: .getProperty(BlojsomXMLRPCConstants.SERVLET_PATH_IP);
147: if (BlojsomUtils.checkNullOrBlank(servletPath)) {
148: servletPath = BlojsomXMLRPCConstants.DEFAULT_SERVLET_PATH;
149: }
150:
151: BlojsomUtils.resolveDynamicBaseAndBlogURL(
152: httpServletRequest, blog, blogId, servletPath);
153: }
154:
155: // Check to see if XML-RPC is enabled for the blog
156: if (!blog.getXmlrpcEnabled().booleanValue()) {
157: if (_logger.isErrorEnabled()) {
158: _logger.error(XMLRPC_DISABLED_MESSAGE);
159: }
160:
161: return null;
162: }
163:
164: Properties xmlrpcProperties = (Properties) _classPathXmlApplicationContext
165: .getBean("xmlrpcProperties");
166: String defaultXMLRPCHandler = xmlrpcProperties
167: .getProperty(BlojsomXMLRPCConstants.DEFAULT_XMLRPC_HANDLER_KEY);
168: xmlrpcProperties
169: .remove(BlojsomXMLRPCConstants.DEFAULT_XMLRPC_HANDLER_KEY);
170:
171: Iterator handlerIterator = xmlrpcProperties.keySet().iterator();
172: while (handlerIterator.hasNext()) {
173: String handlerName = (String) handlerIterator.next();
174: APIHandler apiHandler = (APIHandler) _classPathXmlApplicationContext
175: .getBean(handlerName);
176: apiHandler.setProperties(defaultProperties);
177: apiHandler.setHttpServletRequest(httpServletRequest);
178: apiHandler.setHttpServletResponse(httpServletResponse);
179: apiHandler.setBlog(blog);
180:
181: xmlRpcServer.addHandler(handlerName, apiHandler);
182:
183: if (defaultXMLRPCHandler != null
184: && defaultXMLRPCHandler.equals(handlerName)) {
185: if (_logger.isDebugEnabled()) {
186: _logger.debug("Added default XML-RPC handler: "
187: + apiHandler.getClass().getName()
188: + " for blog: " + blog.getBlogId());
189: }
190: xmlRpcServer
191: .addHandler(
192: BlojsomXMLRPCConstants.DEFAULT_XMLRPC_HANDLER_KEY,
193: apiHandler);
194: }
195:
196: if (_logger.isDebugEnabled()) {
197: _logger.debug("Added [" + handlerName
198: + "] API Handler : "
199: + apiHandler.getClass().getName()
200: + " for blog: " + blog.getBlogId());
201: }
202: }
203:
204: return xmlRpcServer;
205: }
206:
207: /**
208: * Service an XML-RPC request by passing the request to the proper handler
209: *
210: * @param httpServletRequest Request
211: * @param httpServletResponse Response
212: * @throws ServletException If there is an error processing the request
213: * @throws IOException If there is an error during I/O
214: */
215: protected void service(HttpServletRequest httpServletRequest,
216: HttpServletResponse httpServletResponse)
217: throws ServletException, IOException {
218: try {
219: httpServletRequest
220: .setCharacterEncoding(BlojsomConstants.UTF8);
221: } catch (UnsupportedEncodingException e) {
222: _logger.error(e);
223: }
224:
225: if (!"post".equalsIgnoreCase(httpServletRequest.getMethod())) {
226: httpServletResponse
227: .setContentType("text/html; charset=UTF-8");
228: httpServletResponse
229: .setContentLength(XMLRPC_ACCEPTS_ONLY_POSTS_MESSAGE
230: .length());
231: httpServletResponse
232: .setStatus(HttpURLConnection.HTTP_BAD_METHOD);
233: PrintWriter printWriter = httpServletResponse.getWriter();
234: printWriter.print(XMLRPC_ACCEPTS_ONLY_POSTS_MESSAGE);
235: printWriter.flush();
236: } else {
237: // Determine the appropriate blog from the URL
238: String blogId = BlojsomUtils
239: .getBlogFromPath(httpServletRequest.getPathInfo());
240: if (BlojsomUtils.checkNullOrBlank(blogId)
241: || "/".equals(blogId)) {
242: httpServletResponse.sendError(
243: HttpServletResponse.SC_NOT_FOUND,
244: "Requested blog not found: " + blogId);
245:
246: return;
247: }
248:
249: // Make sure that the blog exists in the system
250: XmlRpcServer xmlRpcServer = configureXMLRPCServer(
251: httpServletRequest, httpServletResponse, blogId);
252: if (xmlRpcServer == null) {
253: httpServletResponse.sendError(
254: HttpServletResponse.SC_NOT_FOUND,
255: "Unable to load blog: " + blogId);
256:
257: return;
258: }
259:
260: byte[] result = xmlRpcServer.execute(httpServletRequest
261: .getInputStream());
262: httpServletResponse
263: .setContentType("text/xml; charset=UTF-8");
264: httpServletResponse.setContentLength(result.length);
265: OutputStream os = httpServletResponse.getOutputStream();
266: os.write(result);
267: os.flush();
268: }
269: }
270:
271: /**
272: * Called when removing the servlet from the servlet container
273: */
274: public void destroy() {
275: super .destroy();
276:
277: _classPathXmlApplicationContext.destroy();
278:
279: if (_logger.isDebugEnabled()) {
280: _logger.debug("blojsom XML-RPC destroyed");
281: }
282: }
283: }
|