View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.chemistry.opencmis.client.bindings.spi.http;
20  
21  import java.math.BigInteger;
22  import java.util.Map;
23  
24  import org.apache.chemistry.opencmis.client.bindings.spi.BindingSession;
25  import org.apache.chemistry.opencmis.commons.exceptions.CmisBaseException;
26  import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
27  import org.apache.chemistry.opencmis.commons.impl.ClassLoaderUtil;
28  import org.apache.chemistry.opencmis.commons.impl.UrlBuilder;
29  import org.slf4j.Logger;
30  import org.slf4j.LoggerFactory;
31  
32  /**
33   * A HTTP Invoker that delays HTTP requests and thereby throttles the client.
34   * 
35   * This HTTP Invoker is only a wrapper that delegates the work to another HTTP
36   * Invoker, which is defined with the session parameter
37   * {@link DELEGTAE_HTTP_INVOKER_CLASS}.
38   * 
39   * The session parameter {@link DELAY_TIME} defines the delay in milliseconds.
40   */
41  public class DelayingHttpInvoker implements HttpInvoker {
42  
43      private static final Logger LOG = LoggerFactory.getLogger(DelayingHttpInvoker.class);
44  
45      /**
46       * Session parameter: class name of the HTTP Invoker doing the the real
47       * work. (Optional. Default is the {@link DefaultHttpInvoker}.)
48       */
49      public static final String DELEGTAE_HTTP_INVOKER_CLASS = "org.apache.chemistry.opencmis.binding.httpinvoker.delay.delegate.classname";
50      /**
51       * Session parameter: Delay time in milliseconds. (Required.)
52       */
53      public static final String DELAY_TIME = "org.apache.chemistry.opencmis.binding.httpinvoker.delay.delaytime";
54  
55      protected static final String DELEGTAE_HTTP_INVOKER = "org.apache.chemistry.opencmis.client.bindings.spi.http.limiter.httpInvoker";
56      protected static final String LAST_EXECUTION = "org.apache.chemistry.opencmis.client.bindings.spi.http.limiter.lastExecution";
57  
58      public DelayingHttpInvoker() {
59      }
60  
61      @Override
62      public Response invokeGET(UrlBuilder url, BindingSession session) {
63          delay(session);
64          return getHttpInvoker(session).invokeGET(url, session);
65      }
66  
67      @Override
68      public Response invokeGET(UrlBuilder url, BindingSession session, BigInteger offset, BigInteger length) {
69          delay(session);
70          return getHttpInvoker(session).invokeGET(url, session, offset, length);
71      }
72  
73      @Override
74      public Response invokePOST(UrlBuilder url, String contentType, Output writer, BindingSession session) {
75          delay(session);
76          return getHttpInvoker(session).invokePOST(url, contentType, writer, session);
77      }
78  
79      @Override
80      public Response invokePUT(UrlBuilder url, String contentType, Map<String, String> headers, Output writer,
81              BindingSession session) {
82          delay(session);
83          return getHttpInvoker(session).invokePUT(url, contentType, headers, writer, session);
84      }
85  
86      @Override
87      public Response invokeDELETE(UrlBuilder url, BindingSession session) {
88          delay(session);
89          return getHttpInvoker(session).invokeDELETE(url, session);
90      }
91  
92      protected void delay(BindingSession session) {
93          session.writeLock();
94          try {
95              int delayTime = session.get(DELAY_TIME, -1);
96              if (delayTime < 0) {
97                  if (LOG.isInfoEnabled()) {
98                      LOG.info("No delay time configured.");
99                  }
100                 return;
101             }
102 
103             Object lastExcution = session.get(LAST_EXECUTION);
104             if (lastExcution instanceof Long) {
105                 long lastExcutionLong = (Long) lastExcution;
106                 long now = System.currentTimeMillis();
107                 if (now - lastExcutionLong < delayTime) {
108                     try {
109                         Thread.sleep(delayTime - (now - lastExcutionLong));
110                     } catch (InterruptedException e) {
111                         if (LOG.isDebugEnabled()) {
112                             LOG.debug("Interrupted sleep", e);
113                         }
114                     }
115                 }
116             }
117 
118             session.put(LAST_EXECUTION, System.currentTimeMillis());
119 
120         } finally {
121             session.writeUnlock();
122         }
123     }
124 
125     protected HttpInvoker getHttpInvoker(BindingSession session) {
126         HttpInvoker invoker = (HttpInvoker) session.get(DELEGTAE_HTTP_INVOKER);
127 
128         if (invoker != null) {
129             return invoker;
130         }
131 
132         session.writeLock();
133         try {
134             // try again
135             invoker = (HttpInvoker) session.get(DELEGTAE_HTTP_INVOKER);
136             if (invoker != null) {
137                 return invoker;
138             }
139 
140             // ok, we have to create it...
141             try {
142                 String invokerName = (String) session.get(DELEGTAE_HTTP_INVOKER_CLASS);
143                 if (invokerName == null) {
144                     invoker = new DefaultHttpInvoker();
145                 } else {
146                     invoker = (HttpInvoker) ClassLoaderUtil.loadClass(invokerName).newInstance();
147                 }
148             } catch (CmisBaseException e) {
149                 throw e;
150             } catch (Exception e) {
151                 throw new CmisRuntimeException("Delegate HTTP invoker cannot be initialized: " + e.getMessage(), e);
152             }
153 
154             // we have an Invoker object -> put it into the session
155             session.put(DELEGTAE_HTTP_INVOKER, invoker, true);
156         } finally {
157             session.writeUnlock();
158         }
159 
160         return invoker;
161     }
162 }