9

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)
Liv
  • 6,006
  • 1
  • 22
  • 29
  • BTW the value for `http://localhost:8080` in the `ConnectController` definition is set only for the purpose of when testing locally to verify this wasn't causing any problems. – Liv Oct 23 '13 at 23:57
  • Actually I think the problem might be with the `` element as I've just realised that `/connect/` on it's own as a path works, but `/connect/` -- e.g. `/connect/twitter` -- returns 404. – Liv Oct 26 '13 at 04:01
  • Just a heads-up to everyone who replied to this: thank you so much! I've been busy and unable to check this, but will spend time over the weekend and will choose a winning answer by Sunday/Monday. (I'm in California as well so PST timezone just to put it in perspective). – Liv Nov 02 '13 at 00:09
  • OK so I'm afraid I cannot award yet the award to anyone :( Happy in fact to double the bounty if possible, as long as I get this sorted. I really think now that the problem is with `` - it seems as if the `ConnectController` 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? – Liv Nov 02 '13 at 18:59
  • You are using Tiles for view resolving make sure that tiles knows which views to resolve. – M. Deinum Nov 04 '13 at 07:31
  • @M. Deinum, please notice that there are 2 view resolvers, weighed differently, first will be the tile resolvers then falls back onto the jsp one. The view resolving works fine for all other paths apart from the `/connect/...` ones so that can't be it. – Liv Nov 04 '13 at 22:11
  • I totally missed the `InternalResourceViewResolver`, I probably need a new set of glasses :s – M. Deinum Nov 05 '13 at 07:05
  • Update: see my conversation here: http://chat.stackoverflow.com/rooms/41225/discussion-between-liv-and-m-deinum it seems there is a problem with configuring the `connectionRepository` bean whereby the `#{request.userPrincipal.name}` throws an exception... – Liv Nov 15 '13 at 23:39

4 Answers4

4

Your ConnectController is in the wrong configuration file. You should move it to the legototies-servlet.xml.

The used HandlerMapping implementations only detect (@)Controller beans in the local context (the one loaded by the DispatcherServlet) NOT in the parent context (loaded by the ContextLoaderListener). So your ConnectController is configured but isn't doing anything because it isn't detected.

There is also another problem with your configuration, you are loading all the beans twice. This is due to the way you have configured component scanning. In both configuration files there is a <context:component-scan base-package="com.lt" /> which basically duplicates all beans. In general you should configure the DispatcherServlet to load only @Controllers and the ContextLoaderListener to load everything BUT @Controllers.

<context:component-scan base-package="com.lt">
    <context:exclude-filter type="annotation" value="org.springframework.stereotype.Controller" />
</context:component-scan>

and for the DispatcherServlet

<context:component-scan base-package="com.lt" use-default-filters="false">
    <context:include-filter type="annotation" value="org.springframework.stereotype.Controller" />
</context:component-scan>

And at first glance you also have an error in your database configuration. You are using hibernate but have configured a DataSourceTransactionManager whereas you should configure a HibernateTransactionManager. The latter is perfectly capable of controlling plain JDBC transactions.

M. Deinum
  • 115,695
  • 22
  • 220
  • 224
  • just trying this out-as a heads-up, the example you gave is slightly wrong: for `` and ``, the attribute is not `value=...` but `expression=...` – Liv Nov 02 '13 at 18:45
  • having added the `` in `applicationContext.xml` and the `` in `legototies-servlet.xml` I still see exactly the same result - 404 everywhere! :( So that's not it :( – Liv Nov 02 '13 at 18:48
  • Also I have moved the ` entirely in the `legototies-servlet.xml` with the very same result :( – Liv Nov 02 '13 at 18:50
  • 1
    The error is that your `ConnectController` is in the wrong context and not detected the other ones are suggestions to improve your context (probably should have put that in a comment). To get a feeling to what is happening enable DEBUG logging for `org.springframework.web` (and probably `org.springframework.social`) and you should get more information on what is going wrong. – M. Deinum Nov 04 '13 at 07:28
  • OK I guess I ought to configure extended debug logging and go through it line by line. But as a heads-up, simply declaring the connect controller in a separate file didn't make any difference. (Do I need to move everything related to twitter config in the `legototies-servlet.xml` too?) – Liv Nov 04 '13 at 22:12
  • 1
    Moving it to another file doesn't matter, it has to be loaded by the `DispatcherServlet` to be detected. As long as that isn't happening the controller is basically undetected. – M. Deinum Nov 05 '13 at 07:06
  • I will do once I get it it configured and starting up - currently, being in a hurry I had no logging set up so just setting up SLF5J on it. – Liv Nov 05 '13 at 07:41
  • Just got a dump of the logging -- i'm updating the main body of this. – Liv Nov 05 '13 at 22:44
  • Actually it turns out the log file is too large and can't add it to the main text :( I can't see anything in there about spring social I have to say - in particular no reference at all to Twitter! I see the connect controller is initialized and maps "stuff" correctly but I am back to my first conclusion that the twitter bit never actually works. – Liv Nov 05 '13 at 22:52
  • Crap! I can't add a few lines of logging here either :( damn! – Liv Nov 05 '13 at 22:54
  • You can put it on pastebin and post a link here. What I'm interested in is what happens when the request is handled by the `DispatcherServlet`, because from my point it still looks like the controller isn't called. – M. Deinum Nov 06 '13 at 09:44
  • By the way there is no logging when i access anything under `/connect/*` – Liv Nov 07 '13 at 22:29
  • 1
    There should be some logging of the `DispatcherServlet` receiving and processing the request. You've only added the startup log, I would be interested in seeing the log of a request comming in. The logging at least shows the the controller is detected and registered. – M. Deinum Nov 08 '13 at 10:17
  • I've checked and checked again, once this starts, that's all the logging i get :O accessing any url's doesn't render anything in the console... Here's my `log4j.xml` file : http://pastebin.com/xUpea0Xu – Liv Nov 14 '13 at 21:45
  • 1
    Then either you have a problem in your logging configuration or your `DispatcherServlet` is doing nothing at all.. – M. Deinum Nov 14 '13 at 21:48
  • I hear you... if the dispatcher did nothing though how come it routes the requests for `/signup`, `/books/` etc to the correct controller? :O Hitting those urls shown in the logs (e.g. `/in/books` etc) definitely ends up on the right page... though no logging is generated :O – Liv Nov 14 '13 at 21:59
  • It's pretty simple :O http://pastebin.com/xUpea0Xu It gets passed to tomcat as `-Dlog4j.configuration.file=file:/....` – Liv Nov 14 '13 at 22:14
  • 1
    Well you should at least see that the `DispatcherServlet` is handling the request. Try enabling *trace* logging for `org.springframework.web`. But as mentioned you should already see something. – M. Deinum Nov 14 '13 at 22:25
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/41225/discussion-between-liv-and-m-deinum) – Liv Nov 14 '13 at 23:02
  • To everyone who was following this, I seem to have found the issue -- see my own answer below. I will see if I can award @M. Deinum some points though for all of his ongoing suggestions. – Liv Nov 16 '13 at 02:36
  • The more I look into this, you were right about the controller being in the wrong config file and a bunch of other things so I'm awarding you this bounty. – Liv Nov 21 '13 at 20:06
3

Here you need to override some methods from ConnectController. Here I give you some code snippet (commented) which might be useful to you. The default view for the ConnectController is /connect/{providerID}Connected - once authorized and connected & /connect/{providerID}Connect - once disconnect this is the default view. You are getting 404 Error because, you don't have connect folder and twitterConnected.jsp inside that. Just to test you create a connect folder and put 1.twitterConnected.jsp and 2.twitterConnect.jsp inside that and try connecting it before you subclass your ConnectController as shown below.

@Controller
public class CustomConnectController extends ConnectController {
    @Inject
    public CustomConnectController(
            ConnectionFactoryLocator connectionFactoryLocator,
            ConnectionRepository connectionRepository) {
        super(connectionFactoryLocator, connectionRepository);
    }
    //This connectedView will be called after user authorize twitter app.  So here you can redirect   
    //users to the page you need.
    @Override
    protected String connectedView(String providerId){
        return "redirect:/user/profile";
    }
    //This connectView will be called if user disconnect from social media.  Here you can redirect 
    //them once they got disconnected.  
    @Override
    protected String connectView(String providerId) {
        return "redirect:/connect";
    }
}

Just try it and let me know your output.

Vignesh Gopalakrishnan
  • 1,962
  • 9
  • 31
  • 53
  • See my comments above, I DO have the 2 views you have mentioned. Nevertheless, I have commented out the original `ConnectController` and implemented the one you suggested. And still hitting the `/connect/twitterConnect` renders a 404. (the controller bean though DOES get created but doesn't seem to do anything!) – Liv Nov 02 '13 at 18:55
1

1) Goes on top of web.xml ( you want security filter to be first guy to get the request)

 <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>

2) you are using tiles, so you wont be using jsp view resolver. change it to

<bean id="viewResolver"
        class="org.springframework.web.servlet.view.UrlBasedViewResolver">
        <property name="viewClass"
            value="org.springframework.web.servlet.view.tiles2.TilesView" />
    </bean>

3)servlet name has to match your servlet config file name.

EDIT: Also proper way to load context

  <servlet>
            <servlet-name>appServlet</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet
            </servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>/WEB-INF/appServlet-context.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>

change appServlet to your servlet config file.

kyla
  • 396
  • 1
  • 8
  • Could you also post your controller? – kyla Nov 01 '13 at 18:36
  • you have tiles view resolver configured and as well as jsp view resolver. lets say you return view name("index") in your controller when request comes as connect/twitter, so one of the things happen here: it either goes and tries to find(in tiles.xml) , or jsp view resolver is going to append /WEB-INF/jsp/index.jsp. So for you app which one fits? Remove the other one(use only one). – kyla Nov 04 '13 at 15:41
  • Please see my comment above, you can have 2 views resolvers, they are weighed differently so they get chained. – Liv Nov 04 '13 at 22:13
  • since you are trying to use multiple view resolvers here is the post about it http://stackoverflow.com/questions/1029193/how-to-use-multiple-viewresolvers-in-spring – kyla Nov 04 '13 at 22:23
  • Thank you, that looks like it's based on an older Spring library though. The p-namespace used above achieves the same: http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-p-namespace – Liv Nov 04 '13 at 22:42
1

OK so I got it to work though somehow still not sure as to what the issue was -- I think it's somewhere in between an issue with the "shortcut" spring tags and a misconfigured security path. So it turns out that my spring-security allowed unlogged in access to /connect/.... at which point the #{request.userPrincipal.name} didn't evaluate correctly and was throwing an exception. Being spring, without logging turned on, I never spotted it, and everything was failing quietly. Having re-configured the whole twitter/social support as follows:

    <bean id="connectionFactoryLocator"
        class="org.springframework.social.connect.support.ConnectionFactoryRegistry">
        <property name="connectionFactories">
            <list>
                <ref bean="twitterConnectFactory" />
            </list>
        </property>
    </bean>
    <bean id="twitterConnectFactory" class="org.springframework.social.twitter.connect.TwitterConnectionFactory">
        <constructor-arg value="${twitter.app.consumer.key}" />
        <constructor-arg value="${twitter.app.consumer.secret}" />
    </bean> 

    <bean id="usersConnectionRepository"
        class="org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository">
        <constructor-arg ref="dataSource" />
        <constructor-arg ref="connectionFactoryLocator" />
        <constructor-arg ref="textEncryptor" />
    </bean>

    <bean id="connectionRepository" factory-method="createConnectionRepository"
        factory-bean="usersConnectionRepository" scope="request">
        <constructor-arg value="#{request.userPrincipal.name}" />
        <aop:scoped-proxy proxy-target-class="false" />
    </bean>

Also the connectcontroller has been configured :

<bean id="connectController"
    class="org.springframework.social.connect.web.ConnectController">
    <constructor-arg ref="connectionFactoryLocator" />
    <constructor-arg ref="connectionRepository" />
    <property name="connectInterceptors">
        <list>
            <bean class="com.lt.utils.TweetAfterConnectInterceptor">
                <constructor-arg index="0" value="${twitter.app.connect.msg}" />
            </bean>
        </list>
    </property>
</bean>

And made the changes in my spring security to actually have the user logged in in order to access the /connect... url's, this is now working. I didn't use a custom connect controller as suggested here either, and relied on the standard one. Here's my spring-security.xml for reference as well:

<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="/in/**" access="hasRole('ROLE_USER')" />
        <intercept-url pattern="/connect" access="hasRole('ROLE_USER')" />
        <intercept-url pattern="/connect/**" access="hasRole('ROLE_USER')" />
        <intercept-url pattern="/*" access="permitAll" />
        <intercept-url pattern="/static/**" access="permitAll" />
        <intercept-url pattern="/login" access="permitAll" />
        <intercept-url pattern="/loginfailed" access="permitAll" />


        <form-login login-page="/login" default-target-url="/" authentication-failure-url="/loginfailed" />
        <logout logout-success-url="/logout" />
    </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>
Liv
  • 6,006
  • 1
  • 22
  • 29