View Javadoc
1   /*
2    * #%L
3    * wcm.io
4    * %%
5    * Copyright (C) 2015 wcm.io
6    * %%
7    * Licensed under the Apache License, Version 2.0 (the "License");
8    * you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   *
11   *      http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   * #L%
19   */
20  package io.wcm.caravan.io.http.impl.servletclient;
21  
22  import java.io.IOException;
23  import java.util.Map;
24  import java.util.Set;
25  import java.util.concurrent.ConcurrentMap;
26  import java.util.concurrent.ConcurrentSkipListMap;
27  
28  import javax.servlet.Servlet;
29  import javax.servlet.ServletException;
30  
31  import org.apache.felix.scr.annotations.Component;
32  import org.apache.felix.scr.annotations.Reference;
33  import org.apache.felix.scr.annotations.ReferenceCardinality;
34  import org.apache.felix.scr.annotations.ReferencePolicy;
35  import org.apache.felix.scr.annotations.Service;
36  import org.slf4j.Logger;
37  import org.slf4j.LoggerFactory;
38  
39  import com.google.common.collect.Sets;
40  
41  import io.wcm.caravan.io.http.CaravanHttpClient;
42  import io.wcm.caravan.io.http.IllegalResponseRuntimeException;
43  import io.wcm.caravan.io.http.RequestFailedRuntimeException;
44  import io.wcm.caravan.io.http.impl.CaravanHttpServiceConfigValidator;
45  import io.wcm.caravan.io.http.request.CaravanHttpRequest;
46  import io.wcm.caravan.io.http.response.CaravanHttpResponse;
47  import rx.Observable;
48  
49  /**
50   * Client which executes {@link Servlet}s registered in the same server directly without HTTP. Ignores fallbacks.
51   */
52  @Component
53  @Service(ServletHttpClient.class)
54  public class ServletHttpClient implements CaravanHttpClient {
55  
56    private static final Logger LOG = LoggerFactory.getLogger(ServletHttpClient.class);
57  
58    @Reference(referenceInterface = Servlet.class, cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, policy = ReferencePolicy.DYNAMIC)
59    private final ConcurrentMap<String, Servlet> servlets = new ConcurrentSkipListMap<>();
60  
61    private final Set<String> failedServices = Sets.newHashSet();
62    protected void bindServlet(Servlet servlet, Map<String, Object> config) {
63      String serviceId = (String)config.get("alias");
64      if (serviceId != null) {
65        servlets.put(serviceId, servlet);
66      }
67    }
68  
69    protected void unbindServlet(Servlet servle, Map<String, Object> config) {
70      String serviceId = (String)config.get("alias");
71      if (serviceId != null) {
72        servlets.remove(serviceId);
73      }
74    }
75  
76    @Override
77    public Observable<CaravanHttpResponse> execute(CaravanHttpRequest request) {
78      return Observable.just(request.getServiceId())
79          .map(serviceId -> getServlet(serviceId))
80          .map(servlet -> executeServlet(servlet, request));
81    }
82  
83    @Override
84    public Observable<CaravanHttpResponse> execute(CaravanHttpRequest request, Observable<CaravanHttpResponse> fallback) {
85      return execute(request);
86    }
87  
88    @Override
89    public boolean hasValidConfiguration(String serviceId) {
90      return servlets.containsKey(serviceId) && !failedServices.contains(serviceId);
91    }
92  
93    private Servlet getServlet(String serviceId) {
94  
95      Servlet servlet = servlets.get(serviceId);
96      if (servlet == null) {
97        throw new IllegalStateException("No local servlet registered for " + serviceId);
98      }
99      return servlet;
100 
101   }
102 
103   private CaravanHttpResponse executeServlet(Servlet servlet, CaravanHttpRequest request) {
104 
105     LOG.debug("Execute: {},\n{}", request.toString(), request.getCorrelationId());
106     HttpServletRequestMapper requestMapper = new HttpServletRequestMapper(request);
107     HttpServletResponseMapper responseMapper = new HttpServletResponseMapper();
108     try {
109       servlet.service(requestMapper, responseMapper);
110       CaravanHttpResponse response = responseMapper.getResponse();
111 
112       int status = response.status();
113       boolean throwExceptionForStatus500 = CaravanHttpServiceConfigValidator.throwExceptionForStatus500(request.getServiceId());
114       if (status >= 500 && throwExceptionForStatus500) {
115         String requestUrl = request.getUrl();
116         String responseBody = response.body().asString();
117         throw new IllegalResponseRuntimeException(request, requestUrl, status, responseBody,
118           "Executing '" + requestUrl + "' failed: " + responseBody);
119       }
120       return response;
121     }
122     catch (NotSupportedByRequestMapperException ex) {
123       failedServices.add(request.getServiceId());
124       throw ex;
125     }
126     catch (ServletException | IOException ex) {
127       throw new RequestFailedRuntimeException(request, ex.getMessage(), ex);
128     }
129 
130   }
131 
132 }