1
1
package org .sharextras .webscripts .connector ;
2
2
3
3
import java .io .IOException ;
4
+ import java .io .PrintWriter ;
5
+ import java .io .StringWriter ;
4
6
import java .util .HashMap ;
5
7
import java .util .Map ;
6
8
15
17
import org .springframework .extensions .surf .exception .ConnectorServiceException ;
16
18
import org .springframework .extensions .surf .exception .CredentialVaultProviderException ;
17
19
import org .springframework .extensions .surf .util .FakeHttpServletResponse ;
20
+ import org .springframework .extensions .webscripts .Format ;
18
21
import org .springframework .extensions .webscripts .connector .ConnectorContext ;
19
22
import org .springframework .extensions .webscripts .connector .ConnectorService ;
20
23
import org .springframework .extensions .webscripts .connector .Credentials ;
21
24
import org .springframework .extensions .webscripts .connector .HttpConnector ;
22
25
import org .springframework .extensions .webscripts .connector .RemoteClient ;
23
26
import org .springframework .extensions .webscripts .connector .Response ;
24
27
import org .springframework .extensions .webscripts .connector .ResponseStatus ;
28
+ import org .springframework .extensions .webscripts .json .JSONWriter ;
25
29
26
30
/**
27
31
* Connector for connecting to OAuth 2.0-protected resources
@@ -81,103 +85,105 @@ public Response call(String uri, ConnectorContext context, HttpServletRequest re
81
85
try
82
86
{
83
87
Response resp = null ;
84
- HttpSession session = req .getSession ();
88
+ HttpSession session = req .getSession (false ); // TODO check session is non-null
85
89
if (!hasAccessToken (session ))
86
90
{
87
91
loadTokens (uri , req );
88
92
}
89
- if (logger .isDebugEnabled ())
90
- logger .debug ("Loading resource " + uri + " - first attempt" );
93
+
91
94
if (hasAccessToken (session ))
92
95
{
93
- context .setCommitResponseOnAuthenticationError (false );
94
-
95
- // Wrap the response object, since it gets committed straight away
96
+ // Wrap the response object, since it gets committed straight away, and we may need to retry
96
97
FakeHttpServletResponse wrappedRes = new FakeHttpServletResponse (res );
98
+
99
+ // First call
100
+ context .setCommitResponseOnAuthenticationError (false );
101
+ if (logger .isDebugEnabled ())
102
+ logger .debug ("Loading resource " + uri + " - first attempt" );
97
103
resp = callInternal (uri , context , req , wrappedRes );
104
+
98
105
if (logger .isDebugEnabled ())
99
106
logger .debug ("Response status " + resp .getStatus ().getCode () + " " + resp .getStatus ().getCodeName ());
100
- // We could have a cached access token which has been updated in the repo
107
+
108
+ // We could have a revoked or expired access token cached which has been updated in the repo
101
109
102
110
if (resp .getStatus ().getCode () == ResponseStatus .STATUS_UNAUTHORIZED ||
103
111
resp .getStatus ().getCode () == ResponseStatus .STATUS_FORBIDDEN )
104
112
{
105
113
if (logger .isDebugEnabled ())
106
114
logger .debug ("Loading resource " + uri + " - second attempt" );
115
+
107
116
loadTokens (uri , req );
108
- // Retry the operation
117
+
118
+ // Retry the operation - second call
109
119
if (hasAccessToken (session ))
110
120
{
111
121
context .setCommitResponseOnAuthenticationError (true );
112
122
try
113
123
{
114
124
resp = callInternal (uri , context , req , res );
125
+
115
126
if (logger .isDebugEnabled ())
116
127
logger .debug ("Response status " + resp .getStatus ().getCode () + " " + resp .getStatus ().getCodeName ());
117
128
}
118
129
catch (Throwable t )
119
130
{
120
- logger .error ("Encountered error when attempting to reload" , t );
131
+ writeError (res , ResponseStatus .STATUS_INTERNAL_SERVER_ERROR ,
132
+ "ERR_CALLOUT" ,
133
+ "Encountered error when attempting to reload" ,
134
+ t );
135
+ return null ;
121
136
}
122
137
}
123
138
else
124
139
{
125
- throw new RuntimeException ("No access token is present" );
140
+ writeError (res , ResponseStatus .STATUS_UNAUTHORIZED ,
141
+ "NO_TOKEN" ,
142
+ "No access token is present" ,
143
+ null );
144
+ return null ;
126
145
}
127
146
}
128
147
else
129
148
{
130
- res .setStatus (wrappedRes .getStatus ());
131
- res .setCharacterEncoding (wrappedRes .getCharacterEncoding ());
132
- // Copy headers over
133
- for (Object hdrname : wrappedRes .getHeaderNames ())
134
- {
135
- res .setHeader ((String ) hdrname , (String ) wrappedRes .getHeader ((String ) hdrname ));
136
- }
137
- res .getOutputStream ().write (wrappedRes .getContentAsByteArray ());
138
- res .flushBuffer ();
149
+ copyResponseContent (wrappedRes , res , true );
139
150
}
140
151
}
141
152
else
142
153
{
143
- // TODO Support unauthenticated access if allowed by the connector instance
144
- ResponseStatus status = new ResponseStatus ();
145
- status .setCode (ResponseStatus .STATUS_UNAUTHORIZED );
146
- status .setMessage ("No access token is present" );
147
- resp = new Response (status );
148
- res .setStatus (ResponseStatus .STATUS_UNAUTHORIZED );
149
- //throw new RuntimeException("No access token is present");
154
+ writeError (res , ResponseStatus .STATUS_UNAUTHORIZED ,
155
+ "NO_TOKEN" ,
156
+ "No access token is present" ,
157
+ null );
158
+ return null ;
150
159
151
160
}
152
161
153
162
return resp ;
154
163
}
155
- // TODO return responses with errors when we are able to
156
164
catch (CredentialVaultProviderException e )
157
165
{
158
- /*
159
- * ResponseStatus status = new ResponseStatus();
160
- status.setCode(ResponseStatus.STATUS_INTERNAL_SERVER_ERROR);
161
- status.setMessage("Unable to retrieve OAuth credentials from credential vault");
162
- status.setException(e);
163
- return new Response(status);
164
- */
165
- throw new RuntimeException ("Unable to retrieve OAuth credentials from credential vault" , e );
166
+ writeError (res , ResponseStatus .STATUS_INTERNAL_SERVER_ERROR ,
167
+ "ERR_CREDENTIALSTORE" ,
168
+ "Unable to load credential store" ,
169
+ e );
170
+ return null ;
166
171
}
167
172
catch (ConnectorServiceException e )
168
173
{
169
- /*
170
- ResponseStatus status = new ResponseStatus();
171
- status.setCode(ResponseStatus.STATUS_INTERNAL_SERVER_ERROR);
172
- status.setMessage("Unable to access Alfresco connector in order to retrieve OAuth credentials");
173
- status.setException(e);
174
- return new Response(status);
175
- */
176
- throw new RuntimeException ("Unable to access Alfresco connector in order to retrieve OAuth credentials" , e );
174
+ writeError (res , ResponseStatus .STATUS_INTERNAL_SERVER_ERROR ,
175
+ "ERR_FETCH_CREDENTIALS" ,
176
+ "Unable to retrieve OAuth credentials from credential vault" ,
177
+ e );
178
+ return null ;
177
179
}
178
180
catch (IOException e )
179
181
{
180
- throw new RuntimeException ("Error encountered copying outputstream" , e );
182
+ writeError (res , ResponseStatus .STATUS_INTERNAL_SERVER_ERROR ,
183
+ "ERR_COPY_RESPONSE" ,
184
+ "Error encountered copying outputstream" ,
185
+ e );
186
+ return null ;
181
187
}
182
188
}
183
189
@@ -190,7 +196,7 @@ protected void loadTokens(String uri, HttpServletRequest request) throws Credent
190
196
{
191
197
logger .debug ("Loading OAuth tokens" );
192
198
193
- HttpSession session = request .getSession ();
199
+ HttpSession session = request .getSession (false );
194
200
if (session != null )
195
201
{
196
202
String userId = (String )session .getAttribute (USER_ID );
@@ -217,6 +223,54 @@ protected void loadTokens(String uri, HttpServletRequest request) throws Credent
217
223
}
218
224
}
219
225
226
+ private void copyResponseContent (FakeHttpServletResponse source , HttpServletResponse dest , boolean flush ) throws IOException
227
+ {
228
+ dest .setStatus (source .getStatus ());
229
+ dest .setCharacterEncoding (source .getCharacterEncoding ());
230
+ // Copy headers over
231
+ for (Object hdrname : source .getHeaderNames ())
232
+ {
233
+ dest .setHeader ((String ) hdrname , (String ) source .getHeader ((String ) hdrname ));
234
+ }
235
+ dest .getOutputStream ().write (source .getContentAsByteArray ());
236
+ if (flush )
237
+ {
238
+ dest .flushBuffer ();
239
+ }
240
+ }
241
+
242
+ private void writeError (HttpServletResponse resp , int status , String id , String message , Throwable e )
243
+ {
244
+ resp .setStatus (status );
245
+ resp .setContentType (Format .JSON .mimetype ());
246
+ try
247
+ {
248
+ JSONWriter writer = new JSONWriter (resp .getWriter ());
249
+ writer .startObject ();
250
+ writer .startValue ("error" ).startObject ();
251
+ writer .writeValue ("id" , id );
252
+ writer .writeValue ("message" , message );
253
+ if (e != null )
254
+ {
255
+ writer .startValue ("exception" ).startObject ();
256
+ writer .writeValue ("message" , e .getMessage ());
257
+ StringWriter sw = new StringWriter ();
258
+ PrintWriter pw = new PrintWriter (sw );
259
+ e .printStackTrace (pw );
260
+ writer .writeValue ("stackTrace" , sw .toString ());
261
+ writer .endObject ();
262
+ }
263
+ writer .endObject ();
264
+ writer .endObject ();
265
+ resp .flushBuffer ();
266
+ }
267
+ catch (IOException e1 )
268
+ {
269
+ // Unable to get writer from response
270
+ e1 .printStackTrace ();
271
+ }
272
+ }
273
+
220
274
/* (non-Javadoc)
221
275
* @see org.alfresco.connector.HttpConnector#stampCredentials(org.alfresco.connector.RemoteClient, org.alfresco.connector.ConnectorContext)
222
276
*/
0 commit comments