001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.invocation.http.servlet;
023:
024: import java.io.IOException;
025: import java.io.ObjectInputStream;
026: import java.io.ObjectOutputStream;
027: import java.lang.reflect.InvocationTargetException;
028: import java.security.PrivilegedAction;
029: import java.security.Principal;
030: import java.security.AccessController;
031: import javax.management.MalformedObjectNameException;
032: import javax.management.MBeanServer;
033: import javax.management.ObjectName;
034: import javax.servlet.ServletConfig;
035: import javax.servlet.ServletException;
036: import javax.servlet.ServletInputStream;
037: import javax.servlet.ServletOutputStream;
038: import javax.servlet.http.HttpServlet;
039: import javax.servlet.http.HttpServletRequest;
040: import javax.servlet.http.HttpServletResponse;
041:
042: import org.jboss.invocation.InvocationException;
043: import org.jboss.invocation.MarshalledInvocation;
044: import org.jboss.invocation.MarshalledValue;
045: import org.jboss.logging.Logger;
046: import org.jboss.mx.util.MBeanServerLocator;
047: import org.jboss.mx.util.JMXExceptionDecoder;
048: import org.jboss.system.Registry;
049: import org.jboss.security.SecurityAssociation;
050:
051: /** This servlet accepts a post containing a MarshalledInvocation, extracts
052: the Invocation object, and then routes the invocation via JMX to either:
053: 1. the MBean specified via the invokerName ini parameter
054: 2. the MBean whose object name hash is specified by the invocation.getObjectName()
055: value. This name's hash must have been entered into the Registry.
056:
057: The method signature of the invoker must be Object invoke(org.jboss.invocation.Invocation).
058:
059: @see org.jboss.system.Registry
060: @see org.jboss.invocation.Invocation
061:
062: * @author Scott.Stark@jboss.org
063: * @version $Revision: 57210 $
064: */
065: public class InvokerServlet extends HttpServlet {
066: private static Logger log = Logger.getLogger(InvokerServlet.class);
067: /** A serialized MarshalledInvocation */
068: private static String REQUEST_CONTENT_TYPE = "application/x-java-serialized-object; class=org.jboss.invocation.MarshalledInvocation";
069: /** A serialized MarshalledValue */
070: private static String RESPONSE_CONTENT_TYPE = "application/x-java-serialized-object; class=org.jboss.invocation.MarshalledValue";
071: private MBeanServer mbeanServer;
072: private ObjectName localInvokerName;
073:
074: /** Initializes the servlet.
075: */
076: public void init(ServletConfig config) throws ServletException {
077: super .init(config);
078: try {
079: // See if the servlet is bound to a particular invoker
080: String name = config.getInitParameter("invokerName");
081: if (name != null) {
082: localInvokerName = new ObjectName(name);
083: log.debug("localInvokerName=" + localInvokerName);
084: }
085: } catch (MalformedObjectNameException e) {
086: throw new ServletException("Failed to build invokerName", e);
087: }
088:
089: // Lookup the MBeanServer
090: mbeanServer = MBeanServerLocator.locateJBoss();
091: if (mbeanServer == null)
092: throw new ServletException(
093: "Failed to locate the MBeanServer");
094: }
095:
096: /** Destroys the servlet.
097: */
098: public void destroy() {
099:
100: }
101:
102: /** Read a MarshalledInvocation and dispatch it to the target JMX object
103: invoke(Invocation) object.
104:
105: @param request servlet request
106: @param response servlet response
107: */
108: protected void processRequest(HttpServletRequest request,
109: HttpServletResponse response) throws ServletException,
110: IOException {
111: boolean trace = log.isTraceEnabled();
112: if (trace) {
113: log.trace("processRequest, ContentLength: "
114: + request.getContentLength());
115: log.trace("processRequest, ContentType: "
116: + request.getContentType());
117: }
118:
119: Boolean returnValueAsAttribute = (Boolean) request
120: .getAttribute("returnValueAsAttribute");
121: try {
122: response.setContentType(RESPONSE_CONTENT_TYPE);
123: // See if the request already has the MarshalledInvocation
124: MarshalledInvocation mi = (MarshalledInvocation) request
125: .getAttribute("MarshalledInvocation");
126: if (mi == null) {
127: // Get the invocation from the post
128: ServletInputStream sis = request.getInputStream();
129: ObjectInputStream ois = new ObjectInputStream(sis);
130: mi = (MarshalledInvocation) ois.readObject();
131: ois.close();
132: }
133: /* If the invocation carries no auth context, look to to the auth
134: context of this servlet as seen in the SecurityAssocation. This allows
135: the web app authentication to transparently be used as the call
136: authentication.
137: */
138: if (mi.getPrincipal() == null && mi.getCredential() == null) {
139: mi.setPrincipal(GetPrincipalAction.getPrincipal());
140: mi.setCredential(GetCredentialAction.getCredential());
141: }
142: Object[] params = { mi };
143: String[] sig = { "org.jboss.invocation.Invocation" };
144: ObjectName invokerName = localInvokerName;
145: // If there is no associated invoker, get the name from the invocation
146: if (invokerName == null) {
147: Integer nameHash = (Integer) mi.getObjectName();
148: invokerName = (ObjectName) Registry.lookup(nameHash);
149: if (invokerName == null)
150: throw new ServletException(
151: "Failed to find invoker name for hash("
152: + nameHash + ")");
153: }
154: // Forward the invocation onto the JMX invoker
155: Object value = mbeanServer.invoke(invokerName, "invoke",
156: params, sig);
157: if (returnValueAsAttribute == null
158: || returnValueAsAttribute.booleanValue() == false) {
159: MarshalledValue mv = new MarshalledValue(value);
160: ServletOutputStream sos = response.getOutputStream();
161: ObjectOutputStream oos = new ObjectOutputStream(sos);
162: oos.writeObject(mv);
163: oos.close();
164: } else {
165: request.setAttribute("returnValue", value);
166: }
167: } catch (Throwable t) {
168: t = JMXExceptionDecoder.decode(t);
169: // Unwrap any reflection InvocationTargetExceptions
170: if (t instanceof InvocationTargetException) {
171: InvocationTargetException ite = (InvocationTargetException) t;
172: t = ite.getTargetException();
173: }
174: /* Wrap the exception in an InvocationException to distinguish
175: between application and transport exceptions
176: */
177: InvocationException appException = new InvocationException(
178: t);
179: log.debug("Invoke threw exception", t);
180: // Marshall the exception
181: if (returnValueAsAttribute == null
182: || returnValueAsAttribute.booleanValue() == false) {
183: response.resetBuffer();
184: MarshalledValue mv = new MarshalledValue(appException);
185: ServletOutputStream sos = response.getOutputStream();
186: ObjectOutputStream oos = new ObjectOutputStream(sos);
187: oos.writeObject(mv);
188: oos.close();
189: } else {
190: request.setAttribute("returnValue", appException);
191: }
192: }
193: }
194:
195: /** Handles the HTTP <code>GET</code> method.
196: * @param request servlet request
197: * @param response servlet response
198: */
199: protected void doGet(HttpServletRequest request,
200: HttpServletResponse response) throws ServletException,
201: IOException {
202: processRequest(request, response);
203: }
204:
205: /** Handles the HTTP <code>POST</code> method.
206: * @param request servlet request
207: * @param response servlet response
208: */
209: protected void doPost(HttpServletRequest request,
210: HttpServletResponse response) throws ServletException,
211: IOException {
212: processRequest(request, response);
213: }
214:
215: /** Returns a short description of the servlet.
216: */
217: public String getServletInfo() {
218: return "An HTTP to JMX invocation servlet";
219: }
220:
221: private static class GetPrincipalAction implements PrivilegedAction {
222: static PrivilegedAction ACTION = new GetPrincipalAction();
223:
224: public Object run() {
225: Principal principal = SecurityAssociation.getPrincipal();
226: return principal;
227: }
228:
229: static Principal getPrincipal() {
230: Principal principal = (Principal) AccessController
231: .doPrivileged(ACTION);
232: return principal;
233: }
234: }
235:
236: private static class GetCredentialAction implements
237: PrivilegedAction {
238: static PrivilegedAction ACTION = new GetCredentialAction();
239:
240: public Object run() {
241: Object credential = SecurityAssociation.getCredential();
242: return credential;
243: }
244:
245: static Object getCredential() {
246: Object credential = AccessController.doPrivileged(ACTION);
247: return credential;
248: }
249: }
250: }
|