Shindig Spring Example

Example Overview

MockPersonSpi

MockPersonSpi is a mock implementation of PersonService, returning mock responses to the getPerson and getPeople methods. Obviously, in a real world scenario this should be a real implementation of PersonService, but the mock serves our purposes well for this example.

public class MockPersonSpi implements PersonService {
  private static final UserId JOHN = new UserId(UserId.Type.userId, "john.doe");
  private static final UserId JANE = new UserId(UserId.Type.userId, "jane.doe");
  private static final UserId[] FRIENDS = {JOHN, JANE};

  public Future<Person> getPerson(UserId userId, Set<String> fields, SecurityToken token)
    throws SocialSpiException {
    Person person = new PersonImpl();
    person.setId(userId.getUserId());
    return ImmediateFuture.newInstance(person);
  }

  public Future<RestfulCollection<Person>> getPeople(Set<UserId> userIds, GroupId groupId,
          CollectionOptions collectionOptions, Set<String> fields, SecurityToken token)
          throws SocialSpiException {
    try {
      List<Person> people = new ArrayList<Person>();
      switch (groupId.getType()) {
      case self:
        for (UserId userId: userIds) {
          Person person = new PersonImpl();
          person.setId(userId.getUserId());
          people.add(person);
        }
        break;
      case friends:
        for (UserId userId: FRIENDS) {
          Person person = new PersonImpl();
          person.setId(userId.getUserId());
          people.add(person);
        }
        break;
      case all:
        throw new SocialSpiException(ResponseError.NOT_IMPLEMENTED, "Not yet implemented",null);
      case groupId:
        throw new SocialSpiException(ResponseError.NOT_IMPLEMENTED, "Not yet implemented",null);
      case deleted:
        throw new SocialSpiException(ResponseError.NOT_IMPLEMENTED, "Not yet implemented",null);
      default:
        throw new SocialSpiException(ResponseError.BAD_REQUEST, "Group ID not recognized",null);
      }
      return ImmediateFuture.newInstance(new RestfulCollection<Person>(people, 0, people.size()));
    } catch (Exception e) {
      throw new SocialSpiException(ResponseError.INTERNAL_ERROR, "Exception occurred", e);
    }
  }
}

GuiceModule

GuiceModule is the Guice configuration module that binds any custom shindig bindings, and in this scenario the only customization needed is to bind MockPersonSpi to the PersonService interface. To bind an instance of MockPersonSpi, GuiceModule will use ApplicationContextFactory to get Spring's ApplicationContext, and in turn use ApplicationContext to get the MockPersonSpi bean instance. Since GuiceModule extends SocialApiGuiceModule, it calls super.configure() so that the default social bindings are therefore also configured.

public class GuiceModule extends SocialApiGuiceModule {
  private static final String PERSON_SPI_BEAN_NAME = "personSpi";

  @Override
  protected void configure() {
    ApplicationContext applicationContext = ApplicationContextFactory.getApplicationContext();
    this.bind(PersonService.class).toInstance((PersonService) applicationContext.getBean(PERSON_SPI_BEAN_NAME));
    super.configure();
  }
}

ApplicationContextFactory

ApplicationContextFactory acts as a holder to Spring's ApplicationContext, thus providing a static getter method to the context.

public class ApplicationContextFactory {
  private static final String[] LOCATIONS = {"application-context.xml"};
  private static final AbstractApplicationContext CONTEXT = new ClassPathXmlApplicationContext(LOCATIONS);

  private ApplicationContextFactory() {}

  public static AbstractApplicationContext getApplicationContext() {
    return CONTEXT;
  }
}

application-context.xml

The application-context.xml Spring configuration file is where Spring beans are defined, and in this instance a bean definition has been included for MockPersonSpi.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
	http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
	<bean id="personSpi" class="org.apache.shindig.samples.springexample.spi.MockPersonSpi"/>
</beans>

web.xml

To have this example running within a servlet container it's necessary to add GuiceModule into the web.xml file.

<?xml version="1.0"?>
<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_4.dtd">

<web-app>
  <display-name>shindig-spring-example web descriptor</display-name>

  <!-- configuration -->
  <!-- If you have your own Guice module(s), put them here as a colon-separated list. -->
  <context-param>
    <param-name>guice-modules</param-name>
    <param-value>
      org.apache.shindig.common.PropertiesModule:
      org.apache.shindig.samples.springexample.config.GuiceModule
    </param-value>
  </context-param>

  <!-- Shindig authentication filter -->
  <filter>
    <filter-name>authFilter</filter-name>
    <filter-class>org.apache.shindig.auth.AuthenticationServletFilter</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>authFilter</filter-name>
    <url-pattern>/social/*</url-pattern>
  </filter-mapping>

  <!-- GuiceServletContextListener -->
  <listener>
    <listener-class>org.apache.shindig.common.servlet.GuiceServletContextListener</listener-class>
  </listener>

  <!-- ApplicationServletContextListener -->
  <!-- Destroys Spring's ApplicationContext at shutdown, forcing bean defined destroy-methods to be invoked -->
  <listener>
    <listener-class>org.apache.shindig.samples.springexample.servlet.ApplicationServletContextListener</listener-class>
  </listener>

  <!-- Serve REST api -->
  <servlet>
    <servlet-name>restapiServlet</servlet-name>
    <servlet-class>
      org.apache.shindig.protocol.DataServiceServlet
    </servlet-class>
  </servlet>

  <!-- Serve RPC api -->
  <servlet>
    <servlet-name>jsonRpcServlet</servlet-name>
    <servlet-class>
      org.apache.shindig.protocol.JsonRpcServlet
    </servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>restapiServlet</servlet-name>
    <url-pattern>/social/rest/*</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>jsonRpcServlet</servlet-name>
    <url-pattern>/social/rpc/*</url-pattern>
  </servlet-mapping>
</web-app>

Example Source Code