· 7 years ago · Oct 12, 2018, 12:00 AM
1package com.example.components;
2
3import org.apache.tapestry5.BindingConstants;
4import org.apache.tapestry5.ComponentResources;
5import org.apache.tapestry5.EventConstants;
6import org.apache.tapestry5.Link;
7import org.apache.tapestry5.annotations.*;
8import org.apache.tapestry5.corelib.base.AbstractComponentEventLink;
9import org.apache.tapestry5.internal.util.CaptureResultCallback;
10import org.apache.tapestry5.ioc.annotations.Inject;
11import org.apache.tapestry5.ioc.annotations.InjectService;
12import org.springframework.social.oauth1.AuthorizedRequestToken;
13import org.springframework.social.oauth1.OAuth1Parameters;
14import org.springframework.social.oauth1.OAuth1ServiceProvider;
15import org.springframework.social.oauth1.OAuthToken;
16import org.springframework.social.twitter.api.Twitter;
17
18import java.net.MalformedURLException;
19import java.net.URL;
20
21/**
22 * This component represents link that connects users to their twitter accounts using OAuth protocol. After clicking
23 * this link users will be redirected to twitter authorization page and then returned back to the page where they come
24 * from. After twitter authorization this component triggers {@link EventConstants#SUCCESS} or
25 * {@link EventConstants#FAILURE} events according to authorization result. Success event comes with access token
26 * (token + secret) as event context, so developers can then use it to access twitter api.
27 *
28 * @author Ivan Khalopik
29 * @since 1.0
30 */
31@Events({EventConstants.SUCCESS, EventConstants.FAILURE})
32public class TwitterConnect extends AbstractComponentEventLink {
33 private static final String CONNECT_EVENT = "connect";
34 private static final String CONNECTED_EVENT = "connected";
35
36 /**
37 * This parameter defines OAuth application scope.
38 */
39 @Parameter(defaultPrefix = BindingConstants.LITERAL)
40 private String scope;
41
42 @Inject
43 private ComponentResources resources;
44
45 /**
46 * Injected spring-social OAuth service for twitter. It should be configured somewhere in application modules.
47 */
48 @InjectService("twitterService")
49 private OAuth1ServiceProvider<Twitter> twitterService;
50
51 /**
52 * Generates OAuth callbackUrl as event link to current component with internal {@code 'connected'} event.
53 * This event will be processed inside this component later.
54 *
55 * @return OAuth callbackUrl
56 */
57 @Cached
58 protected String getCallbackUrl() {
59 return resources.createEventLink(CONNECTED_EVENT).toAbsoluteURI();
60 }
61
62 /**
63 * Generates component link that will present on html markup as a connect URL. This link will produce internal
64 * {@code 'connect'} event. Then this event will be processed by this component to generate correct twitter
65 * authorization URL.
66 *
67 * @param eventContext event context
68 * @return link that will present on html markup as a connect URL
69 */
70 @Override
71 protected Link createLink(final Object[] eventContext) {
72 return resources.createEventLink(CONNECT_EVENT);
73 }
74
75 /**
76 * Event handler for internal {@code 'connect'} event. Generates correct twitter authorization URL with all needed
77 * parameters.
78 *
79 * @return twitter authorization URL
80 * @throws MalformedURLException for incorrect URL produced by twitter service
81 */
82 @OnEvent(CONNECT_EVENT)
83 URL connect() throws MalformedURLException {
84 // generate oauth parameters with callback url
85 final OAuth1Parameters parameters = new OAuth1Parameters();
86 parameters.setCallbackUrl(getCallbackUrl());
87 // build and return connection url as redirect response
88 return new URL(buildConnectUrl(parameters));
89 }
90
91 /**
92 * Event handler for internal {@code 'connected'} event. Processes reply came from twitter authorization page. If
93 * access was granted then it tries to get OAuth access token and triggers {@link EventConstants#SUCCESS} event with
94 * access token as event context. If access was denied or error occurs while getting access token then
95 * {@link EventConstants#FAILURE} event will be triggered.
96 *
97 * @param token authorized OAuth token came from twitter
98 * @param verifier authorized OAuth token secret came from twitter
99 * @param denied error came from twitter
100 * @return result came from container for triggered events or null if was not processed.
101 */
102 @OnEvent(CONNECTED_EVENT)
103 Object connected(
104 @RequestParameter(value = "oauth_token", allowBlank = true) String token,
105 @RequestParameter(value = "oauth_verifier", allowBlank = true) final String verifier,
106 @RequestParameter(value = "denied", allowBlank = true) String denied) {
107
108 final CaptureResultCallback<Object> callback = new CaptureResultCallback<Object>();
109 if (verifier != null) {
110 try {
111 // create authorized OAuth request token
112 final AuthorizedRequestToken requestToken =
113 new AuthorizedRequestToken(new OAuthToken(token, verifier), verifier);
114 // request for access token from twitter
115 final OAuthToken accessToken = twitterService.getOAuthOperations()
116 .exchangeForAccessToken(requestToken, null);
117 // create success even context
118 final Object[] context = {accessToken.getValue(), accessToken.getSecret()};
119 // trigger success event
120 final boolean handled = resources.triggerEvent(EventConstants.SUCCESS, context, callback);
121 // if event was processed return result
122 if (handled) {
123 return callback.getResult();
124 }
125 // return null if not processed
126 return null;
127 } catch (Exception e) {
128 // error occurs, so failure event will be triggered
129 }
130 }
131
132 // handle failure event if access was denied or error occurs while requesting access token
133 final boolean handled = resources.triggerEvent(EventConstants.FAILURE, new Object[]{}, callback);
134
135 // if event was processed return result
136 if (handled) {
137 return callback.getResult();
138 }
139 // return null if not processed
140 return null;
141 }
142
143 /**
144 * Generates OAuth authorization URL according to specified parameters.
145 *
146 * @param parameters OAuth parameters
147 * @return OAuth authorization URL
148 */
149 private String buildConnectUrl(final OAuth1Parameters parameters) {
150 final OAuthToken token = twitterService.getOAuthOperations().fetchRequestToken(getCallbackUrl(), null);
151 return twitterService.getOAuthOperations().buildAuthorizeUrl(token.getValue(), parameters);
152 }
153}