Spring-a-Gram is a demonstration of the Spring stack used to upload and view pictures.
Spring Data REST makes it incredibly simple to define and export data store entities. What does this mean? You declare the POJOs that are stored in your database. In this case, we are using a JPA, in-memory data store. (Spring Data REST supports other data stores as well). You then define a related repository interface. Spring Data REST takes it from there to create a hypermedia-driven, RESTful interface using Spring MVC that lets you create, read, update, and delete data.
This sample application demonstrations how declaring a backend aimed at storing image data can make it as simple as possible to upload pictures and then turn around and display them on a website. This opens the door to other front ends, like an iOS or Android app.
If you want to run it right now, you need:
-
Java 8+
-
Maven 3.0+ (http://maven.apache.org)
-
Bower 1.3+ (http://bower.io)
To download and run:
-
git clone [email protected]:gregturn/spring-a-gram.git
to grab a local copy (or clone your own fork). -
bower install
to pull down javascript libraries. If you get prompted about jQuery, accept the version specified in bower.json -
mvn clean spring-boot:run
Note
|
To deploy a copy to PWS, you need a Java 8 build pack. Execute cf push spring-a-gram -p target/spring-a-gram-0.1.0.jar -b https://github.com/spring-io/java-buildpack.git and you’ll be set.
|
This should get the app started courtesy of Spring Boot. Now you can traverse the app from different view points.
-
From the command line, interrogate the API with
curl localhost:8080/api
. Navigate to the other links. -
From the desktop, visit http://localhost:8080 to see the Desktop version of the web experience.
-
From a mobile device, visit http://localhost:8080 and see a different flow, optimized for the smaller real estate.
Let’s explore.
src/main/java/com/greglturnquist/springagram/Item.java
package com.greglturnquist.springagram;
/* some import statements */
@Entity
public class Item {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
@Lob
private String image;
@ManyToOne
private Gallery gallery;
...
The core piece of this app are the Items. Each one contains an auto-generated id, a name, a raw image
(stored in the format of a String
) and an optional Gallery.
Note
|
Java strings are wide and generally unicode based. To store the raw data of a picture in the database, it has been tagged with @Lob, a JPA annotation used to signal this a Large Object. |
We declared up above that Items can be put in Galleries. This gives us the opportunity to define a relationship and see to manage it through the RESTful endpoints later on.
src/main/java/com/greglturnquist/springagram/Gallery.java
package com.greglturnquist.springagram;
/* import statements */
@Entity
public class Gallery {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String description;
@OneToMany(mappedBy = "gallery")
private List<Item> items;
...
A Gallery has an id, a description, and can contain zero or more Items.
src/main/java/com/greglturnquist/springagram/ItemRepository.java
package com.greglturnquist.springagram;
import java.util.List;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource()
public interface ItemRepository extends CrudRepository<Item, Long> {
List<Item> findByGalleryIsNull();
}
ItemRepository
has one custom finder method, and that’s to get a listing of items that are NOT assigned to a gallery.
src/main/java/com/greglturnquist/springagram/GalleryRepository.java
package com.greglturnquist.springagram;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource
public interface GalleryRepository extends CrudRepository<Gallery, Long> {
}
All of the Spring Data projects are based on this similar concept: define an
interface that extends either CrudRepository
or PagingAndSortingRepository
and then let Spring Data create a
proxied concrete implementation. You get a handful of built in operations. You can add custom queries, but we aren’t
doing that right now.
Both of these interfaces have an extra annotation: RepositoryRestResource
. This annotation provides the means to
change parts of the URLs and hypermedia, but we aren’t using that here.
Note
|
If you notice the end of the declaration where it says <Item, Long> and <Gallery, Long> , that indicates the
entity type and the type of its keys.
|
One last piece remains. We need a runnable app. By default, Spring Data REST serves up our hypermedia interface
at /
. To alter it, we need to subclass a piece of Spring Data REST.
@Configuration
public class CustomizedRestMvcConfiguration extends RepositoryRestMvcConfiguration {
@Override
public RepositoryRestConfiguration config() {
RepositoryRestConfiguration config = super.config();
config.setBaseUri(URI.create("/api"));
return config;
}
}
This class sets the baseUri
to /api
. Why would you want to do this? So when you point your browser at http://localhost:8080, you see
the web page not the JSON served up by the hypermedia.
src/main/java/com/greglturnquist/springagram/Application.java
package com.greglturnquist.springagram;
/* import statements */
@Configuration
@EnableJpaRepositories
@Import(CustomizedRestMvcConfiguration.class)
@EnableAutoConfiguration
@ComponentScan
public class Application {
public static void main(String[] args) throws IOException {
SpringApplication.run(Application.class, args);
}
}
This is mostly boilerplate. It contains key annotations to declare and launch an application.
-
@Configuration means this class is the source of beans for a Spring app
-
@EnableJpaRepositories turns on the ability to scan and detect JPA entities and repository interfaces
-
@Import(CustomizedRestMvcConfiguration.class) pulls in a handful of beans needed to launch Spring Data REST, which is really a specialized Spring MVC app
-
@EnableAutoConfiguration tells Spring Boot to autoconfigure as much as possible
-
@ComponentScan tells Spring to look for any other classes in the same package that are configurable, such as @Component’s, @Service’s, and @Controller’s
This code, by itself, it enough to spin up a RESTful, hypermedia based app. There is no visual element here. You only have access to tools like curl or whatever REST client you wish to use. But you can perform all the CRUD operations you want.
At the root, you can see what links are available.
$ curl localhost:8080/api
{
"_links" : {
"items" : {
"href" : "http://localhost:8080/api/items"
},
"galleries" : {
"href" : "http://localhost:8080/api/galleries"
}
}
}
-
/api/items is where individual items are stored.
-
/api/galleries is where the galleries are stored.
$ curl localhost:8080/api/items
{
"_links" : {
"search" : {
"href" : "http://localhost:8080/api/items/search"
}
}
}
Here you can see there are no entries yet. You can go on and explore the other links to get a feel for things.
To dial up the visual appeal and show easy Spring Data REST makes it to build your own picture sharing service, I added a web page.
It’s pretty simple. It contains a form where you can pick a picture and submit it for upload. Following that is a table to display a thumbnail-sized version of each picture.
-
This app is using webjars to fetch a copy of jQuery
In encourage you to skim through all the code to see how it readily demonstrates the power and simplicity of having a complete, RESTful service.