├── .gitignore ├── pom.xml └── src ├── main ├── java │ └── org │ │ └── springframework │ │ └── hateoas │ │ └── sample │ │ ├── ApplicationConfig.java │ │ ├── SampleWebApplicationInitializer.java │ │ ├── core │ │ ├── Customer.java │ │ ├── Customers.java │ │ ├── Order.java │ │ ├── Orders.java │ │ └── package-info.java │ │ ├── hateoas │ │ ├── CustomerController.java │ │ ├── HypermediaConfiguration.java │ │ ├── OrderController.java │ │ └── package-info.java │ │ ├── package-info.java │ │ └── web │ │ ├── CustomerController.java │ │ ├── OrderController.java │ │ ├── Snippets.java │ │ └── package-info.java └── resources │ └── logback.xml └── test └── java └── org └── springframework └── hateoas └── sample ├── AbstractWebIntegrationTest.java ├── hateoas └── ApplicationIntegrationTest.java └── web └── ApplicationIntegrationTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .settings/ 3 | .project 4 | .classpath 5 | .springBeans 6 | .sonar4clipse 7 | *.sonar4clipseExternals -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | org.springframework.hateoas 6 | spring-hateoas-sample 7 | 1.0.0.BUILD-SNAPSHOT 8 | war 9 | 10 | Spring HATEOAS - Sample 11 | 12 | 13 | 3.2.2.RELEASE 14 | 15 | 16 | 17 | 18 | 19 | org.springframework.hateoas 20 | spring-hateoas 21 | 0.7.0.RELEASE 22 | 23 | 24 | 25 | org.springframework 26 | spring-webmvc 27 | ${spring} 28 | 29 | 30 | 31 | org.springframework 32 | spring-web 33 | ${spring} 34 | 35 | 36 | 37 | org.springframework 38 | spring-core 39 | ${spring} 40 | 41 | 42 | 43 | org.springframework.plugin 44 | spring-plugin-core 45 | 0.8.0.RELEASE 46 | 47 | 48 | 49 | com.fasterxml.jackson.core 50 | jackson-databind 51 | 2.1.4 52 | 53 | 54 | 55 | javax.servlet 56 | javax.servlet-api 57 | 3.0.1 58 | provided 59 | 60 | 61 | 62 | org.projectlombok 63 | lombok 64 | 0.11.8 65 | provided 66 | 67 | 68 | 69 | 70 | org.slf4j 71 | jcl-over-slf4j 72 | 1.7.2 73 | runtime 74 | 75 | 76 | 77 | ch.qos.logback 78 | logback-classic 79 | 1.0.9 80 | runtime 81 | 82 | 83 | 84 | 85 | org.springframework 86 | spring-test 87 | ${spring} 88 | test 89 | 90 | 91 | 92 | junit 93 | junit 94 | 4.11 95 | test 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | org.apache.maven.plugins 105 | maven-compiler-plugin 106 | 3.0 107 | 108 | 1.7 109 | 1.7 110 | 111 | 112 | 113 | 114 | org.apache.maven.plugins 115 | maven-war-plugin 116 | 2.3 117 | 118 | false 119 | 120 | 121 | 122 | 123 | org.eclipse.jetty 124 | jetty-maven-plugin 125 | 9.0.0.v20130308 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/hateoas/sample/ApplicationConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.hateoas.sample; 17 | 18 | import org.springframework.context.annotation.ComponentScan; 19 | import org.springframework.context.annotation.Configuration; 20 | import org.springframework.http.MediaType; 21 | import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer; 22 | import org.springframework.web.servlet.config.annotation.EnableWebMvc; 23 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 24 | 25 | /** 26 | * Spring JavaConfig configuration class. Enabling component scanning and basic Spring MVC configuration. 27 | * 28 | * @author Oliver Gierke 29 | */ 30 | @Configuration 31 | @ComponentScan 32 | @EnableWebMvc 33 | class ApplicationConfig extends WebMvcConfigurerAdapter { 34 | 35 | /* 36 | * (non-Javadoc) 37 | * @see org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter#configureContentNegotiation(org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer) 38 | */ 39 | @Override 40 | public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { 41 | configurer.defaultContentType(MediaType.APPLICATION_JSON); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/hateoas/sample/SampleWebApplicationInitializer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.hateoas.sample; 17 | 18 | import org.springframework.web.WebApplicationInitializer; 19 | import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; 20 | 21 | /** 22 | * {@link WebApplicationInitializer} to bootstrap the app as Servlet 3 application. 23 | * 24 | * @author Oliver Gierke 25 | */ 26 | public class SampleWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { 27 | 28 | /* 29 | * (non-Javadoc) 30 | * @see org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer#getRootConfigClasses() 31 | */ 32 | @Override 33 | protected Class[] getRootConfigClasses() { 34 | return null; 35 | } 36 | 37 | /* (non-Javadoc) 38 | * @see org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer#getServletConfigClasses() 39 | */ 40 | @Override 41 | protected Class[] getServletConfigClasses() { 42 | return new Class[] { ApplicationConfig.class }; 43 | } 44 | 45 | /* 46 | * (non-Javadoc) 47 | * @see org.springframework.web.servlet.support.AbstractDispatcherServletInitializer#getServletMappings() 48 | */ 49 | @Override 50 | protected String[] getServletMappings() { 51 | return new String[] { "/" }; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/hateoas/sample/core/Customer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.hateoas.sample.core; 17 | 18 | import lombok.EqualsAndHashCode; 19 | import lombok.Getter; 20 | import lombok.RequiredArgsConstructor; 21 | 22 | /** 23 | * Customer domain class. 24 | * 25 | * @author Oliver Gierke 26 | */ 27 | @Getter 28 | @RequiredArgsConstructor 29 | @EqualsAndHashCode(of = "id") 30 | public class Customer { 31 | 32 | private final Long id; 33 | private final String firstname, lastname; 34 | 35 | /** 36 | * Returns whether the {@link Customer} has the given id. 37 | * 38 | * @param id 39 | * @return 40 | */ 41 | public boolean hasId(Long id) { 42 | 43 | return this.id.equals(id); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/hateoas/sample/core/Customers.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.hateoas.sample.core; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | import org.springframework.stereotype.Repository; 22 | 23 | /** 24 | * Simple repository abstraction for {@link Customer}s. 25 | * 26 | * @author Oliver Gierke 27 | */ 28 | public interface Customers { 29 | 30 | /** 31 | * Returns all {@link Customer}s. 32 | * 33 | * @return 34 | */ 35 | List findAll(); 36 | 37 | /** 38 | * Finds a particular {@link Customer} with the given id or {@literal null} in case no Customer is available with the 39 | * given id. 40 | * 41 | * @param id 42 | * @return 43 | */ 44 | Customer findOne(Long id); 45 | 46 | /** 47 | * Basic in-memory implementation of {@link Customers}. 48 | * 49 | * @author Oliver Gierke 50 | */ 51 | @Repository 52 | static class InMemoryCustomers implements Customers { 53 | 54 | private final List customers = new ArrayList(); 55 | 56 | public InMemoryCustomers() { 57 | customers.add(new Customer(1L, "Dave", "Matthews")); 58 | customers.add(new Customer(2L, "Carter", "Beauford")); 59 | } 60 | 61 | /* 62 | * (non-Javadoc) 63 | * @see org.springframework.hateoas.sample.Customers#findAll() 64 | */ 65 | public List findAll() { 66 | return customers; 67 | } 68 | 69 | /* 70 | * (non-Javadoc) 71 | * @see org.springframework.hateoas.sample.Customers#findOne(java.lang.Long) 72 | */ 73 | public Customer findOne(Long id) { 74 | 75 | for (Customer customer : customers) { 76 | if (customer.hasId(id)) { 77 | return customer; 78 | } 79 | } 80 | 81 | return null; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/hateoas/sample/core/Order.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.hateoas.sample.core; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | import lombok.EqualsAndHashCode; 22 | import lombok.Getter; 23 | import lombok.RequiredArgsConstructor; 24 | 25 | /** 26 | * Simple order entity. 27 | * 28 | * @author Oliver Gierke 29 | */ 30 | @Getter 31 | @RequiredArgsConstructor 32 | @EqualsAndHashCode(of = "id") 33 | public class Order { 34 | 35 | private final Long id; 36 | private final Customer customer; 37 | private final List lineItems = new ArrayList(); 38 | 39 | /** 40 | * Returns whether the Order has the given id. 41 | * 42 | * @param id 43 | * @return 44 | */ 45 | public boolean hasId(Long id) { 46 | return this.id.equals(id); 47 | } 48 | 49 | /** 50 | * Returns whether the {@link Order} belongs to the given {@link Customer}. 51 | * 52 | * @param customer 53 | * @return 54 | */ 55 | public boolean belongsTo(Customer customer) { 56 | return this.customer.equals(customer); 57 | } 58 | 59 | /** 60 | * Adds the given {@link LineItem} to the {@link Order}. 61 | * 62 | * @param item 63 | * @return 64 | */ 65 | public Order add(LineItem item) { 66 | this.lineItems.add(item); 67 | return this; 68 | } 69 | 70 | @Getter 71 | @RequiredArgsConstructor 72 | public static class LineItem { 73 | 74 | private final String name; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/hateoas/sample/core/Orders.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.hateoas.sample.core; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Collections; 20 | import java.util.List; 21 | 22 | import org.springframework.beans.factory.annotation.Autowired; 23 | import org.springframework.hateoas.sample.core.Order.LineItem; 24 | import org.springframework.stereotype.Repository; 25 | import org.springframework.util.Assert; 26 | 27 | /** 28 | * Simple repository to access {@link Order}s. 29 | * 30 | * @author Oliver Gierke 31 | */ 32 | public interface Orders { 33 | 34 | /** 35 | * Returns all {@link Order}s. 36 | * 37 | * @return 38 | */ 39 | List findAll(); 40 | 41 | /** 42 | * Returns all {@link Order}s placed by the given {@link Customer}. 43 | * 44 | * @param customer 45 | * @return 46 | */ 47 | List findAll(Customer customer); 48 | 49 | /** 50 | * Returns the {@link Order} with the given id. 51 | * 52 | * @param id 53 | * @return 54 | */ 55 | Order findOne(Long id); 56 | 57 | /** 58 | * In-memory implementation of {@link Orders}. 59 | * 60 | * @author Oliver Gierke 61 | */ 62 | @Repository 63 | static class InMemoryOrders implements Orders { 64 | 65 | private final List orders = new ArrayList(); 66 | 67 | @Autowired 68 | public InMemoryOrders(Customers customers) { 69 | 70 | Assert.notNull(customers, "Customers must not be null!"); 71 | Customer customer = customers.findOne(1L); 72 | 73 | LineItem iPad = new Order.LineItem("iPad"); 74 | LineItem iPhone = new Order.LineItem("iPhone"); 75 | 76 | Order order = new Order(1L, customer); 77 | order.add(iPhone).add(iPad); 78 | 79 | this.orders.add(order); 80 | } 81 | 82 | /* 83 | * (non-Javadoc) 84 | * @see org.springframework.hateoas.sample.Orders#findAll() 85 | */ 86 | public List findAll() { 87 | return Collections.unmodifiableList(orders); 88 | } 89 | 90 | /* 91 | * (non-Javadoc) 92 | * @see org.springframework.hateoas.sample.Orders#findAll(org.springframework.hateoas.sample.Customer) 93 | */ 94 | public List findAll(Customer customer) { 95 | 96 | List result = new ArrayList(); 97 | 98 | for (Order order : orders) { 99 | if (order.belongsTo(customer)) { 100 | result.add(order); 101 | } 102 | } 103 | 104 | return Collections.unmodifiableList(result); 105 | } 106 | 107 | /* 108 | * (non-Javadoc) 109 | * @see org.springframework.hateoas.sample.Orders#findOne(java.lang.Long) 110 | */ 111 | public Order findOne(Long id) { 112 | 113 | for (Order order : orders) { 114 | if (order.hasId(id)) { 115 | return order; 116 | } 117 | } 118 | return null; 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/hateoas/sample/core/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Core domain abstractions. 3 | */ 4 | package org.springframework.hateoas.sample.core; -------------------------------------------------------------------------------- /src/main/java/org/springframework/hateoas/sample/hateoas/CustomerController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.hateoas.sample.hateoas; 17 | 18 | import lombok.NonNull; 19 | import lombok.RequiredArgsConstructor; 20 | 21 | import org.springframework.beans.factory.annotation.Autowired; 22 | import org.springframework.context.annotation.Profile; 23 | import org.springframework.hateoas.EntityLinks; 24 | import org.springframework.hateoas.ExposesResourceFor; 25 | import org.springframework.hateoas.Resource; 26 | import org.springframework.hateoas.Resources; 27 | import org.springframework.hateoas.sample.core.Customer; 28 | import org.springframework.hateoas.sample.core.Customers; 29 | import org.springframework.http.HttpEntity; 30 | import org.springframework.http.HttpStatus; 31 | import org.springframework.http.ResponseEntity; 32 | import org.springframework.stereotype.Controller; 33 | import org.springframework.web.bind.annotation.PathVariable; 34 | import org.springframework.web.bind.annotation.RequestMapping; 35 | import org.springframework.web.bind.annotation.RequestMethod; 36 | 37 | /** 38 | * @author Oliver Gierke 39 | */ 40 | @Controller 41 | @Profile("hateoas") 42 | @RequestMapping("/customers") 43 | @ExposesResourceFor(Customer.class) 44 | @RequiredArgsConstructor(onConstructor = @_(@Autowired)) 45 | public class CustomerController { 46 | 47 | private final @NonNull Customers customers; 48 | private final @NonNull EntityLinks entityLinks; 49 | 50 | @RequestMapping(method = RequestMethod.GET) 51 | HttpEntity> showCustomers() { 52 | 53 | Resources resources = new Resources<>(customers.findAll()); 54 | resources.add(entityLinks.linkToCollectionResource(Customer.class)); 55 | 56 | return new ResponseEntity<>(resources, HttpStatus.OK); 57 | } 58 | 59 | @RequestMapping(value = "/{id}", method = RequestMethod.GET) 60 | HttpEntity> showCustomer(@PathVariable Long id) { 61 | 62 | Resource resource = new Resource<>(customers.findOne(id)); 63 | resource.add(entityLinks.linkToSingleResource(Customer.class, id)); 64 | 65 | return new ResponseEntity<>(resource, HttpStatus.OK); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/hateoas/sample/hateoas/HypermediaConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.hateoas.sample.hateoas; 17 | 18 | import org.springframework.context.annotation.Configuration; 19 | import org.springframework.context.annotation.Profile; 20 | import org.springframework.hateoas.config.EnableHypermediaSupport; 21 | import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType; 22 | 23 | /** 24 | * Separate configuration class to enable Spring Hateoas functionality if the {@code hateoas} profile is activated. 25 | * 26 | * @author Oliver Gierke 27 | */ 28 | @Configuration 29 | @Profile("hateoas") 30 | @EnableHypermediaSupport(type = HypermediaType.HAL) 31 | class HypermediaConfiguration { 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/hateoas/sample/hateoas/OrderController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.hateoas.sample.hateoas; 17 | 18 | import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | import lombok.Getter; 24 | import lombok.NonNull; 25 | import lombok.RequiredArgsConstructor; 26 | 27 | import org.springframework.beans.factory.annotation.Autowired; 28 | import org.springframework.context.annotation.Profile; 29 | import org.springframework.hateoas.Resource; 30 | import org.springframework.hateoas.ResourceSupport; 31 | import org.springframework.hateoas.Resources; 32 | import org.springframework.hateoas.sample.core.Customer; 33 | import org.springframework.hateoas.sample.core.Customers; 34 | import org.springframework.hateoas.sample.core.Order; 35 | import org.springframework.hateoas.sample.core.Order.LineItem; 36 | import org.springframework.hateoas.sample.core.Orders; 37 | import org.springframework.http.HttpEntity; 38 | import org.springframework.http.HttpHeaders; 39 | import org.springframework.http.HttpStatus; 40 | import org.springframework.http.ResponseEntity; 41 | import org.springframework.stereotype.Controller; 42 | import org.springframework.web.bind.annotation.PathVariable; 43 | import org.springframework.web.bind.annotation.RequestMapping; 44 | 45 | /** 46 | * @author Oliver Gierke 47 | */ 48 | @Controller 49 | @Profile("hateoas") 50 | @RequiredArgsConstructor(onConstructor = @_(@Autowired)) 51 | public class OrderController { 52 | 53 | private final @NonNull Orders orders; 54 | private final @NonNull Customers customers; 55 | 56 | /** 57 | * Exposes a collection resource for {@link Order}s. 58 | * 59 | * @return 60 | */ 61 | @RequestMapping("/orders") 62 | HttpEntity> showOrders() { 63 | 64 | Resources orders = new Resources<>(this.orders.findAll()); 65 | orders.add(linkTo(methodOn(OrderController.class).showOrders()).withSelfRel()); 66 | 67 | return new ResponseEntity<>(orders, HttpStatus.OK); 68 | } 69 | 70 | /** 71 | * Exposes a single {@link Order} resource. 72 | * 73 | * @param id 74 | * @return 75 | */ 76 | @RequestMapping("/orders/{id}") 77 | HttpEntity> showOrder(@PathVariable Long id) { 78 | 79 | Resource order = new Resource(orders.findOne(id)); 80 | order.add(linkTo(methodOn(OrderController.class).showOrder(id)).withSelfRel()); 81 | 82 | return new ResponseEntity>(order, HttpStatus.OK); 83 | } 84 | 85 | /** 86 | * Exposes all {@link Order}s for {@link Customer}s. 87 | * 88 | * @param id 89 | * @return 90 | */ 91 | @RequestMapping("/customers/{id}/orders") 92 | HttpEntity> showCustomerOrders(@PathVariable Long id) { 93 | 94 | Customer customer = customers.findOne(id); 95 | 96 | if (customer == null) { 97 | return new ResponseEntity<>(HttpStatus.OK); 98 | } 99 | 100 | List orderResources = new ArrayList<>(); 101 | 102 | for (Order order : orders.findAll(customer)) { 103 | 104 | OrderResource resource = new OrderResource(order.getLineItems()); 105 | resource.add(linkTo(methodOn(OrderController.class).showOrder(order.getId())).withSelfRel()); 106 | resource.add(linkTo(methodOn(CustomerController.class).showCustomer(id)).withRel("customer")); 107 | 108 | orderResources.add(resource); 109 | } 110 | 111 | Resources resources = new Resources<>(orderResources); 112 | resources.add(linkTo(methodOn(OrderController.class).showCustomerOrders(id)).withSelfRel()); 113 | 114 | HttpHeaders headers = new HttpHeaders(); 115 | headers.add("Link", linkTo(methodOn(OrderController.class).showCustomerOrders(id)).withSelfRel().toString()); 116 | 117 | return new ResponseEntity<>(resources, headers, HttpStatus.OK); 118 | } 119 | 120 | /** 121 | * DTO for {@link Order}s. 122 | * 123 | * @author Oliver Gierke 124 | */ 125 | @RequiredArgsConstructor 126 | @Getter 127 | private static class OrderResource extends ResourceSupport { 128 | 129 | private final List lineItems; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/hateoas/sample/hateoas/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Controller classes using Spring HATEOAS. 3 | */ 4 | package org.springframework.hateoas.sample.hateoas; -------------------------------------------------------------------------------- /src/main/java/org/springframework/hateoas/sample/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Spring and web application configuration files. 3 | */ 4 | package org.springframework.hateoas.sample; -------------------------------------------------------------------------------- /src/main/java/org/springframework/hateoas/sample/web/CustomerController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.hateoas.sample.web; 17 | 18 | import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*; 19 | 20 | import java.util.List; 21 | 22 | import lombok.NonNull; 23 | import lombok.RequiredArgsConstructor; 24 | 25 | import org.springframework.beans.factory.annotation.Autowired; 26 | import org.springframework.context.annotation.Profile; 27 | import org.springframework.hateoas.Resource; 28 | import org.springframework.hateoas.sample.core.Customer; 29 | import org.springframework.hateoas.sample.core.Customers; 30 | import org.springframework.http.HttpEntity; 31 | import org.springframework.http.HttpStatus; 32 | import org.springframework.http.ResponseEntity; 33 | import org.springframework.stereotype.Controller; 34 | import org.springframework.web.bind.annotation.PathVariable; 35 | import org.springframework.web.bind.annotation.RequestMapping; 36 | import org.springframework.web.bind.annotation.RequestMethod; 37 | 38 | /** 39 | * Plain Spring MVC controller implementation to expose {@link Customer}s. 40 | * 41 | * @author Oliver Gierke 42 | */ 43 | @Controller 44 | @Profile("web") 45 | @RequestMapping("/customers") 46 | @RequiredArgsConstructor(onConstructor = @_(@Autowired)) 47 | public class CustomerController { 48 | 49 | private final @NonNull Customers customers; 50 | 51 | /** 52 | * Exposes a single {@link Customer}. 53 | * 54 | * @param id 55 | * @return 56 | */ 57 | @RequestMapping(value = "/{id}", method = RequestMethod.GET) 58 | HttpEntity> showCustomer(@PathVariable Long id) { 59 | 60 | Resource resource = new Resource<>(customers.findOne(id)); 61 | resource.add(linkTo(methodOn(CustomerController.class).showCustomer(id)).withSelfRel()); 62 | 63 | return new ResponseEntity<>(resource, HttpStatus.OK); 64 | } 65 | 66 | /** 67 | * Exposes all {@link Customers}. 68 | * 69 | * @return 70 | */ 71 | @RequestMapping(method = RequestMethod.GET) 72 | HttpEntity> showCustomers() { 73 | 74 | List result = customers.findAll(); 75 | return new ResponseEntity>(result, HttpStatus.OK); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/hateoas/sample/web/OrderController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.hateoas.sample.web; 17 | 18 | import java.util.List; 19 | 20 | import lombok.NonNull; 21 | import lombok.RequiredArgsConstructor; 22 | 23 | import org.springframework.beans.factory.annotation.Autowired; 24 | import org.springframework.context.annotation.Profile; 25 | import org.springframework.hateoas.sample.core.Customer; 26 | import org.springframework.hateoas.sample.core.Customers; 27 | import org.springframework.hateoas.sample.core.Order; 28 | import org.springframework.hateoas.sample.core.Orders; 29 | import org.springframework.http.HttpEntity; 30 | import org.springframework.http.HttpStatus; 31 | import org.springframework.http.ResponseEntity; 32 | import org.springframework.stereotype.Controller; 33 | import org.springframework.web.bind.annotation.PathVariable; 34 | import org.springframework.web.bind.annotation.RequestMapping; 35 | import org.springframework.web.bind.annotation.RequestMethod; 36 | 37 | /** 38 | * Spring MVC controller exposing {@link Order} instances directly to the web. Quite a couple of problems to note here: 39 | *
    40 | *
  1. Basically exporting the the domain model to the web 1:1. No means to control what is exposed in which way except 41 | * rendering-specific annotations on the domain classes themselves (which you wnat to avoid).
  2. 42 | *
  3. No hypermedia elements at all. Relations to other resources exposed should rather become links in the 43 | * representation, not embedded. Every resource is "on it's own". There are no general links between the entities.
  4. 44 | *
45 | * 46 | * @author Oliver Gierke 47 | */ 48 | @Controller 49 | @Profile("web") 50 | @RequiredArgsConstructor(onConstructor = @_(@Autowired)) 51 | public class OrderController { 52 | 53 | private final @NonNull Orders orders; 54 | private final @NonNull Customers customers; 55 | 56 | /** 57 | * Exposes all {@link Order}s. 58 | * 59 | * @return 60 | */ 61 | @RequestMapping(value = "/orders", method = RequestMethod.GET) 62 | HttpEntity> showOrders() { 63 | 64 | List result = orders.findAll(); 65 | return new ResponseEntity>(result, HttpStatus.OK); 66 | } 67 | 68 | /** 69 | * Exposes an individual {@link Order}. 70 | * 71 | * @param id 72 | * @return 73 | */ 74 | @RequestMapping(value = "/orders/{id}") 75 | HttpEntity showOrder(@PathVariable Long id) { 76 | 77 | Order order = orders.findOne(id); 78 | return new ResponseEntity(order, HttpStatus.OK); 79 | } 80 | 81 | /** 82 | * Exposes all {@link Order}s placed by a particular customer. 83 | * 84 | * @param id 85 | * @return 86 | */ 87 | @RequestMapping("/customers/{id}/orders") 88 | HttpEntity> showCustomerOrders(@PathVariable Long id) { 89 | 90 | Customer customer = customers.findOne(id); 91 | 92 | if (customer == null) { 93 | return new ResponseEntity>(HttpStatus.NOT_FOUND); 94 | } 95 | 96 | return new ResponseEntity>(orders.findAll(customer), HttpStatus.OK); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/hateoas/sample/web/Snippets.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.hateoas.sample.web; 17 | 18 | import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*; 19 | 20 | import org.springframework.hateoas.EntityLinks; 21 | import org.springframework.hateoas.Resource; 22 | import org.springframework.hateoas.Resources; 23 | import org.springframework.hateoas.sample.core.Customer; 24 | import org.springframework.hateoas.sample.core.Customers; 25 | import org.springframework.hateoas.sample.core.Order; 26 | import org.springframework.hateoas.sample.core.Orders; 27 | import org.springframework.http.HttpEntity; 28 | import org.springframework.http.HttpStatus; 29 | import org.springframework.http.ResponseEntity; 30 | import org.springframework.web.bind.annotation.PathVariable; 31 | import org.springframework.web.bind.annotation.RequestMapping; 32 | 33 | /** 34 | * Code snippets to hypermediize the sample controllers. 35 | * 36 | * @author Oliver Gierke 37 | */ 38 | public class Snippets { 39 | 40 | Customers customers; 41 | Orders orders; 42 | 43 | Resource resource; 44 | Resources resources; 45 | 46 | EntityLinks links; 47 | 48 | /** 49 | * Step 1 - Resources 50 | */ 51 | HttpEntity> showCustomer(@PathVariable Long id) { 52 | 53 | Resource resource = new Resource<>(customers.findOne(id)); 54 | return new ResponseEntity<>(resource, HttpStatus.OK); 55 | } 56 | 57 | /** 58 | * Step 2 - LinkBuilder 59 | */ 60 | void linkBuilder(Long id) { 61 | 62 | // import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*; 63 | resource.add(linkTo(CustomerController.class).slash(id).withSelfRel()); 64 | resource.add(linkTo(methodOn(CustomerController.class).showCustomer(id)).withSelfRel()); 65 | } 66 | 67 | /** 68 | * Step 3 - EntityLinks 69 | */ 70 | @RequestMapping("/customers/{id}/orders") 71 | HttpEntity> showCustomerOrders(@PathVariable Long id) { 72 | 73 | // @ExposesResourceFor(Customer.class) 74 | 75 | // @Autowired 76 | // EntityLinks links; 77 | 78 | // @EnableHypermediaSupport 79 | 80 | Customer customer = customers.findOne(id); 81 | 82 | if (customer == null) { 83 | return new ResponseEntity<>(HttpStatus.NOT_FOUND); 84 | } 85 | 86 | Resources resources = new Resources<>(orders.findAll(customer)); 87 | resources.add(linkTo(methodOn(CustomerController.class).showCustomer(id)).withRel("customer")); 88 | // resources.add(links.linkForSingleResource(Customer.class, id).withRel("customer")); 89 | 90 | return new ResponseEntity<>(resources, HttpStatus.OK); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/hateoas/sample/web/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Controller classes using plain Spring MVC. 3 | */ 4 | package org.springframework.hateoas.sample.web; -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | %d %5p %40.40c:%4L - %m%n 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/hateoas/sample/AbstractWebIntegrationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.hateoas.sample; 17 | 18 | import static org.hamcrest.CoreMatchers.*; 19 | import static org.junit.Assert.*; 20 | 21 | import org.junit.Before; 22 | import org.junit.Test; 23 | import org.junit.runner.RunWith; 24 | import org.springframework.beans.factory.annotation.Autowired; 25 | import org.springframework.test.context.ContextConfiguration; 26 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 27 | import org.springframework.test.context.web.WebAppConfiguration; 28 | import org.springframework.test.web.servlet.MockMvc; 29 | import org.springframework.test.web.servlet.setup.MockMvcBuilders; 30 | import org.springframework.web.context.WebApplicationContext; 31 | 32 | /** 33 | * Base class setting up Spring MVC integration testing bootstrapping the core application configuration. 34 | * 35 | * @author Oliver Gierke 36 | */ 37 | @RunWith(SpringJUnit4ClassRunner.class) 38 | @WebAppConfiguration 39 | @ContextConfiguration(classes = ApplicationConfig.class) 40 | public abstract class AbstractWebIntegrationTest { 41 | 42 | @Autowired 43 | WebApplicationContext context; 44 | 45 | protected MockMvc mvc; 46 | 47 | @Before 48 | public void setUp() { 49 | this.mvc = MockMvcBuilders.webAppContextSetup(context).build(); 50 | } 51 | 52 | @Test 53 | public void bootstrapsWebApp() { 54 | assertThat(mvc, is(notNullValue())); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/hateoas/sample/hateoas/ApplicationIntegrationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.hateoas.sample.hateoas; 17 | 18 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; 19 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; 20 | 21 | import org.junit.Test; 22 | import org.springframework.hateoas.sample.AbstractWebIntegrationTest; 23 | import org.springframework.test.context.ActiveProfiles; 24 | 25 | /** 26 | * Web integration test using the {@code hateoas} profile. 27 | * 28 | * @author Oliver Gierke 29 | */ 30 | @ActiveProfiles("hateoas") 31 | public class ApplicationIntegrationTest extends AbstractWebIntegrationTest { 32 | 33 | @Test 34 | public void foo() throws Exception { 35 | mvc.perform(get("/customers/1")).andExpect(status().isOk()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/hateoas/sample/web/ApplicationIntegrationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.hateoas.sample.web; 17 | 18 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; 19 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; 20 | 21 | import org.junit.Test; 22 | import org.springframework.hateoas.sample.AbstractWebIntegrationTest; 23 | import org.springframework.test.context.ActiveProfiles; 24 | 25 | /** 26 | * Web integration test using the {@code web} profile. 27 | * 28 | * @author Oliver Gierke 29 | */ 30 | @ActiveProfiles("web") 31 | public class ApplicationIntegrationTest extends AbstractWebIntegrationTest { 32 | 33 | @Test 34 | public void foo() throws Exception { 35 | mvc.perform(get("/customers/1")).andExpect(status().isOk()); 36 | } 37 | } 38 | --------------------------------------------------------------------------------