View Javadoc

1   /*
2    * $Id: RestActionInvocation.java 651946 2008-04-27 13:41:38Z apetrelli $
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  package org.apache.struts2.rest;
23  
24  import com.opensymphony.xwork2.ActionContext;
25  import com.opensymphony.xwork2.ActionEventListener;
26  import com.opensymphony.xwork2.ActionInvocation;
27  import com.opensymphony.xwork2.ActionProxy;
28  import com.opensymphony.xwork2.DefaultActionInvocation;
29  import com.opensymphony.xwork2.ModelDriven;
30  import com.opensymphony.xwork2.ObjectFactory;
31  import com.opensymphony.xwork2.Result;
32  import com.opensymphony.xwork2.UnknownHandler;
33  import com.opensymphony.xwork2.config.ConfigurationException;
34  import com.opensymphony.xwork2.config.entities.ActionConfig;
35  import com.opensymphony.xwork2.config.entities.InterceptorMapping;
36  import com.opensymphony.xwork2.config.entities.ResultConfig;
37  import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
38  import com.opensymphony.xwork2.inject.Container;
39  import com.opensymphony.xwork2.inject.Inject;
40  import com.opensymphony.xwork2.interceptor.PreResultListener;
41  import com.opensymphony.xwork2.util.ValueStack;
42  import com.opensymphony.xwork2.util.ValueStackFactory;
43  import com.opensymphony.xwork2.util.logging.LoggerFactory;
44  import com.opensymphony.xwork2.util.profiling.UtilTimerStack;
45  import com.opensymphony.xwork2.util.logging.Logger;
46  
47  import org.apache.struts2.ServletActionContext;
48  import org.apache.struts2.rest.handler.ContentTypeHandler;
49  
50  import java.io.ByteArrayOutputStream;
51  import java.io.IOException;
52  import java.lang.reflect.InvocationTargetException;
53  import java.lang.reflect.Method;
54  import java.lang.reflect.Proxy;
55  import java.util.ArrayList;
56  import java.util.Collections;
57  import java.util.HashMap;
58  import java.util.HashSet;
59  import java.util.Iterator;
60  import java.util.LinkedHashMap;
61  import java.util.List;
62  import java.util.Map;
63  import java.util.Set;
64  
65  import javax.servlet.http.HttpServletRequest;
66  import javax.servlet.http.HttpServletResponse;
67  
68  
69  /***
70   * Extends the usual {@link ActionInvocation} to add support for processing the object returned
71   * from the action execution.  This allows us to support methods that return {@link HttpHeaders}
72   * as well as apply content type-specific operations to the result.
73   */
74  public class RestActionInvocation extends DefaultActionInvocation {
75      
76      private static final long serialVersionUID = 3485701178946428716L;
77  
78      private static final Logger LOG = LoggerFactory.getLogger(RestActionInvocation.class);
79      
80      private ContentTypeHandlerManager handlerSelector;
81  
82      protected RestActionInvocation(Map extraContext, boolean pushAction) {
83          super(extraContext, pushAction);
84      }
85  
86      @Inject
87      public void setMimeTypeHandlerSelector(ContentTypeHandlerManager sel) {
88          this.handlerSelector = sel;
89      }
90      
91      protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
92          String methodName = proxy.getMethod();
93  
94          if (LOG.isDebugEnabled()) {
95              LOG.debug("Executing action method = " + actionConfig.getMethodName());
96          }
97  
98          String timerKey = "invokeAction: "+proxy.getActionName();
99          try {
100             UtilTimerStack.push(timerKey);
101             
102             boolean methodCalled = false;
103             Object methodResult = null;
104             Method method = null;
105             try {
106                 method = getAction().getClass().getMethod(methodName, new Class[0]);
107             } catch (NoSuchMethodException e) {
108                 // hmm -- OK, try doXxx instead
109                 try {
110                     String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
111                     method = getAction().getClass().getMethod(altMethodName, new Class[0]);
112                 } catch (NoSuchMethodException e1) {
113                     // well, give the unknown handler a shot
114                     if (unknownHandler != null) {
115                         try {
116                             methodResult = unknownHandler.handleUnknownActionMethod(action, methodName);
117                             methodCalled = true;
118                         } catch (NoSuchMethodException e2) {
119                             // throw the original one
120                             throw e;
121                         }
122                     } else {
123                         throw e;
124                     }
125                 }
126             }
127             
128             if (!methodCalled) {
129                 methodResult = method.invoke(action, new Object[0]);
130             }
131             
132             return processResult(actionConfig, methodResult);
133         } catch (NoSuchMethodException e) {
134             throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");
135         } catch (InvocationTargetException e) {
136             // We try to return the source exception.
137             Throwable t = e.getTargetException();
138 
139             if (actionEventListener != null) {
140                 String result = actionEventListener.handleException(t, getStack());
141                 if (result != null) {
142                     return result;
143                 }
144             }
145             if (t instanceof Exception) {
146                 throw(Exception) t;
147             } else {
148                 throw e;
149             }
150         } finally {
151             UtilTimerStack.pop(timerKey);
152         }
153     }
154 
155     protected String processResult(ActionConfig actionConfig, Object methodResult) throws IOException {
156         if (methodResult instanceof Result) {
157             this.explicitResult = (Result) methodResult;
158             return null;
159         } else if (methodResult != null) {
160             resultCode = handlerSelector.handleResult(actionConfig, methodResult, action);
161         }
162         return resultCode;
163     }
164 
165 }