What's wrong with my spring-social ConnectController? - spring-mvc

I'm trying to make a Spring Boot app that will connect to Fitbit's api using spring-social. I've (half-way) implemented a ConnectionFactory and it's dependencies for Fitbit, and am trying to consume it from my app. Part of this involves starting up a ConnectController to handle the OAuth2 "dance".
When I try to hit the ConnectController through my browser at http://localhost:8080/connect or http://localhost:8080/connect/fitbit I get redirected to the whitelable error page with the message:
There was an unexpected error (type=Method Not Allowed, status=405).
Request method 'GET' not supported
I don't really understand what I'm seeing, but when I set DEBUG level logging and use some breakpoints it looks like hitting /connect from the browser results in Spring trying to find something called connect/status and hitting /connect/fitbit result in spring trying to find something named /connect/fitbitConnect and then trying to internally make a GET request to /connect/connect/fitbitConnect.
In both cases it looks like the methods on ConnectController corresponding to /connect and /connect/{providerId} get called fine, and then Spring bombs when it goes looking for all that other stuff.
Here is the SocialConfigurer implementation I'm using which creates the ConnectController bean:
#Configuration
#EnableSocial
#PropertySource("${properties.path}/fitbot-service.properties")
public class SpringSocialConfig implements SocialConfigurer{
#Override
public void addConnectionFactories(ConnectionFactoryConfigurer connectionFactoryConfigurer, Environment environment) {
connectionFactoryConfigurer.addConnectionFactory(new FitbitConnectionFactory(
environment.getProperty("fitbit.clientId"),
environment.getProperty("fitbit.clientSecret")
));
}
#Override
public UserIdSource getUserIdSource() {
return new SessionUserIdSource();
}
#Override
public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
return new InMemoryUsersConnectionRepository(connectionFactoryLocator);
}
#Bean
public ConnectController connectController(ConnectionFactoryLocator connectionFactoryLocator, ConnectionRepository connectionRepository) {
return new ConnectController(connectionFactoryLocator, connectionRepository);
}
}
What on earth is going on here? What am I doing wrong?

I believe this to be related to your question regarding GET vs. POST in ConnectController, so you may have already answered this for yourself. Nonetheless, let me clarify why it's looking for connect/status and connect/fitbitConnect.
Those are view names. When you do a GET for /connect, you're asking ConnectController to fetch connection status for all providers and to place it in the model, after which it will forward that model to a view whose logical name is "connect/status". Usually this is a JSP at the path "/connect/status.jsp" or maybe a Thymeleaf template at "/connect/status.html", but it can be any view following the rules of whatever Spring MVC view resolvers are in play.
Likewise, a GET request for /connect/fitbit is asking ConnectController to fetch connection status for the "fitbit" provider and to place that information in the model and forward it on to a view whose name is "/connect/fitbitConnect" (if there isn't a connection) or "/connect/fitbitConnected" (if there is a connection).
Aside from answering your question, may I also request that you tell me more about your FitBit Spring Social provider project? Is it modeled after other community-led Spring Social projects? In other words, is it a standalone extension to Spring Social that others may use? If so, tell me where it is in GitHub and I'll be happy to add it to the "Community Projects" section at http://projects.spring.io/spring-social/.

Related

How to access "session" scoped beans in a Spring WebSocket handler (not "websocket" scope)

In a raw Spring WebSocket application (not using sockjs/STOMP or any other middleware), how can I have Spring inject beans that have been registered in the HTTP session scope so that they can be used by code in my WebSocketHandler bean?
Note that what I am not asking is any of these questions:
How do I create beans in a scope that is accessible to all handler invocations for the same WebSocket session (e.g. as described in the answer to Request or Session scope in Spring Websocket). The beans I need to access already exist in the scope for the HTTP session
How do I (programatically) access objects in the servlet container's HTTP session storage (I haven't tried to do this, but I'm pretty sure the answer involves using an HttpSessionHandshakeInterceptor), but that doesn't get me injection of Spring scoped dependencies.
How to use a ScopedProxy to pass beans between code in different scopes (e.g. as described here); I'm already familiar with how to do this, but attempting to do so for a WebSocketHandler causes an error because the session scope hasn't been bound to the thread at the point the object is accessed.
How to access the current security principal -- again, very useful, but not what I'm currently trying to achieve.
What I'm hoping to do is provide a simple framework that allows for the traditional HTTP-request initiated parts of an MVC application to communicate directly with a WebSocket protocol (for sending simple push updates to the client). What I want to be able to do is push data into a session scoped object from the MVC controller and pull it out in the websocket handler. I would like the simplest possible API for this from the MVC controller's perspective, which if it is possible to just use a session-scoped bean for this would be ideal. If you have any other ideas about very simple ways of sharing this data, I'd also like to hear those in case this approach isn't possible.
You can also use Java API for websocket. This link https://spring.io/blog/2013/05/23/spring-framework-4-0-m1-websocket-support
explains how to do this with Spring.
Ufortunately, something like this
#ServerEndpoint(value = "/sample", configurator = SpringConfigurator.class)
public class SampleEndpoint {
private SessionScopedBean sessionScopedBean;
#Autowired
public SampleEndpoint(SessionScopedBean sessionScopedBean) {
this.sessionScopedBean = sessionScopedBean;
}
}
causes exception (because we're trying to access bean outside its scope), but for singleton and prototype beans it works well.
To work with session attributes you can modify the hanshake and pass required attributes:
public class CustomWebSocketConfigurator extends SpringConfigurator {
#Override
public void modifyHandshake(ServerEndpointConfig config,
HandshakeRequest request,
HandshakeResponse response) {
//put attributes from http session to websocket session
HttpSession httpSession = (HttpSession) request.getHttpSession();
config.getUserProperties().put("some_attribute",
httpSession.getAttribute("some_attribute_in_http_session"));
}
}
P. S. More a comment than an answer. I just wanted to add another way of handling session attributes in websocket to your question-answer. I have been searching the web for exactly the same issue and the way showed above seems to me the most systematic approach to handling the session data in websocket.

How to Set Request Headers Using a Feign Client?

We are developing a suite of Microservices using Spring Cloud framework and one of the the things that we need to do is to set request headers. I know I can pass a parameter #RequestHeader to a Feign method but the value needs to come from another bean. I don't know if SPEL can be used for a Feign param value.
I was thinking that this is a common enough use case for most clients so there'd be some examples, but so far I've not found any. Of course I can dig through the Spring course code and try to override the default Feign configuration but it kinda defeats the purpose of a declarative client if I've to write a lot of code to achieve this.
Any thoughts?
I have done this before using a RequestInterceptor as follows:
#Component
public class MyRequestInterceptor implements RequestInterceptor {
#Override
public void apply(RequestTemplate template) {
template.headers(getHeadersFromWherever());
}
}
You can find some more useful information here:
https://github.com/Netflix/feign#user-content-setting-headers-per-target

spring-cloud with RestTemplate//Ribbon/Eureka - retry when server not available

I managed to successfully get my RestTemplate client discover remote service using Eureka and forward calls to it using Ribbon as described in the documentation.
Basically, it was just a matter of adding the following annotations of my Application class and let the magic of Spring-Boot do the rest:
#Configuration
#ComponentScan
#EnableAutoConfiguration
#EnableDiscoveryClient
(PS: you noticed I'm using spring-cloud:1.0.0-SNAPSHOT-BUILD and not 1.0.0.M3 - but this doesn't seem to affect my problem).
When two service instances are started, the rest-template client successfully load balance requests between the two. However, the client won't fallback to the second instance if the first is stopped before the Eureka load balancer notices, instead an exception is thrown.
Hence my question: is there a way to configure the RestTemplate/Ribbon/Eureka stack to automatically retry the call to another instance if the one selected the first place is not available? Zuul proxy and feign clients do this "out of the box" so I believe the library holds the necessary features...
Any idea/hint?
Thx,
/Bertrand
The RestTemplate support on its own does not know how to do any retrying (whereas the Feign client and the proxy support in Spring Cloud does, as you noticed). I think this is probably a good things because it gives you the option to add it yourself. For instance, using Spring Retry you can do it in a simple declarative style:
#Retryable
public Object doSomething() {
// use your RestTemplate here
}
(and add #EnableRetry to your #Configuration). It makes a nice combination with #HystrixCommand (from Spring Cloud / Javanica):
#HystrixCommand
#Retryable
public Object doSomething() {
// use your RestTemplate here
}
In this form, every failure counts towards the circuit breaker metrics (maybe we could change that, or maybe it makes sense to leave it like that), even if the retry is successful.
I couldn't get it to work with both #HystrixCommand and #Retryable, regardless of order of annotations on #Configuration class or on #Retryable method due to order of interceptors. I solved this by creating another class with the matching set of methods and had the #HystrixCommand annotated methods delegate to the corresponding #Retryable method in the second class. You could probably have the two classes implement the same interface. This is kind of a pain in the butt, but until order can be configured, this is all I could come up with. Still waiting on a real solution from Dave Syer and the spring cloud guys.
public class HystrixWrapper {
#Autowired
private RetryableWrapper retryableWrapper;
#HystrixCommand
public Response doSomething(...) {
return retryableWrapper.doSomething(...);
}
}
public class RetryableWrapper {
#Autowired
private RestTemplate restTemplate;
#Retryable
public Response doSomething(...) {
// do something with restTemplate;
}
}

Spring Social Facebook

I am developing with Spring Social and Thymeleaf from the quick start example, but I realised that it only supports one Facebook object per controller. This means the sample can't provide support for multiple users and I am guessing it has to do with the #Scope of the variable. Its runs in a Spring boot container and I wonder how I can configure this so that each session has its own Facebook object.
As you suggested, the Facebook object should be configured with request scope. If you're using the configuration support and/or Spring Boot, then it will be request scoped. Therefore, even though the controller is injected once with a Facebook instance, that instance is really a proxy that will delegate to a real FacebookTemplate instance that is created at request time for the authenticated user.
I can only assume that you're referring to the getting started guide example at http://spring.io/guides/gs/accessing-facebook/. In that case, it's using the most simple Spring Boot autoconfiguration possible for Spring Social, which includes a basic (yet not intended for production) implementation of UserIdSource which always returns "anonymous" as the user ID. Therefore, after you create the first Facebook connection, the second browser tries to find a connection for "anonymous", finds it, and gives you an authorized Facebook object.
This may seem peculiar, but it is an example app intended to get you started...and it does that. All you need to do to get a real UserIdSource is to add Spring Security to the project. That will tell Spring Social autoconfiguration to configure a UserIdSource that fetches the current user ID from the security context. This reflects a more real-world use of Spring Social, albeit obviously more involved and beyond the scope of the getting started guide.
But you can look at https://github.com/spring-projects/spring-social-samples/tree/master/spring-social-showcase-boot for a more complete example of Spring Social within Spring Boot.
Spring Boot autoconfigures a lot of things behind the scenes. It does autoconfigure the Facebook, LinkedIn and Twitter properties and sets up the connection factories for social providers.
However, the implementation of UserIdSource always returns “anonymous” as the user ID. Once the first Facebook connection is established the second browser will try to find a connection for “anonymous” which it finds and gives you an authorised Facebook object.
#Configuration
#EnableSocial
#ConditionalOnWebApplication
#ConditionalOnMissingClass("org.springframework.security.core.context.SecurityContextHolder")
protected static class AnonymousUserIdSourceConfig extends SocialConfigurerAdapter {
#Override
public UserIdSource getUserIdSource() {
return new UserIdSource() {
#Override
public String getUserId() {
return "anonymous";
}
};
}
}
Solution
The solution is to override the “anonymous” as the UserId for each new user/session. So for each session, we can simply return a SessionID, however, it may not be unique enough to identify users, especially if it’s being cached or stored somewhere in a connection database.
#Override
public String getUserId() {
RequestAttributes request = RequestContextHolder.currentRequestAttributes();
String uuid = (String) request.getAttribute("_socialUserUUID", RequestAttributes.SCOPE_SESSION);
if (uuid == null) {
uuid = UUID.randomUUID().toString();
request.setAttribute("_socialUserUUID", uuid, RequestAttributes.SCOPE_SESSION);
}
return uuid;
}
The solution for above problem has been talked about in detail over here

Configuring dependency injection with ASP.NET Web API 2.1

I'm creating an ASP.NET Web API 2.1 site and as I want to inject dependencies directly into the controllers, I've created my own implementation of IDependencyResolver so that StructureMap will handle that for me.
public class StructureMapDependencyResolver : IDependencyResolver
{
public IDependencyScope BeginScope()
{
return this;
}
public object GetService(Type serviceType)
{
return ObjectFactory.GetInstance(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return ObjectFactory.GetAllInstances(serviceType).Cast<object>();
}
public void Dispose()
{
}
}
I've then told Web API to use this class by adding this line to the Application_Start method in Global.asax
GlobalConfiguration.Configuration.DependencyResolver = new StructureMapDependencyResolver();
That compiled but when I tried to access any of the API methods in a browser I got an error like this
No Default Instance defined for PluginFamily System.Web.Http.Hosting.IHostBufferPolicySelector, System.Web.Http
That one was relatively easy to solve as I added a line to my StructureMap configuration
this.For<IHostBufferPolicySelector>().Use<WebHostBufferPolicySelector>();
However then I got other similar errors for other System.Web.Http classes and while I could resolve some of them I am stuck on how to deal with 3 of them, namely ITraceManager, IExceptionHandler and IContentNegotiator.
The issue is that TraceManager which seems to be the default implementation of ITraceManager is an internal class and so I can't reference it in my StructureMap configuration.
So am I going about this completely the wrong way or is there some other way to inject these internal classes?
I'd like to give you a suggestion and explanation why not to go this way, and how to do it differently (I'd even say better and properly).
The full and complete explanation of the inappropriate IDependencyResolver design could be found here: Dependency Injection and Lifetime Management with ASP.NET Web API by Mark Seemann
Let me cite these essential parts:
The problem with IDependencyResolver
The main problem with IDependencyResolver is that it's essentially a Service Locator. There are many problems with the Service Locator anti-pattern, but most of them I've already described elsewhere on this blog (and in my book). One disadvantage of Service Locator that I haven't yet written so much about is that within each call to GetService there's no context at all. This is a general problem with the Service Locator anti-pattern, not just with IDependencyResolver.
And also:
...dependency graph need to know something about the context. What was the request URL? What was the base address (host name etc.) requested? How can you share dependency instances within a single request? To answer such questions, you must know about the context, and IDependencyResolver doesn't provide this information.
In short, IDependencyResolver isn't the right hook to compose dependency graphs. **Fortunately, the ASP.NET Web API has a better extensibility point for this purpose. **
ServiceActivator
So, the answer in this scenario would be the ServiceActivator. Please take a look at this answer:
WebAPI + APIController with structureMap
An example of the ServiceActivator:
public class ServiceActivator : IHttpControllerActivator
{
public ServiceActivator(HttpConfiguration configuration) {}
public IHttpController Create(HttpRequestMessage request
, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
var controller = ObjectFactory.GetInstance(controllerType) as IHttpController;
return controller;
}
}
All we can do with StructureMap, is in place. The key features of the Web API framework are still in place... we do not have to hack them. And we are also rather using DI/IoC then Service locator
Just try using UnityHierarchicalDependencyResolver instead of the other one. It worked for me. This is for future reference if somebody would like to use Unity

Resources