Develop a Facebook-App with Spring-Social – Part I: Behind the Scenes

In this series of Mini-How-Tow’s I will describe how to develop a facebook app with the help of Spring-Social.

In the last and first part of this series, I prepared you for our little course.

In this part we will take a look behind the scenes and learn more about the autoconfiguration performed by Spring-Boot, which made our first small example so automagically.

The Source is With You

You can find the source-code on http://juplo.de/git/examples/facebook-app/ and browse it via gitweb. Check out part-01 to get the source for this part of the series.

Our Silent Servant Behind the Scenes: Spring-Boot

While looking at our simple example from the last part of this series, you may have wondered, how all this is wired up. You can log in a user from facebook, access his public profile and all this without one line of configuration.

This is achieved via Spring-Boot autoconfiguration.

What comes in very handy in the beginning, sometimes get’s in your way, when your project grows. This may happen, because these parts of the code are not under your control and you do not know what the autoconfiguration is doing on your behalf. Because of that, in this part of our series, we will rebuild the most relevant parts of the configuration by hand. As you will see later, this is not only an exercise, but will lead us to the first improvement of our little example.

What Is Going On Here?

In our case, two Spring-Boot configuration-classes are defining the configuration. These two classes are SocialWebAutoConfiguration and FacebookAutoConfiguration. Both classes are located in the package spring-boot-autoconfigure.

The first one configures the ConnectController, sets up an instance of InMemoryUsersConnectionRepository as persitent store for user/connection-mappings and sets up a UserIdService on our behalf, that always returns the user-id anonymous.

The second one adds an instance of FacebookConnectionFactory to the list of available connection-factories, if the required properties (spring.social.facebook.appId and spring.social.facebook.appSecret) are available. It also configures, that a request-scoped bean of the type Connection<Facebook> is created for each request, that has a known user, who is connected to the Graph-API.

Rebuild This Configuration By Hand

The following class rebuilds the same configuration explicitly:

@Configuration
@EnableSocial
public class SocialConfig extends SocialConfigurerAdapter
{
  /**
   * Add a {@link FacebookConnectionFactory} to the configuration.
   * The factory is configured through the keys <code>facebook.app.id</code>
   * and <,code>facebook.app.secret</code>.
   *
   * @param config
   * @param env 
   */
  @Override
  public void addConnectionFactories(
      ConnectionFactoryConfigurer config,
      Environment env
      )
  {
    config.addConnectionFactory(
        new FacebookConnectionFactory(
            env.getProperty("facebook.app.id"),
            env.getProperty("facebook.app.secret")
            )
        );
  }

  /**
   * Configure an instance of {@link InMemoryUsersConnection} as persistent
   * store of user/connection-mappings.
   *
   * At the moment, no special configuration is needed.
   *
   * @param connectionFactoryLocator
   *     The {@link ConnectionFactoryLocator} will be injected by Spring.
   * @return
   *     The configured {@link UsersConnectionRepository}.
   */
  @Override
  public UsersConnectionRepository getUsersConnectionRepository(
      ConnectionFactoryLocator connectionFactoryLocator
      )
  {
    InMemoryUsersConnectionRepository repository =
        new InMemoryUsersConnectionRepository(connectionFactoryLocator);
    return repository;
  }

  /**
   * Configure a {@link UserIdSource}, that is equivalent to the one, that is
   * created by Spring-Boot.
   *
   * @return
   *     An instance of {@link AnonymousUserIdSource}.
   *
   * @see {@link AnonymousUserIdSource}
   */
  @Override
  public UserIdSource getUserIdSource()
  {
    return new AnonymousUserIdSource();
  }


  /**
   * Configuration of the controller, that handles the authorization against
   * the Facebook-API, to connect a user to Facebook.
   *
   * At the moment, no special configuration is needed.
   *
   * @param factoryLocator
   *     The {@link ConnectionFactoryLocator} will be injected by Spring.
   * @param repository
   *     The {@link ConnectionRepository} will be injected by Spring.
   * @return
   *     The configured controller.
   */
  @Bean
  public ConnectController connectController(
      ConnectionFactoryLocator factoryLocator,
      ConnectionRepository repository
      )
  {
    ConnectController controller =
        new ConnectController(factoryLocator, repository);
    return controller;
  }

  /**
   * Configure a scoped bean named <code>facebook</code>, that enables
   * access to the Graph-API in the name of the current user.
   *
   * @param repository
   *     The {@link ConnectionRepository} will be injected by Spring.
   * @return
   *     A {@Connection}, that represents the authorization of the
   *     current user against the Graph-API, or null, if the
   *     current user is not connected to the API.
   */
  @Bean
  @Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
  public Facebook facebook(ConnectionRepository repository)
  {
    Connection connection =
        repository.findPrimaryConnection(Facebook.class);
    return connection != null ? connection.getApi() : null;
  }
}

If you run this refined version of our app, you will see, that it behaves in exact the same way, as the initial version.

Coming next

You may ask, why we should rebuild the configuration by hand, if it does the same thing. This is, because the example, so far, would not work as a real app. The first step, to refine it, is to take control of the configuration.

In the next part of this series, I will show you, why this is necessary. But, first, we have to take a short look into Spring Social.

Funded by the Europian Union

This article was published in the course of a resarch-project, that is funded by the European Union and the federal state Northrhine-Wetphalia.

Europäische Union: Investitionen in unsere Zukunft - Europäischer Fonds für regionale Entwicklung EFRE.NRW 2014-2020: Invesitionen in Wachstum und Beschäftigung

Leave a Reply

Your email address will not be published. Required fields are marked *