11/* 
22 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 
33 * 
4-  * Copyright (c) 2010-2015  Oracle and/or its affiliates. All rights reserved. 
4+  * Copyright (c) 2010-2016  Oracle and/or its affiliates. All rights reserved. 
55 * 
66 * The contents of this file are subject to the terms of either the GNU 
77 * General Public License Version 2 only ("GPL") or the Common Development 
3737 * only if the new code is made subject to such option by the copyright 
3838 * holder. 
3939 */ 
40+ 
4041package  org .glassfish .jersey .simple ;
4142
4243import  java .io .IOException ;
4748import  java .security .Principal ;
4849import  java .util .List ;
4950import  java .util .Map ;
51+ import  java .util .concurrent .ScheduledExecutorService ;
52+ import  java .util .concurrent .ScheduledFuture ;
53+ import  java .util .concurrent .ScheduledThreadPoolExecutor ;
5054import  java .util .concurrent .TimeUnit ;
55+ import  java .util .concurrent .atomic .AtomicReference ;
5156import  java .util .logging .Level ;
5257import  java .util .logging .Logger ;
5358
7075import  org .glassfish .jersey .server .internal .ContainerUtils ;
7176import  org .glassfish .jersey .server .spi .Container ;
7277import  org .glassfish .jersey .server .spi .ContainerResponseWriter ;
78+ import  org .glassfish .jersey .server .spi .ContainerResponseWriter .TimeoutHandler ;
7379import  org .glassfish .jersey .server .spi .RequestScopedInitializer ;
7480
7581import  org .glassfish .hk2 .api .ServiceLocator ;
7682import  org .glassfish .hk2 .api .TypeLiteral ;
7783import  org .glassfish .hk2 .utilities .binding .AbstractBinder ;
7884
85+ import  org .simpleframework .common .thread .DaemonFactory ;
7986import  org .simpleframework .http .Address ;
8087import  org .simpleframework .http .Request ;
8188import  org .simpleframework .http .Response ;
8289import  org .simpleframework .http .Status ;
8390
8491/** 
85-  * Jersey {@code Container} implementation based on Simple framework {@link org.simpleframework.http.core.Container}. 
92+  * Jersey {@code Container} implementation based on Simple framework 
93+  * {@link org.simpleframework.http.core.Container}. 
8694 * 
8795 * @author Arul Dhesiaseelan ([email protected] ) 8896 * @author Marek Potociar (marek.potociar at oracle.com) 
@@ -120,48 +128,57 @@ public SimpleResponseReferencingFactory(final Provider<Ref<Response>> referenceF
120128    }
121129
122130    /** 
123-      * An internal binder to enable Simple HTTP container specific types injection. 
124-      * This binder allows  to inject underlying Grizzly HTTP request and response instances. 
131+      * An internal binder to enable Simple HTTP container specific types injection. This binder allows  
132+      * to inject underlying Grizzly HTTP request and response instances. 
125133     */ 
126134    private  static  class  SimpleBinder  extends  AbstractBinder  {
127135
128136        @ Override 
129137        protected  void  configure () {
130-             bindFactory (SimpleRequestReferencingFactory .class ).to (Request .class )
131-                     . proxy ( true ) .proxyForSameScope (false ).in (RequestScoped .class );
132-             bindFactory (ReferencingFactory .<Request >referenceFactory ()). to ( new   TypeLiteral < Ref < Request >>() { 
133-             }) 
134-                     .in (RequestScoped .class );
135- 
136-             bindFactory (SimpleResponseReferencingFactory .class ).to (Response .class )
137-                     . proxy ( true ) .proxyForSameScope (false ).in (RequestScoped .class );
138-             bindFactory (ReferencingFactory .<Response >referenceFactory ()). to ( new   TypeLiteral < Ref < Response >>() { 
139-             }) 
140-                     .in (RequestScoped .class );
138+             bindFactory (SimpleRequestReferencingFactory .class ).to (Request .class ). proxy ( true ) 
139+                                                                .proxyForSameScope (false ).in (RequestScoped .class );
140+             bindFactory (ReferencingFactory .<Request >referenceFactory ())
141+                     . to ( new   TypeLiteral < Ref < Request >>() { 
142+                     }) .in (RequestScoped .class );
143+ 
144+             bindFactory (SimpleResponseReferencingFactory .class ).to (Response .class ). proxy ( true ) 
145+                                                                 .proxyForSameScope (false ).in (RequestScoped .class );
146+             bindFactory (ReferencingFactory .<Response >referenceFactory ())
147+                     . to ( new   TypeLiteral < Ref < Response >>() { 
148+                     }) .in (RequestScoped .class );
141149        }
142150    }
143151
152+     private  volatile  ScheduledExecutorService  scheduler ;
144153    private  volatile  ApplicationHandler  appHandler ;
145154
146-     private  static  final  class  Writer  implements  ContainerResponseWriter  {
155+     private  static  final  class  ResponseWriter  implements  ContainerResponseWriter  {
147156
157+         private  final  AtomicReference <TimeoutTimer > reference ;
158+         private  final  ScheduledExecutorService  scheduler ;
148159        private  final  Response  response ;
149160
150-         Writer (final  Response  response ) {
161+         ResponseWriter (final  Response  response , final  ScheduledExecutorService  scheduler ) {
162+             this .reference  = new  AtomicReference <TimeoutTimer >();
151163            this .response  = response ;
164+             this .scheduler  = scheduler ;
152165        }
153166
154167        @ Override 
155-         public  OutputStream  writeResponseStatusAndHeaders (final  long  contentLength ,  final   ContainerResponse   context ) 
156-                 throws  ContainerException  {
168+         public  OutputStream  writeResponseStatusAndHeaders (final  long  contentLength ,
169+                                                            final   ContainerResponse   context )  throws  ContainerException  {
157170            final  javax .ws .rs .core .Response .StatusType  statusInfo  = context .getStatusInfo ();
158171
159172            final  int  code  = statusInfo .getStatusCode ();
160-             final  String  reason  = statusInfo .getReasonPhrase () == null  ? Status .getDescription (code )
173+             final  String  reason  = statusInfo .getReasonPhrase () == null 
174+                     ? Status .getDescription (code )
161175                    : statusInfo .getReasonPhrase ();
162176            response .setCode (code );
163177            response .setDescription (reason );
164-             response .setContentLength (contentLength );
178+ 
179+             if  (contentLength  != -1 ) {
180+                 response .setContentLength (contentLength );
181+             }
165182            for  (final  Map .Entry <String , List <String >> e  : context .getStringHeaders ().entrySet ()) {
166183                for  (final  String  value  : e .getValue ()) {
167184                    response .addValue (e .getKey (), value );
@@ -176,13 +193,41 @@ public OutputStream writeResponseStatusAndHeaders(final long contentLength, fina
176193        }
177194
178195        @ Override 
179-         public  boolean  suspend (final  long  timeOut , final  TimeUnit  timeUnit , final  TimeoutHandler  timeoutHandler ) {
180-             throw  new  UnsupportedOperationException ("Method suspend is not supported by the container." );
196+         public  boolean  suspend (final  long  timeOut , final  TimeUnit  timeUnit ,
197+                                final  TimeoutHandler  timeoutHandler ) {
198+             try  {
199+                 TimeoutTimer  timer  = reference .get ();
200+ 
201+                 if  (timer  == null ) {
202+                     TimeoutDispatcher  task  = new  TimeoutDispatcher (this , timeoutHandler );
203+                     ScheduledFuture <?> future  =
204+                             scheduler .schedule (task , timeOut  == 0  ? Integer .MAX_VALUE  : timeOut ,
205+                                     timeOut  == 0  ? TimeUnit .SECONDS  : timeUnit );
206+                     timer  = new  TimeoutTimer (scheduler , future , task );
207+                     reference .set (timer );
208+                     return  true ;
209+                 }
210+                 return  false ;
211+             } catch  (final  IllegalStateException  ex ) {
212+                 return  false ;
213+             } finally  {
214+                 logger .debugLog ("suspend(...) called" );
215+             }
181216        }
182217
183218        @ Override 
184-         public  void  setSuspendTimeout (final  long  timeOut , final  TimeUnit  timeUnit ) throws  IllegalStateException  {
185-             throw  new  UnsupportedOperationException ("Method suspend is not supported by the container." );
219+         public  void  setSuspendTimeout (final  long  timeOut , final  TimeUnit  timeUnit )
220+                 throws  IllegalStateException  {
221+             try  {
222+                 TimeoutTimer  timer  = reference .get ();
223+ 
224+                 if  (timer  == null ) {
225+                     throw  new  IllegalStateException ("Response has not been suspended" );
226+                 }
227+                 timer .reschedule (timeOut , timeUnit );
228+             } finally  {
229+                 logger .debugLog ("setTimeout(...) called" );
230+             }
186231        }
187232
188233        @ Override 
@@ -196,6 +241,10 @@ public void commit() {
196241            }
197242        }
198243
244+         public  boolean  isSuspended () {
245+             return  reference .get () != null ;
246+         }
247+ 
199248        @ Override 
200249        public  void  failure (final  Throwable  error ) {
201250            try  {
@@ -231,19 +280,64 @@ private void rethrow(final Throwable error) {
231280
232281    }
233282
283+     private  static  final  class  TimeoutTimer  {
284+ 
285+         private  final  AtomicReference <ScheduledFuture <?>> reference ;
286+         private  final  ScheduledExecutorService  service ;
287+         private  final  TimeoutDispatcher  task ;
288+ 
289+         public  TimeoutTimer (ScheduledExecutorService  service , ScheduledFuture <?> future ,
290+                             TimeoutDispatcher  task ) {
291+             this .reference  = new  AtomicReference <ScheduledFuture <?>>();
292+             this .service  = service ;
293+             this .task  = task ;
294+         }
295+ 
296+         public  void  reschedule (long  timeOut , TimeUnit  timeUnit ) {
297+             ScheduledFuture <?> future  = reference .getAndSet (null );
298+ 
299+             if  (future  != null ) {
300+                 if  (future .cancel (false )) {
301+                     future  = service .schedule (task , timeOut  == 0  ? Integer .MAX_VALUE  : timeOut ,
302+                             timeOut  == 0  ? TimeUnit .SECONDS  : timeUnit );
303+                     reference .set (future );
304+                 }
305+             } else  {
306+                 future  = service .schedule (task , timeOut  == 0  ? Integer .MAX_VALUE  : timeOut ,
307+                         timeOut  == 0  ? TimeUnit .SECONDS  : timeUnit );
308+                 reference .set (future );
309+             }
310+         }
311+     }
312+ 
313+     private  static  final  class  TimeoutDispatcher  implements  Runnable  {
314+ 
315+         private  final  ResponseWriter  writer ;
316+         private  final  TimeoutHandler  handler ;
317+ 
318+         public  TimeoutDispatcher (ResponseWriter  writer , TimeoutHandler  handler ) {
319+             this .writer  = writer ;
320+             this .handler  = handler ;
321+         }
322+ 
323+         public  void  run () {
324+             try  {
325+                 handler .onTimeout (writer );
326+             } catch  (Exception  e ) {
327+                 logger .log (Level .INFO , "Failed to call timeout handler" , e );
328+             }
329+         }
330+     }
331+ 
234332    @ Override 
235333    public  void  handle (final  Request  request , final  Response  response ) {
236-         final  Writer  responseWriter  = new  Writer (response );
334+         final  ResponseWriter  responseWriter  = new  ResponseWriter (response ,  scheduler );
237335        final  URI  baseUri  = getBaseUri (request );
238336        final  URI  requestUri  = getRequestUri (request , baseUri );
239337
240338        try  {
241-             final  ContainerRequest  requestContext  = new  ContainerRequest (
242-                     baseUri ,
243-                     requestUri ,
244-                     request .getMethod (),
245-                     getSecurityContext (request ),
246-                     new  MapPropertiesDelegate ());
339+             final  ContainerRequest  requestContext  = new  ContainerRequest (baseUri , requestUri ,
340+                     request .getMethod (), getSecurityContext (request ), new  MapPropertiesDelegate ());
247341            requestContext .setEntityStream (request .getInputStream ());
248342            for  (final  String  headerName  : request .getNames ()) {
249343                requestContext .headers (headerName , request .getValue (headerName ));
@@ -261,7 +355,9 @@ public void initialize(final ServiceLocator locator) {
261355        } catch  (final  Exception  ex ) {
262356            throw  new  RuntimeException (ex );
263357        } finally  {
264-             close (response );
358+             if  (!responseWriter .isSuspended ()) {
359+                 close (response );
360+             }
265361        }
266362    }
267363
@@ -282,7 +378,8 @@ private URI getRequestUri(final Request request, final URI baseUri) {
282378    }
283379
284380    private  String  getServerAddress (final  URI  baseUri ) throws  URISyntaxException  {
285-         return  new  URI (baseUri .getScheme (), null ,  baseUri .getHost (), baseUri .getPort (), null , null , null ).toString ();
381+         return  new  URI (baseUri .getScheme (), null , baseUri .getHost (), baseUri .getPort (), null , null ,
382+                 null ).toString ();
286383    }
287384
288385    private  URI  getBaseUri (final  Request  request ) {
@@ -294,7 +391,8 @@ private URI getBaseUri(final Request request) {
294391                return  new  URI (scheme  + "://"  + hostHeader  + "/" );
295392            } else  {
296393                final  Address  address  = request .getAddress ();
297-                 return  new  URI (address .getScheme (), null , address .getDomain (), address .getPort (), "/" , null , null );
394+                 return  new  URI (address .getScheme (), null , address .getDomain (), address .getPort (), "/" , null ,
395+                         null );
298396            }
299397        } catch  (final  URISyntaxException  ex ) {
300398            throw  new  IllegalArgumentException (ex );
@@ -316,7 +414,7 @@ public boolean isSecure() {
316414
317415            @ Override 
318416            public  Principal  getUserPrincipal () {
319-                 return  request . getSecuritySession (). getLocalPrincipal () ;
417+                 return  null ;
320418            }
321419
322420            @ Override 
@@ -349,6 +447,7 @@ public void reload(final ResourceConfig configuration) {
349447        appHandler .onShutdown (this );
350448
351449        appHandler  = new  ApplicationHandler (configuration .register (new  SimpleBinder ()));
450+         scheduler  = new  ScheduledThreadPoolExecutor (2 , new  DaemonFactory (TimeoutDispatcher .class ));
352451        appHandler .onReload (this );
353452        appHandler .onStartup (this );
354453    }
@@ -360,7 +459,7 @@ public ApplicationHandler getApplicationHandler() {
360459
361460    /** 
362461     * Inform this container that the server has been started. 
363-      * 
462+      * <p/>  
364463     * This method must be implicitly called after the server containing this container is started. 
365464     */ 
366465    void  onServerStart () {
@@ -369,29 +468,34 @@ void onServerStart() {
369468
370469    /** 
371470     * Inform this container that the server is being stopped. 
372-      * 
471+      * <p/>  
373472     * This method must be implicitly called before the server containing this container is stopped. 
374473     */ 
375474    void  onServerStop () {
376475        appHandler .onShutdown (this );
476+         scheduler .shutdown ();
377477    }
378478
379479    /** 
380480     * Create a new Simple framework HTTP container. 
381481     * 
382-      * @param application JAX-RS / Jersey application to be deployed on Simple framework HTTP container. 
482+      * @param application   JAX-RS / Jersey application to be deployed on Simple framework HTTP 
483+      *                      container. 
383484     * @param parentLocator parent HK2 service locator. 
384485     */ 
385486    SimpleContainer (final  Application  application , final  ServiceLocator  parentLocator ) {
386487        this .appHandler  = new  ApplicationHandler (application , new  SimpleBinder (), parentLocator );
488+         this .scheduler  = new  ScheduledThreadPoolExecutor (2 , new  DaemonFactory (TimeoutDispatcher .class ));
387489    }
388490
389491    /** 
390492     * Create a new Simple framework HTTP container. 
391493     * 
392-      * @param application JAX-RS / Jersey application to be deployed on Simple framework HTTP container. 
494+      * @param application JAX-RS / Jersey application to be deployed on Simple framework HTTP 
495+      *                    container. 
393496     */ 
394497    SimpleContainer (final  Application  application ) {
395498        this .appHandler  = new  ApplicationHandler (application , new  SimpleBinder ());
499+         this .scheduler  = new  ScheduledThreadPoolExecutor (2 , new  DaemonFactory (TimeoutDispatcher .class ));
396500    }
397501}
0 commit comments