Enabled autoconfiguration for Spring-Social and Spring-Security-OAuth2
authorKai Moritz <kai@juplo.de>
Sun, 12 Jun 2016 12:31:42 +0000 (14:31 +0200)
committerKai Moritz <kai@juplo.de>
Sun, 12 Jun 2016 12:39:27 +0000 (14:39 +0200)
pom.xml
src/main/java/de/juplo/facebook/errors/FacebookErrorsOAuth2AutoConfiguration.java [new file with mode: 0644]
src/main/java/de/juplo/facebook/errors/FacebookErrorsSpringSocialAutoConfiguration.java [new file with mode: 0644]
src/main/resources/META-INF/spring.factories [new file with mode: 0644]
src/test/java/de/juplo/facebook/errors/FacebookErrorsOAuth2AutoConfigurationTest.java [new file with mode: 0644]
src/test/java/de/juplo/facebook/errors/FacebookErrorsSpringSocialAutoConfigurationTest.java [new file with mode: 0644]
src/test/resources/logback-test.xml

diff --git a/pom.xml b/pom.xml
index e06266b..84b68e9 100644 (file)
--- a/pom.xml
+++ b/pom.xml
 
   <dependencies>
 
-    <!-- Spring -->
+    <!-- Spring-Boot-Autoconfiguration -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-autoconfigure</artifactId>
+      <optional>true</optional>
+    </dependency>
     <dependency>
       <groupId>org.springframework.social</groupId>
       <artifactId>spring-social-facebook</artifactId>
       <artifactId>spring-test</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-configuration-processor</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>javax.servlet-api</artifactId>
+      <scope>test</scope>
+    </dependency>
     <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
diff --git a/src/main/java/de/juplo/facebook/errors/FacebookErrorsOAuth2AutoConfiguration.java b/src/main/java/de/juplo/facebook/errors/FacebookErrorsOAuth2AutoConfiguration.java
new file mode 100644 (file)
index 0000000..082b0d6
--- /dev/null
@@ -0,0 +1,75 @@
+package de.juplo.facebook.errors;
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration;
+import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.oauth2.client.OAuth2RestTemplate;
+import org.springframework.security.oauth2.client.http.OAuth2ErrorHandler;
+
+
+/**
+ * Automatic configuration for Srping-Security-OAuth2
+ *
+ * @author Kai Moritz
+ */
+@Configuration
+@ConditionalOnClass(value = OAuth2RestTemplate.class)
+@AutoConfigureAfter({
+  WebMvcAutoConfiguration.class,
+  OAuth2AutoConfiguration.class
+  })
+public class FacebookErrorsOAuth2AutoConfiguration
+{
+  private static final Logger LOG =
+      LoggerFactory.getLogger(FacebookErrorsOAuth2AutoConfiguration.class);
+
+  @Bean
+  static public BeanPostProcessor errorHandlerInjectorOAuth2()
+  {
+    LOG.info("Configuring OAuth2GraphApiErrorHandler for handling error-messages");
+
+    return new BeanPostProcessor()
+    {
+      @Override
+      public Object postProcessBeforeInitialization(
+          Object bean,
+          String beanName
+          )
+          throws
+            BeansException
+      {
+        if (bean instanceof OAuth2RestTemplate)
+        {
+          LOG.debug("Injecting OAuth2GraphApiErrorHandler in {}", bean);
+          OAuth2RestTemplate template = (OAuth2RestTemplate) bean;
+          template.setErrorHandler(
+              new OAuth2GraphApiErrorHandler(
+                  (OAuth2ErrorHandler)template.getErrorHandler()
+                  )
+              );
+        }
+
+        return bean;
+      }
+
+      @Override
+      public Object postProcessAfterInitialization(
+          Object bean,
+          String beanName
+          )
+          throws
+          BeansException
+      {
+        return bean;
+      }
+    };
+  }
+}
diff --git a/src/main/java/de/juplo/facebook/errors/FacebookErrorsSpringSocialAutoConfiguration.java b/src/main/java/de/juplo/facebook/errors/FacebookErrorsSpringSocialAutoConfiguration.java
new file mode 100644 (file)
index 0000000..f474be8
--- /dev/null
@@ -0,0 +1,92 @@
+package de.juplo.facebook.errors;
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration;
+import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.social.facebook.api.Facebook;
+import org.springframework.social.facebook.api.impl.FacebookTemplate;
+import org.springframework.web.client.ResponseErrorHandler;
+import org.springframework.web.client.RestTemplate;
+
+
+
+/**
+ * Automatic configuration for Srping-Social-Facebook
+ *
+ * @author Kai Moritz
+ */
+@Configuration
+@ConditionalOnClass(value = FacebookTemplate.class)
+@AutoConfigureAfter({
+  WebMvcAutoConfiguration.class,
+  FacebookAutoConfiguration.class
+  })
+public class FacebookErrorsSpringSocialAutoConfiguration
+{
+  private static final Logger LOG =
+      LoggerFactory.getLogger(FacebookErrorsSpringSocialAutoConfiguration.class);
+
+
+  @Bean
+  static public BeanPostProcessor errorHandlerInjectorSpringSocial()
+  {
+    LOG.info("Configuring GraphApiErrorHandler for handling error-messages");
+    return new BeanPostProcessor()
+    {
+      @Override
+      public Object postProcessBeforeInitialization(
+          Object bean,
+          String beanName
+          )
+          throws
+          BeansException
+      {
+        return bean;
+      }
+
+      @Override
+      public Object postProcessAfterInitialization(
+          Object bean,
+          String beanName
+          )
+          throws
+          BeansException
+      {
+        if (bean instanceof Facebook)
+        {
+          try
+          {
+            Facebook facebook = (Facebook) bean;
+            RestTemplate template = (RestTemplate) facebook.restOperations();
+            ResponseErrorHandler handler = template.getErrorHandler();
+            template.setErrorHandler(new GraphApiErrorHandler(handler));
+            // Be sure, that the potential exception is triggered before!
+            LOG.debug("Injecting GraphApiErrorHandler in {}", facebook);
+          }
+          catch (BeanCreationException e)
+          {
+            /**
+             * This exception is called, if the BeanPostProcessor is called
+             * with a scoped bean, while the according scope is not
+             * accessible.
+             * This happens during initialization and can safely be ignored,
+             * because we only have to inject the handler for beans, that are
+             * actually usable.
+             */
+          }
+        }
+
+        return bean;
+      }
+    };
+  }
+}
\ No newline at end of file
diff --git a/src/main/resources/META-INF/spring.factories b/src/main/resources/META-INF/spring.factories
new file mode 100644 (file)
index 0000000..96f4bdd
--- /dev/null
@@ -0,0 +1,3 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+de.juplo.facebook.errors.SpringSocialAutoConfiguration,\
+de.juplo.facebook.errors.OAuth2AutoConfiguration
diff --git a/src/test/java/de/juplo/facebook/errors/FacebookErrorsOAuth2AutoConfigurationTest.java b/src/test/java/de/juplo/facebook/errors/FacebookErrorsOAuth2AutoConfigurationTest.java
new file mode 100644 (file)
index 0000000..7c1e156
--- /dev/null
@@ -0,0 +1,116 @@
+package de.juplo.facebook.errors;
+
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import org.junit.After;
+import org.junit.Test;
+import org.springframework.context.annotation.Configuration;
+import static org.junit.Assert.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer;
+import org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.mock.env.MockEnvironment;
+import org.springframework.security.oauth2.client.OAuth2RestTemplate;
+import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
+
+
+
+public class FacebookErrorsOAuth2AutoConfigurationTest
+{
+  private final Logger LOG =
+      LoggerFactory.getLogger(FacebookErrorsOAuth2AutoConfigurationTest.class);
+
+
+  private ConfigurableApplicationContext context;
+
+
+  @After
+  public void tearDown()
+  {
+    if (this.context != null)
+      this.context.close();
+  }
+
+
+  @Test
+  public void defaultNonWebConfiguration()
+  {
+    LOG.info("<-- Start Of New Test-Case!");
+    Map<String, String> properties = new HashMap<>();
+    properties.put("security.oauth2.client.client-id", "CLIENT_ID");
+    context = loadNonWebApplicationContext(EmptyConfiguration.class, properties);
+    OAuth2RestTemplate template = context.getBean(OAuth2RestTemplate.class);
+    assertEquals(OAuth2GraphApiErrorHandler.class, template.getErrorHandler().getClass());
+  }
+
+  @Test
+  public void defaultWebConfiguration()
+  {
+    LOG.info("<-- Start Of New Test-Case!");
+    Map<String, String> properties = new HashMap<>();
+    properties.put("security.oauth2.client.client-id", "CLIENT_ID");
+    context = loadWebApplicationContext(EmptyConfiguration.class, properties);
+    OAuth2RestTemplate template = context.getBean(OAuth2RestTemplate.class);
+    assertEquals(OAuth2GraphApiErrorHandler.class, template.getErrorHandler().getClass());
+  }
+
+
+  @Configuration
+  static class EmptyConfiguration
+  {
+  }
+
+
+  private ConfigurableApplicationContext loadNonWebApplicationContext(
+      Class<?> config,
+      Map<String, String> properties
+      )
+  {
+    AnnotationConfigApplicationContext ctx =
+        new AnnotationConfigApplicationContext();
+    if (properties != null)
+    {
+      MockEnvironment env = new MockEnvironment();
+      for (Entry<String, String> entry : properties.entrySet())
+        env.withProperty(entry.getKey(), entry.getValue());
+      ctx.setEnvironment(env);
+    }
+    ctx.register(FacebookErrorsOAuth2AutoConfiguration.class);
+    ctx.register(OAuth2AutoConfiguration.class);
+    ctx.register(config);
+    AutoConfigurationReportLoggingInitializer report =
+        new AutoConfigurationReportLoggingInitializer();
+    report.initialize(ctx);
+    ctx.refresh();
+    return ctx;
+  }
+
+  private ConfigurableApplicationContext loadWebApplicationContext(
+      Class<?> config,
+      Map<String, String> properties
+      )
+  {
+    AnnotationConfigWebApplicationContext ctx =
+        new AnnotationConfigWebApplicationContext();
+    if (properties != null)
+    {
+      MockEnvironment env = new MockEnvironment();
+      for (Entry<String, String> entry : properties.entrySet())
+        env.withProperty(entry.getKey(), entry.getValue());
+      ctx.setEnvironment(env);
+    }
+    ctx.register(FacebookErrorsOAuth2AutoConfiguration.class);
+    ctx.register(OAuth2AutoConfiguration.class);
+    ctx.register(config);
+    AutoConfigurationReportLoggingInitializer report =
+        new AutoConfigurationReportLoggingInitializer();
+    report.initialize(ctx);
+    ctx.refresh();
+    return ctx;
+  }
+}
diff --git a/src/test/java/de/juplo/facebook/errors/FacebookErrorsSpringSocialAutoConfigurationTest.java b/src/test/java/de/juplo/facebook/errors/FacebookErrorsSpringSocialAutoConfigurationTest.java
new file mode 100644 (file)
index 0000000..651ced5
--- /dev/null
@@ -0,0 +1,111 @@
+package de.juplo.facebook.errors;
+
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import org.junit.After;
+import org.junit.Test;
+import org.springframework.context.annotation.Configuration;
+import static org.junit.Assert.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer;
+import org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration;
+import org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Scope;
+import org.springframework.context.annotation.ScopedProxyMode;
+import org.springframework.mock.env.MockEnvironment;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.social.connect.ConnectionRepository;
+import org.springframework.social.facebook.api.Facebook;
+import org.springframework.social.facebook.api.impl.FacebookTemplate;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
+
+
+
+public class FacebookErrorsSpringSocialAutoConfigurationTest
+{
+  private final Logger LOG =
+      LoggerFactory.getLogger(FacebookErrorsSpringSocialAutoConfigurationTest.class);
+
+
+  private ConfigurableApplicationContext context;
+
+
+  @After
+  public void tearDown()
+  {
+    if (this.context != null)
+      this.context.close();
+  }
+
+
+  @Test
+  public void defaultConfiguration()
+  {
+    LOG.info("<-- Start Of New Test-Case!");
+    Map<String, String> properties = new HashMap<>();
+    properties.put("spring.social.facebook.app-id", "APP_ID");
+    properties.put("spring.social.facebook.app-secret", "APP_SECRET");
+    context = loadWebApplicationContext(EmptyConfiguration.class, properties);
+    Facebook facebook = context.getBean("facebook", Facebook.class);
+    RestTemplate template = (RestTemplate) facebook.restOperations();
+    assertEquals(GraphApiErrorHandler.class, template.getErrorHandler().getClass());
+  }
+
+
+  @Configuration
+  static class EmptyConfiguration
+  {
+    /**
+     * We have to provide this scoped bean for all our tests.
+     * Otherwise, the tests will fail, if we do not set up a complete
+     * environment with a connection-repository and a properly mocked request,
+     * that holds appropriate information to fetch a connection from that
+     * repository.
+     * @param repository
+     * @return 
+     */
+    @Bean
+    @Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
+    public Facebook facebook(ConnectionRepository repository)
+    {
+      return new FacebookTemplate("ACCESS_TOKEN");
+    }
+  }
+
+
+  private ConfigurableApplicationContext loadWebApplicationContext(
+      Class<?> config,
+      Map<String, String> properties
+      )
+  {
+    AnnotationConfigWebApplicationContext ctx =
+        new AnnotationConfigWebApplicationContext();
+    if (properties != null)
+    {
+      MockEnvironment env = new MockEnvironment();
+      for (Entry<String, String> entry : properties.entrySet())
+        env.withProperty(entry.getKey(), entry.getValue());
+      ctx.setEnvironment(env);
+    }
+    ctx.register(FacebookErrorsSpringSocialAutoConfiguration.class);
+    ctx.register(FacebookAutoConfiguration.class);
+    ctx.register(SocialWebAutoConfiguration.class);
+    ctx.register(config);
+    AutoConfigurationReportLoggingInitializer report =
+        new AutoConfigurationReportLoggingInitializer();
+    report.initialize(ctx);
+    MockHttpServletRequest request = new MockHttpServletRequest();
+    ServletRequestAttributes attributes = new ServletRequestAttributes(request);
+    RequestContextHolder.setRequestAttributes(attributes);
+    ctx.refresh();
+    return ctx;
+  }
+}
index 0638ecb..e6f6c58 100644 (file)
     <level value="trace"/>
   </logger>
 
+  <logger name="org.springframework.boot.autoconfigure.logging">
+    <level value="debug"/>
+  </logger>
+
   <root>
     <level value="info"/>
     <appender-ref ref="CONSOLE"/>