View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied. See the License for the
16   * specific language governing permissions and limitations under the License.
17   */
18  package org.apache.shindig.social.opensocial.service;
19  
20  import org.apache.shindig.common.testing.FakeGadgetToken;
21  import org.apache.shindig.common.testing.FakeHttpServletRequest;
22  import org.apache.shindig.common.util.ImmediateFuture;
23  import org.apache.shindig.social.ResponseError;
24  import org.apache.shindig.social.SocialApiTestsGuiceModule;
25  import org.apache.shindig.social.core.util.BeanAtomConverter;
26  import org.apache.shindig.social.core.util.BeanJsonConverter;
27  import org.apache.shindig.social.core.util.BeanXStreamAtomConverter;
28  import org.apache.shindig.social.core.util.BeanXStreamConverter;
29  import org.apache.shindig.social.core.util.BeanXmlConverter;
30  import org.apache.shindig.social.core.util.xstream.GuiceBeanProvider;
31  import org.apache.shindig.social.core.util.xstream.XStream081Configuration;
32  import org.apache.shindig.social.opensocial.spi.SocialSpiException;
33  
34  import com.google.common.collect.ImmutableMap;
35  import com.google.common.collect.Maps;
36  import com.google.inject.Guice;
37  import com.google.inject.Injector;
38  import com.google.inject.Provider;
39  
40  import junit.framework.TestCase;
41  import org.easymock.classextension.EasyMock;
42  
43  import java.io.IOException;
44  import java.io.PrintWriter;
45  import java.util.StringTokenizer;
46  import java.util.concurrent.ExecutionException;
47  import javax.servlet.ServletInputStream;
48  import javax.servlet.http.HttpServletRequest;
49  import javax.servlet.http.HttpServletResponse;
50  
51  public class DataServiceServletTest extends TestCase {
52  
53    private static final FakeGadgetToken FAKE_GADGET_TOKEN = new FakeGadgetToken()
54        .setOwnerId("john.doe").setViewerId("john.doe");
55  
56    private HttpServletRequest req;
57    private HttpServletResponse res;
58    private DataServiceServlet servlet;
59    private PersonHandler peopleHandler;
60    private ActivityHandler activityHandler;
61    private AppDataHandler appDataHandler;
62    private BeanJsonConverter jsonConverter;
63  
64    private final ServletInputStream dummyPostData = new ServletInputStream() {
65      @Override public int read()  {
66        return -1;
67      }
68    };
69  
70    @Override protected void setUp() throws Exception {
71      servlet = new DataServiceServlet();
72      req = EasyMock.createMock(HttpServletRequest.class);
73      res = EasyMock.createMock(HttpServletResponse.class);
74      jsonConverter = EasyMock.createMock(BeanJsonConverter.class);
75      BeanXStreamConverter xmlConverter = EasyMock.createMock(BeanXStreamConverter.class);
76      BeanXStreamAtomConverter atomConverter = EasyMock.createMock(BeanXStreamAtomConverter.class);
77      peopleHandler = EasyMock.createMock(PersonHandler.class);
78      activityHandler = EasyMock.createMock(ActivityHandler.class);
79      appDataHandler = EasyMock.createMock(AppDataHandler.class);
80  
81      EasyMock.expect(jsonConverter.getContentType()).andReturn("application/json");
82      EasyMock.expect(xmlConverter.getContentType()).andReturn("application/xml");
83      EasyMock.expect(atomConverter.getContentType()).andReturn("application/atom+xml");
84  
85      HandlerDispatcher dispatcher = new StandardHandlerDispatcher(constant(peopleHandler),
86          constant(activityHandler), constant(appDataHandler));
87      servlet.setHandlerDispatcher(dispatcher);
88  
89      servlet.setBeanConverters(jsonConverter, xmlConverter, atomConverter);
90    }
91  
92    // TODO: replace with Providers.of() when Guice version is upgraded
93    private static <T> Provider<T> constant(final T value) {
94      return new Provider<T>() {
95        public T get() {
96          return value;
97        }
98      };
99    }
100 
101   public void testPeopleUriRecognition() throws Exception {
102     verifyHandlerWasFoundForPathInfo('/'
103         + DataServiceServlet.PEOPLE_ROUTE + "/5/@self", peopleHandler);
104   }
105 
106   public void testActivitiesUriRecognition() throws Exception {
107     verifyHandlerWasFoundForPathInfo('/'
108         + DataServiceServlet.ACTIVITY_ROUTE + "/5/@self", activityHandler);
109   }
110 
111   public void testAppDataUriRecognition() throws Exception {
112     verifyHandlerWasFoundForPathInfo('/'
113         + DataServiceServlet.APPDATA_ROUTE + "/5/@self", appDataHandler);
114   }
115 
116   public void testMethodOverride() throws Exception {
117     String route = '/' + DataServiceServlet.APPDATA_ROUTE;
118     verifyHandlerWasFoundForPathInfo(route, appDataHandler, "POST", "GET", "GET");
119     verifyHandlerWasFoundForPathInfo(route, appDataHandler, "POST", "", "POST");
120     verifyHandlerWasFoundForPathInfo(route, appDataHandler, "POST", null, "POST");
121     verifyHandlerWasFoundForPathInfo(route, appDataHandler, "POST", "POST", "POST");
122     verifyHandlerWasFoundForPathInfo(route, appDataHandler, "GET", null, "GET");
123     verifyHandlerWasFoundForPathInfo(route, appDataHandler, "DELETE", null, "DELETE");
124     verifyHandlerWasFoundForPathInfo(route, appDataHandler, "PUT", null, "PUT");
125   }
126 
127   /***
128    * Tests a data handler that returns a failed Future
129    */
130   public void testFailedRequest() throws Exception {
131     String route = '/' + DataServiceServlet.APPDATA_ROUTE;
132     setupRequest(route, "GET", null);
133 
134     EasyMock.expect(appDataHandler.handleItem(EasyMock.isA(RestfulRequestItem.class)));
135     EasyMock.expectLastCall().andReturn(
136         ImmediateFuture.errorInstance(new RuntimeException("FAILED")));
137 
138     res.sendError(500, "FAILED");
139     res.setCharacterEncoding("UTF-8");
140     res.setContentType("application/json");
141 
142     EasyMock.replay(req, res, appDataHandler, jsonConverter);
143     servlet.service(req, res);
144     EasyMock.verify(req, res, appDataHandler, jsonConverter);
145     EasyMock.reset(req, res, appDataHandler, jsonConverter);
146   }
147 
148   private void verifyHandlerWasFoundForPathInfo(String peoplePathInfo, DataRequestHandler handler)
149       throws Exception {
150     String post = "POST";
151     verifyHandlerWasFoundForPathInfo(peoplePathInfo, handler, post, post, post);
152   }
153 
154   private void verifyHandlerWasFoundForPathInfo(String pathInfo, DataRequestHandler handler,
155       String actualMethod, String overrideMethod, String expectedMethod) throws Exception {
156     setupRequest(pathInfo, actualMethod, overrideMethod);
157 
158     String jsonObject = "my lovely json";
159 
160     EasyMock.expect(handler.handleItem(EasyMock.isA(RequestItem.class)));
161     EasyMock.expectLastCall().andReturn(ImmediateFuture.newInstance(jsonObject));
162 
163     EasyMock.expect(jsonConverter.convertToString(ImmutableMap.of("entry", jsonObject)))
164         .andReturn("{ 'entry' : " + jsonObject + " }");
165 
166     PrintWriter writerMock = EasyMock.createMock(PrintWriter.class);
167     EasyMock.expect(res.getWriter()).andReturn(writerMock);
168     writerMock.write(jsonObject);
169     res.setCharacterEncoding("UTF-8");
170     res.setContentType("application/json");
171 
172     EasyMock.replay(req, res, handler, jsonConverter);
173     servlet.service(req, res);
174     EasyMock.verify(req, res, handler, jsonConverter);
175     EasyMock.reset(req, res, handler, jsonConverter);
176     // ick, this resets for the next call...
177     EasyMock.expect(jsonConverter.getContentType()).andReturn("application/json");
178   }
179 
180   private void setupRequest(String pathInfo, String actualMethod, String overrideMethod)
181       throws IOException {
182     EasyMock.expect(req.getCharacterEncoding()).andStubReturn("UTF-8");
183     if (!("GET").equals(overrideMethod) && !("HEAD").equals(overrideMethod)) {
184       EasyMock.expect(req.getInputStream()).andStubReturn(dummyPostData);
185     }
186     EasyMock.expect(req.getPathInfo()).andStubReturn(pathInfo);
187     EasyMock.expect(req.getMethod()).andStubReturn(actualMethod);
188     EasyMock.expect(req.getParameterNames()).andStubReturn(new StringTokenizer(""));
189     EasyMock.expect(req.getParameter(RestfulRequestItem.X_HTTP_METHOD_OVERRIDE)).andReturn(
190         overrideMethod).times(2);
191     EasyMock.expect(req.getParameter(DataServiceServlet.FORMAT_PARAM)).andReturn(null);
192 
193     EasyMock.expect(req.getAttribute(EasyMock.isA(String.class))).andReturn(FAKE_GADGET_TOKEN);
194   }
195 
196   public void testInvalidRoute() throws Exception {
197     RestfulRequestItem requestItem = new RestfulRequestItem("/ahhh!", "GET", null,
198         FAKE_GADGET_TOKEN, jsonConverter);
199     try {
200       servlet.handleRequestItem(requestItem, new FakeHttpServletRequest()).get();
201       fail();
202     } catch (ExecutionException ee) {
203       assertTrue(ee.getCause() instanceof SocialSpiException);
204       assertEquals(ResponseError.NOT_IMPLEMENTED, ((SocialSpiException) ee.getCause()).getError());
205     }
206   }
207 
208   public void testGetConverterForRequest() throws Exception {
209 
210     Injector injector = Guice.createInjector(new SocialApiTestsGuiceModule());
211     BeanJsonConverter json = new BeanJsonConverter(injector);
212     BeanXStreamConverter xml = new BeanXStreamConverter(new XStream081Configuration(injector));
213     BeanXStreamAtomConverter atom = new BeanXStreamAtomConverter(new XStream081Configuration(injector));
214     servlet.setBeanConverters(json, xml, atom);
215 
216     assertConverter(atom, "atom");
217     assertConverter(xml, "xml");
218     assertConverter(json, "");
219     assertConverter(json, null);
220     assertConverter(json, "ahhhh!");
221   }
222 
223   public void testGetConverterForRequestContentType() throws Exception {
224     Injector injector = Guice.createInjector(new SocialApiTestsGuiceModule());
225     BeanJsonConverter json = new BeanJsonConverter(injector);
226     BeanXStreamConverter xml = new BeanXStreamConverter(new XStream081Configuration(injector));
227     BeanXStreamAtomConverter atom = new BeanXStreamAtomConverter(new XStream081Configuration(injector));
228     servlet.setBeanConverters(json, xml, atom);
229 
230     assertConverterForContentType(atom, "application/atom+xml");
231     assertConverterForContentType(xml, "application/xml");
232     assertConverterForContentType(json, "");
233     assertConverterForContentType(json, null);
234     assertConverterForContentType(json, "abcd!");
235 
236   }
237 
238   private void assertConverter(BeanConverter converter, String format) {
239     EasyMock.expect(req.getParameter(DataServiceServlet.FORMAT_PARAM))
240         .andReturn(format);
241     EasyMock.replay(req);
242     assertEquals(converter, servlet.getConverterForRequest(req));
243     EasyMock.verify(req);
244     EasyMock.reset(req);
245   }
246 
247   private void assertConverterForContentType(BeanConverter converter,
248       String contentType) {
249     EasyMock.expect(req.getHeader(DataServiceServlet.CONTENT_TYPE)).andReturn(
250         contentType);
251     EasyMock.replay(req);
252     assertEquals(converter, servlet.getConverterForRequest(req));
253     EasyMock.verify(req);
254     EasyMock.reset(req);
255   }
256 }