OK so I've been pulling my hair for ages now (at least so it seems!) trying to figure out what i'm doing wrong: I have a Java project in which I want to allow users who log in (via normal Spring-Security JDBC enabled repository) to grant access to their Twitter account to my application. I have registered an app with Twitter etc and have secret and access keys and everything else required to test, however, despite all the docco's read and all the configurations tried, even though my spring config creates a ConnectController
, whenever I hit the /connect/twitter
I get a 404 (not found) though there is absolutely no error generated during the context coming up in Tomcat and everything else works fine (i.e all my beans get instantianted and all the views / controllers work etc).
To my understanding -- though I do struggle with the Spring Social docco, even more so as a few of the examples shown only work on specific versions! -- simply instantiating this controller should take care of the rest -- but perhaps I'm wrong???
Here's what my config looks like -- yes it's a bit allover the place:
/WEB-INF/web.xml
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>legototies</display-name>
<servlet>
<servlet-name>legototies</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>legototies</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- allow robots.txt -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.txt</url-pattern>
</servlet-mapping>
<!-- allow favicon.ico -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.ico</url-pattern>
</servlet-mapping>
<!-- allow everything under /static/ -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/static/*</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext.xml,
/WEB-INF/spring-security.xml
</param-value>
</context-param>
<!-- Spring Security -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>
org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
/WEB-INF/legototies-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.lt" />
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:order="1">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<bean id="tilesviewResolver" class="org.springframework.web.servlet.view.tiles2.TilesViewResolver" p:order="0"/>
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/tiles.xml</value>
</list>
</property>
</bean>
</beans>
/WEB-INF/applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:task="http://www.springframework.org/schema/task"
xmlns:twitter="http://www.springframework.org/schema/social/twitter"
xmlns:social="http://www.springframework.org/schema/social"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd
http://www.springframework.org/schema/social/twitter http://www.springframework.org/schema/social/spring-social-twitter.xsd
http://www.springframework.org/schema/social http://www.springframework.org/schema/social/spring-social-1.1.xsd">
<!-- **** BEGIN: Config files **** -->
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath*:config/*.properties</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="true" />
</bean>
<!-- **** END: Config files **** -->
<context:component-scan base-package="com.lt" />
<!-- **** BEGIN: Database **** -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${database.driverClassName}" />
<property name="url" value="${database.url}" />
<property name="username" value="${database.username}" />
<property name="password" value="${database.password}" />
<property name="initialSize" value="${database.initial.size}" />
<property name="maxActive" value="${database.max.active}" />
</bean>
<bean id="localJdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg>
<ref bean="dataSource" />
</constructor-arg>
</bean>
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
</bean>
<!-- **** END: Database **** -->
<bean id="messageAssembler" class="com.lt.message.MessageAssembler" />
<!-- **** BEGIN: Scheduler **** -->
<!-- Tasks -->
<bean id="createTweetsScheduler" class="com.lt.scheduller.CreateTweetsScheduler">
<constructor-arg index="0" ref="sessionFactory" />
<constructor-arg index="1" ref="execSendTweet" />
<constructor-arg index="2" ref="messageAssembler" />
</bean>
<bean id="bookScrapingNeededTask" class="com.lt.scheduller.BooksScrapingNeededCheckTask">
<constructor-arg index="0" ref="sessionFactory" />
<constructor-arg index="1" ref="execPageScraping" />
<constructor-arg index="2" value="${shefari.htmlpath}" />
</bean>
<!-- Schedulers -->
<task:executor id="execPageScraping" pool-size="${page.scrape.threadpool.size}" />
<task:executor id="execSendTweet" pool-size="${broadcastTweets.threadpool.size}" />
<task:scheduler id="mainScheduler" pool-size="${mainScheduler.size}" />
<task:scheduled-tasks scheduler="mainScheduler">
<task:scheduled ref="createTweetsScheduler" method="run"
fixed-rate="${selectTweets.period.ms}" initial-delay="${selectTweets.initial.delay.ms}" />
<task:scheduled ref="bookScrapingNeededTask" method="run"
fixed-rate="${page.scrape.check.period.ms}" initial-delay="${page.scrape.check.initial.delay.ms}" />
</task:scheduled-tasks>
<!-- **** END: Scheduler **** -->
<bean id="textEncryptor" class="org.springframework.security.crypto.encrypt.Encryptors"
factory-method="noOpText" />
<bean id="passwordEncoder"
class="org.springframework.security.crypto.password.NoOpPasswordEncoder"
factory-method="getInstance" />
<!-- **** BEGIN: twitter/social **** -->
<social:jdbc-connection-repository />
<twitter:config app-id="${twitter.app.consumer.key}"
app-secret="${twitter.app.consumer.secret}" />
<bean id="userIdSource"
class="org.springframework.social.security.AuthenticationNameUserIdSource" />
<bean id="connectController"
class="org.springframework.social.connect.web.ConnectController">
<property name="connectInterceptors">
<list>
<bean class="com.lt.utils.TweetAfterConnectInterceptor">
<constructor-arg index="0" value="${twitter.app.connect.msg}" />
</bean>
</list>
</property>
<property name="applicationUrl" value="http://localhost:8080/" />
</bean>
<!-- **** END: twitter/social **** -->
</beans>
/WEB-INF/spring-security.xml
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="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-3.2.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<global-method-security secured-annotations="enabled" />
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/static/**" access="permitAll" />
<intercept-url pattern="/home" access="permitAll" />
<intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
</http>
<authentication-manager>
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select username,password, enabled from users where username=?"
authorities-by-username-query="select u.username, ur.authority from users u, user_roles ur
where u.id = ur.user_id and u.username =? "
/>
</authentication-provider>
</authentication-manager>
</beans:beans>
I'm not including here /WEB-INF/tiles.xml as I don't think it's relevant -- it just defines some basic templates.
Lastly, this is the project pom.xml -- please note that I'm using the latest spring social milestone (M4):
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>liviutudor</groupId>
<artifactId>legototies</artifactId>
<packaging>war</packaging>
<version>1.0.0-SNAPSHOT</version>
<name>legototies</name>
<inceptionYear>2013</inceptionYear>
<description>This is "lego toties"</description>
<url>http://legototies.com</url>
<developers>
<developer>
<name>Liviu Tudor</name>
<id>liviut</id>
<email>me at liviutudor.com</email>
</developer>
</developers>
<repositories>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>central</id>
<name>Maven Repository Switchboard</name>
<url>http://repo1.maven.org/maven2</url>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>http://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>http://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<properties>
<project.build.jdkVersion>1.6</project.build.jdkVersion>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<hibernate.version>3.6.10.Final</hibernate.version>
<jsoup.version>1.7.2</jsoup.version>
<junit.version>4.10</junit.version>
<spring.version>3.2.3.RELEASE</spring.version>
<spring.social.version>1.1.0.M4</spring.social.version>
<spring.security.version>3.1.4.RELEASE</spring.security.version>
<tiles.version>2.2.2</tiles.version>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Spring security -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${spring.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>${spring.security.version}</version>
</dependency>
<dependency> <!-- needed by spring social twitter -->
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
<version>${spring.security.version}</version>
</dependency>
<!-- Spring Social -->
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-twitter</artifactId>
<version>${spring.social.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-web</artifactId>
<version>${spring.social.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-security</artifactId>
<version>${spring.social.version}</version>
</dependency>
<!-- Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<version>1.0.0.Final</version>
</dependency>
<!-- this is needed for hibernate -->
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.0.GA</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.0.5</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>${jsoup.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tiles</groupId>
<artifactId>tiles-extras</artifactId>
<version>${tiles.version}</version>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>legototies</finalName>
<defaultGoal>install</defaultGoal>
<resources>
<resource>
<filtering>false</filtering>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<filtering>false</filtering>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${project.build.jdkVersion}</source>
<target>${project.build.jdkVersion}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
As I said everything works just fine, however, given the above, even though there is a ConnectController
defined, the /connect/twitter
(or any other /connect/...
URL) returns 404 not found. What am I missing?
Update: Logging segment regarding the connect controller
5148 [main] INFO org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped "{[/connect/{providerId}],methods=[POST],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.web.servlet.view.RedirectView org.springframework.social.connect.web.ConnectController.connect(java.lang.String,org.springframework.web.context.request.NativeWebRequest)
5148 [main] INFO org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped "{[/connect/{providerId}],methods=[DELETE],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.web.servlet.view.RedirectView org.springframework.social.connect.web.ConnectController.removeConnections(java.lang.String,org.springframework.web.context.request.NativeWebRequest)
5148 [main] INFO org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped "{[/connect/{providerId}/{providerUserId}],methods=[DELETE],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.web.servlet.view.RedirectView org.springframework.social.connect.web.ConnectController.removeConnection(java.lang.String,java.lang.String,org.springframework.web.context.request.NativeWebRequest)
5149 [main] INFO org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped "{[/connect/{providerId}],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public java.lang.String org.springframework.social.connect.web.ConnectController.connectionStatus(java.lang.String,org.springframework.web.context.request.NativeWebRequest,org.springframework.ui.Model)
5150 [main] INFO org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped "{[/connect],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public java.lang.String org.springframework.social.connect.web.ConnectController.connectionStatus(org.springframework.web.context.request.NativeWebRequest,org.springframework.ui.Model)
5150 [main] INFO org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped "{[/connect/{providerId}],methods=[GET],params=[oauth_token],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.web.servlet.view.RedirectView org.springframework.social.connect.web.ConnectController.oauth1Callback(java.lang.String,org.springframework.web.context.request.NativeWebRequest)
5151 [main] INFO org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped "{[/connect/{providerId}],methods=[GET],params=[code],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.web.servlet.view.RedirectView org.springframework.social.connect.web.ConnectController.oauth2Callback(java.lang.String,org.springframework.web.context.request.NativeWebRequest)
5152 [main] INFO org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped "{[/connect/{providerId}],methods=[GET],params=[error],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.web.servlet.view.RedirectView org.springframework.social.connect.web.ConnectController.oauth2ErrorCallback(java.lang.String,java.lang.String,java.lang.String,java.lang.String,org.springframework.web.context.request.NativeWebRequest)
http://localhost:8080
in theConnectController
definition is set only for the purpose of when testing locally to verify this wasn't causing any problems. – Jointure<twitter:config>
element as I've just realised that/connect/
on it's own as a path works, but/connect/<service>
-- e.g./connect/twitter
-- returns 404. – Jointure<twitter:config>
- it seems as if theConnectController
starts but doesn't register the twitter service (and yes, the app / secret keys are valid, they work perfectly fine in a separate branch with Twitter4J library, so it's not that)! It feels as if I'm missing one tiny thing which links the controller to the twitter service...any ideas? – Jointure/connect/...
ones so that can't be it. – JointureInternalResourceViewResolver
, I probably need a new set of glasses :s – SinkageconnectionRepository
bean whereby the#{request.userPrincipal.name}
throws an exception... – Jointure