Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ javadoc
bin/
lib/
*~
.svn/
9 changes: 9 additions & 0 deletions client/src/main/java/br/com/caelum/restfulie/Restfulie.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@

import java.net.URI;
import java.net.URISyntaxException;

import br.com.caelum.restfulie.http.DefaultHttpRequest;
import br.com.caelum.restfulie.http.DefaultRestClient;
import br.com.caelum.restfulie.http.Request;
import br.com.caelum.restfulie.mediatype.GsonMediaType;

/**
* Restfulie's client API entry point.<br/>
Expand All @@ -44,6 +46,13 @@ public static RestClient custom() {
return new DefaultRestClient();
}

/**
* Entry point to configure serialization data prior to accessing the resources (json only).
*/
public static RestClient jsonOnly() {
return new DefaultRestClient( new GsonMediaType() );
}

/**
* Entry point to direct access an uri.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import br.com.caelum.restfulie.http.apache.ApacheDispatcher;
import br.com.caelum.restfulie.mediatype.FormEncoded;
import br.com.caelum.restfulie.mediatype.JsonMediaType;
import br.com.caelum.restfulie.mediatype.MediaType;
import br.com.caelum.restfulie.mediatype.MediaTypes;
import br.com.caelum.restfulie.mediatype.XmlMediaType;
import br.com.caelum.restfulie.request.RequestDispatcher;
Expand Down Expand Up @@ -61,6 +62,21 @@ public DefaultRestClient()
this.threads = Executors.newCachedThreadPool();
}

/**
* Constructor which only will register the specified media types
* @param medias MediaTypes to be registered with this client
* @author Felipe Brandao
*/
public DefaultRestClient( MediaType...medias )
{
this.dispatcher = new ApacheDispatcher(this);
this.inflector = new NounPluralizer();

for( MediaType media : medias ) this.types.register( media );

this.threads = Executors.newCachedThreadPool();
}

public DefaultRestClient use(RequestDispatcher executor) {
this.dispatcher = executor;
return this;
Expand Down
108 changes: 108 additions & 0 deletions client/src/main/java/br/com/caelum/restfulie/http/JsonRestClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package br.com.caelum.restfulie.http;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.jvnet.inflector.Pluralizer;
import org.jvnet.inflector.lang.en.NounPluralizer;

import br.com.caelum.restfulie.RestClient;
import br.com.caelum.restfulie.RestfulieException;
import br.com.caelum.restfulie.http.apache.ApacheDispatcher;
import br.com.caelum.restfulie.mediatype.GsonMediaType;
import br.com.caelum.restfulie.mediatype.MediaTypes;
import br.com.caelum.restfulie.request.RequestDispatcher;

/**
* RestClient implementation based on DefaultRestClient
* <br>
* This client has been created to allow using restfulie without XML related
* libs, which i consider a bit bloat if users really don't need any XML stuff.
*
* @author Felipe Brandao
*
*/
public class JsonRestClient implements RestClient{

private final MediaTypes types = new MediaTypes();

private RequestDispatcher dispatcher;

private Pluralizer inflector;

private URI lastURI = null;

private final ExecutorService threads;

public JsonRestClient(){
this.dispatcher = new ApacheDispatcher(this);
//TODO don't know if really need to use it
this.inflector = new NounPluralizer();
types.register(new GsonMediaType());
this.threads = Executors.newCachedThreadPool();
}

public JsonRestClient use(RequestDispatcher executor) {
this.dispatcher = executor;
return this;
}

public RequestDispatcher getProvider() {
return dispatcher;
}

public MediaTypes getMediaTypes() {
return types;
}

/**
* Entry point to direct access an uri.
*/
public Request at(URI uri) {
lastURI = uri;
return createRequestFor(uri);
}

/**
* Override this method to use your own Request object
*
* @param uri
* @return
*/
protected Request createRequestFor(URI uri) {
return new DefaultHttpRequest(uri, this);
}

/**
* Entry point to direct access an uri.
* @throws URISyntaxException
*/
public Request at(String uri) {
try {
return at(new URI(uri));
} catch (URISyntaxException e) {
throw new RestfulieException("Unable to build an URI for this request.", e);
}
}

public URI lastURI() {
return lastURI;
}

public Pluralizer inflectionRules() {
return inflector;
}

public RestClient withInflector(Pluralizer inflector){
this.inflector = inflector;
return this;
}

@Override
public ExecutorService getThreads() {
return threads;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package br.com.caelum.restfulie.mediatype;

import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import br.com.caelum.restfulie.RestClient;
import br.com.caelum.restfulie.mediatype.MediaType;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

/**
* Media Type implementation for unmarshalling data received in JSON format using GSON library
*
* @author Felipe Brandao
*
*/
public class GsonMediaType implements MediaType{

private final static Pattern rootTypeDetectionRegex = Pattern.compile( "^\\{[ ]*\"([^\"]*)\"[ ]*:(.*)}$" );

private final List<String> types = Arrays.asList("application/json", "text/json", "json");

private Map<String,Class<?>> classes = new HashMap<String, Class<?>>();
private Map<String,Type> collections = new HashMap<String, Type>();

private Gson gson;

public GsonMediaType() {
this.gson = new Gson();
}

/**
* Register a class which should be used for unmarshalling.
* <br>
* It uses the lower case class name as alias to knows what should be unmarshalled.
* <br>
* @see #withType(String, Class)
* @param javaType Java class to be registered
* @return This instance
*/
public GsonMediaType withType( Class<?> javaType ){
String correctAlias = javaType.getSimpleName();
correctAlias = correctAlias.substring( 0 , 1 ).toLowerCase() + correctAlias.substring( 1 );
return this.withType( correctAlias , javaType );
}

/**
* Register a class which should be used for unmarshalling.
* <br>
* Uses the informed alias to knows what should be unmarshalled.
* <br>
* @param alias Alias to the class
* @param javaType Java class to be registered
* @return This instance
*/
public GsonMediaType withType( String alias , Class<?> javaType ){
this.classes.put( alias , javaType );
return this;
}

/**
* Register an alias to identify a JSON resource (usually top level arrays) which should be unmarshalled for a
* specific Collection.
*
* @see TypeToken
* @param alias Collection alias
* @param collectionType Java Type which sould be used (use TypeToken)
* @return This instance
*/
public GsonMediaType withCollection( String alias , Type collectionType ) {
this.collections.put( alias , collectionType );
return this;
}

@Override
public boolean answersTo( String type ) {
return types.contains( type );
}

@Override
public <T> void marshal( T payload, Writer writer, RestClient client ) throws IOException {
String json = this.gson.toJson( payload );
System.out.println( "Marshalled object:" + json );
writer.append( json );
}

@Override
public <T> T unmarshal( String content, RestClient client ) {
JsonData jsonData = new JsonData();
jsonData.json = content;

Class<T> rootClass = detectRootClass( jsonData );
if( rootClass != null ){ //we know wich class should be used
return this.gson.fromJson( jsonData.json , rootClass );
}else{//detection failed, will try to detect as top level array (alias for collection)
Type rootType = detectRootType( jsonData );

if( rootType == null ){//we don't know exactly what to do, so let's blow up
throw new IllegalArgumentException( "There's no registered class/collection alias for '" + jsonData.alias + "'" );
}


return this.gson.fromJson( jsonData.json , rootType );
}
}

/**
* Detects which class should be used for unmarshalling
* @param <T>
* @param jsonData
* @return A class object for unmarshalling JSON data
*/
@SuppressWarnings("unchecked")
private <T> Class<T> detectRootClass( JsonData jsonData ){
Matcher matcher = rootTypeDetectionRegex.matcher( jsonData.json );
if( matcher.matches() ){
String alias = matcher.group(1);
//defines informations inside jsonData, if fails to detect a registered class for unmarshall (could be a collection)
jsonData.alias = alias;
jsonData.json = matcher.group(2);

Class<T> javaClass = (Class<T>) this.classes.get( alias );
return javaClass;
}
return null;
}

/**
* Detects which type should be used for unmarshalling a top level array
* @param jsonData
* @return
*/
private Type detectRootType( JsonData jsonData ){
return this.collections.get( jsonData.alias );
}


/**
* Handler for value related with the (un)marshalling
* @author felipebn
*/
private final static class JsonData{
String alias;
String json;
}
}
Loading