Oleg Atamanenko
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
@Path("/service/entity")
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public interface EntityRestService {
@GET
@Path("/all")
EntityList listAll();
@GET
@Path("/{id}")
Entity findById(@PathParam("id") Integer id);
}
Создаём интерфейс, в котором расставляем JAX-RS аннотации. Аннотация @Path указывает путь, по которому будет доступен наш сервис. Аннотация @GET определяет, какой HTTP-запрос будет обрабатываться данным методом. Аннотация @Produces позволяет указать, в каком формате данный сервис предоставляет результаты.
Конфигурация очень проста, во-первых, нам нужно добавить обновить pom.xml и добавить необходимые зависимости:
Добавляем compile-time зависимость на JAX-RS API
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jaxrs-api</artifactId>
</dependency>
и runtime зависимости на реализацию
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>${resteasy-jaxrs.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson-provider</artifactId>
<version>${resteasy-jaxrs.version}</version>
<scope>runtime</scope>
</dependency>
<listener>
<listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
</listener>
<context-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/rest</param-value>
</context-param>
<servlet>
<servlet-name>REST Easy</servlet-name>
<servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>REST Easy</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
Данный код объявляет сервлет HttpServletDispatcher, который будет обрабатывать все запросы, которые приходят на /rest/*. Слушатель ResteasyBootstrap выполняет всю необходимую инициализацию JBoss RESTEasy.
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-spring</artifactId>
<version>${resteasy-jaxrs.version}</version>
<scope>runtime</scope>
</dependency>
<listener>
<listener-class>org.jboss.resteasy.plugins.spring.SpringContextLoaderListener</listener-class>
</listener>
После этих изменений JBoss RESTEasy будет в курсе про Spring, это позволит в реализации REST-сервисов полноценно использовать все возможности, предоставляемые Spring Framework.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"/applicationContext-test.xml"})
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class})
public class TestEntityServiceRest {
private static final int PORT = 8081;
private static final String BASE_URL = "http://localhost:" + PORT;
@Autowired
EntityService entityService;
@Autowired
ConfigurableApplicationContext applicationContext;
protected HttpClient client;
protected TJWSEmbeddedJaxrsServer server;
@Before
public void setUpClient() throws Exception {
client = new DefaultHttpClient();
}
@Before
public void setUpServer() throws Exception {
server = new TJWSEmbeddedJaxrsServer();
server.setPort(PORT);
ResteasyDeployment deployment = server.getDeployment();
server.start();
Dispatcher dispatcher = deployment.getDispatcher();
SpringBeanProcessor processor = new SpringBeanProcessor(dispatcher, deployment.getRegistry(), deployment.getProviderFactory());
applicationContext.addBeanFactoryPostProcessor(processor);
SpringResourceFactory noDefaults = new SpringResourceFactory(
"entityServiceRestImpl", applicationContext, EntityRestServiceImpl.class);
dispatcher.getRegistry().addResourceFactory(noDefaults);
}
@After
public void stop() {
server.stop();
}
@Test
public void testListAll() throws Exception {
int i = 0;
final List<Entity> returnedList = new ArrayList<Entity>();
int EXPECTED_SIZE = 6;
while (i < EXPECTED_SIZE) {
i++;
final Entity entity = new Entity();
entity.setId(i);
entity.setName("test" + i);
returnedList.add(entity);
}
when(entityService.findAll()).thenReturn(returnedList);
HttpGet get = new HttpGet(BASE_URL + "/service/entity/");
HttpResponse response = client.execute(get);
InputStream content = response.getEntity().getContent();
EntityList result = fromString(EntityList.class, content);
content.close();
Assert.assertNotNull(result);
Assert.assertEquals(EXPECTED_SIZE, result.getEntities().size());
}
@Test
public void testFindById() throws Exception {
final Entity validEntity = getValidEntity();
validEntity.setId(2);
when(entityService.findById(validEntity.getId())).thenAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return validEntity;
}
});
HttpGet get = new HttpGet(BASE_URL + "/service/entity/2");
HttpResponse response = client.execute(get);
InputStream content = response.getEntity().getContent();
Entity result = fromString(Entity.class, content);
content.close();
Assert.assertNotNull(result);
deepAssertEquals(validEntity, result);
}
private void deepAssertEquals(Object expected, Object actual) throws IllegalAccessException, InvocationTargetException {
Assert.assertEquals(expected.getClass(), actual.getClass());
PropertyDescriptor[] propertyDescriptors = BeanUtils.getPropertyDescriptors(expected.getClass());
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
Object expectedValue = propertyDescriptor.getReadMethod().invoke(expected);
Object actualValue = propertyDescriptor.getReadMethod().invoke(actual);
Assert.assertEquals(expectedValue, actualValue);
}
}
public static <T> T fromString(Class<T> clazz, String input) throws JAXBException {
JAXBContext ctx = JAXBContext.newInstance(clazz);
Unmarshaller unmarshaller = ctx.createUnmarshaller();
@SuppressWarnings("unchecked")
T unmarshal = (T) unmarshaller.unmarshal(new StringReader(input));
return unmarshal;
}
public static <T> T fromString(Class<T> clazz, InputStream input) throws JAXBException {
JAXBContext ctx = JAXBContext.newInstance(clazz);
@SuppressWarnings("unchecked")
T unmarshal = (T) ctx.createUnmarshaller().unmarshal(input);
return unmarshal;
}
}
Для этого нам необходимо внести следующие модификации в pom.xml:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.11</version>
<executions>
<execution>
<id>Unit tests</id>
<phase>test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<excludes>
<exclude>**/IT*.java</exclude>
</excludes>
</configuration>
</execution>
<execution>
<id>Integration tests</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<includes>
<include>**/IT*.java</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.1.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-spring</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>tjws</artifactId>
<version>${resteasy-jaxrs.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<scope>test</scope>
</dependency>