001package ca.uhn.fhir.rest.method;
002
003/*
004 * #%L
005 * HAPI FHIR - Core Library
006 * %%
007 * Copyright (C) 2014 - 2017 University Health Network
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 * 
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 * 
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import static org.apache.commons.lang3.StringUtils.isBlank;
024
025import java.lang.reflect.Method;
026import java.util.HashSet;
027import java.util.Set;
028
029import org.hl7.fhir.instance.model.api.IBase;
030import org.hl7.fhir.instance.model.api.IBaseResource;
031
032import ca.uhn.fhir.context.FhirContext;
033import ca.uhn.fhir.model.api.Bundle;
034import ca.uhn.fhir.model.api.Include;
035import ca.uhn.fhir.model.valueset.BundleTypeEnum;
036import ca.uhn.fhir.rest.api.RequestTypeEnum;
037import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
038import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
039import ca.uhn.fhir.rest.server.Constants;
040import ca.uhn.fhir.rest.server.EncodingEnum;
041import ca.uhn.fhir.rest.server.IBundleProvider;
042import ca.uhn.fhir.rest.server.IPagingProvider;
043import ca.uhn.fhir.rest.server.IRestfulServer;
044import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
045import ca.uhn.fhir.rest.server.RestfulServerUtils;
046import ca.uhn.fhir.rest.server.RestfulServerUtils.ResponseEncoding;
047import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
048import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
049import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
050import ca.uhn.fhir.util.CoverageIgnore;
051
052public class PageMethodBinding extends BaseResourceReturningMethodBinding {
053
054        public PageMethodBinding(FhirContext theContext, Method theMethod) {
055                super(null, theMethod, theContext, null);
056        }
057
058        private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PageMethodBinding.class);
059
060        public IBaseResource provider() {
061                return null;
062        }
063
064        @Override
065        protected BundleTypeEnum getResponseBundleType() {
066                return null;
067        }
068
069        @Override
070        public ReturnTypeEnum getReturnType() {
071                return ReturnTypeEnum.BUNDLE;
072        }
073
074        @Override
075        public Object invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
076                return handlePagingRequest(theServer, theRequest, theRequest.getParameters().get(Constants.PARAM_PAGINGACTION)[0]);
077        }
078
079        @Override
080        public ResourceOrDstu1Bundle doInvokeServer(IRestfulServer<?> theServer, RequestDetails theRequest) {
081                IBase bundle = handlePagingRequest(theServer, theRequest, theRequest.getParameters().get(Constants.PARAM_PAGINGACTION)[0]);
082                if (bundle instanceof Bundle) {
083                        return new ResourceOrDstu1Bundle((Bundle) bundle);
084                }
085                return new ResourceOrDstu1Bundle((IBaseResource) bundle);
086        }
087        
088        private IBase handlePagingRequest(IRestfulServer<?> theServer, RequestDetails theRequest, String thePagingAction) {
089                IPagingProvider pagingProvider = theServer.getPagingProvider();
090                if (pagingProvider == null) {
091                        throw new InvalidRequestException("This server does not support paging");
092                }
093                IBundleProvider resultList = pagingProvider.retrieveResultList(thePagingAction);
094                if (resultList == null) {
095                        ourLog.info("Client requested unknown paging ID[{}]", thePagingAction);
096                        String msg = getContext().getLocalizer().getMessage(PageMethodBinding.class, "unknownSearchId", thePagingAction);
097                        throw new ResourceGoneException(msg);
098                }
099
100                Integer count = RestfulServerUtils.extractCountParameter(theRequest);
101                if (count == null) {
102                        count = pagingProvider.getDefaultPageSize();
103                } else if (count > pagingProvider.getMaximumPageSize()) {
104                        count = pagingProvider.getMaximumPageSize();
105                }
106
107                Integer offsetI = RestfulServerUtils.tryToExtractNamedParameter(theRequest, Constants.PARAM_PAGINGOFFSET);
108                if (offsetI == null || offsetI < 0) {
109                        offsetI = 0;
110                }
111
112                Integer totalNum = resultList.size();
113                int start = offsetI;
114                if (totalNum != null) {
115                        start = Math.min(start, totalNum - 1);
116                }
117                
118                ResponseEncoding responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequest, theServer.getDefaultResponseEncoding());
119                boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theServer, theRequest);
120
121                IVersionSpecificBundleFactory bundleFactory = theServer.getFhirContext().newBundleFactory();
122
123                Set<Include> includes = new HashSet<Include>();
124                String[] reqIncludes = theRequest.getParameters().get(Constants.PARAM_INCLUDE);
125                if (reqIncludes != null) {
126                        for (String nextInclude : reqIncludes) {
127                                includes.add(new Include(nextInclude));
128                        }
129                }
130
131                String linkSelfBase = theRequest.getFhirServerBase(); // myServerAddressStrategy.determineServerBase(getServletContext(),
132                                                                                                                                                                // theRequest.getServletRequest());
133                String completeUrl = theRequest.getCompleteUrl();
134                String linkSelf = linkSelfBase + completeUrl.substring(theRequest.getCompleteUrl().indexOf('?'));
135
136                BundleTypeEnum bundleType = null;
137                String[] bundleTypeValues = theRequest.getParameters().get(Constants.PARAM_BUNDLETYPE);
138                if (bundleTypeValues != null) {
139                        bundleType = BundleTypeEnum.VALUESET_BINDER.fromCodeString(bundleTypeValues[0]);
140                }
141
142                EncodingEnum encodingEnum = null;
143                if (responseEncoding != null) {
144                        encodingEnum = responseEncoding.getEncoding();
145                }
146                bundleFactory.initializeBundleFromBundleProvider(theServer, resultList, encodingEnum, theRequest.getFhirServerBase(), linkSelf, prettyPrint, start, count, thePagingAction, bundleType, includes);
147
148                Bundle bundle = bundleFactory.getDstu1Bundle();
149                if (bundle != null) {
150                        return bundle;
151                }
152                return bundleFactory.getResourceBundle();
153                // if (bundle != null) {
154                // for (int i = getInterceptors().size() - 1; i >= 0; i--) {
155                // IServerInterceptor next = getInterceptors().get(i);
156                // boolean continueProcessing = next.outgoingResponse(theRequest, bundle, theRequest.getServletRequest(),
157                // theRequest.getServletResponse());
158                // if (!continueProcessing) {
159                // ourLog.debug("Interceptor {} returned false, not continuing processing");
160                // return;
161                // }
162                // }
163                // theRequest.getResponse().streamResponseAsBundle(bundle, summaryMode, respondGzip, requestIsBrowser);
164                // } else {
165                // IBaseResource resBundle = bundleFactory.getResourceBundle();
166                // for (int i = getInterceptors().size() - 1; i >= 0; i--) {
167                // IServerInterceptor next = getInterceptors().get(i);
168                // boolean continueProcessing = next.outgoingResponse(theRequest, resBundle, theRequest.getServletRequest(),
169                // theRequest.getServletResponse());
170                // if (!continueProcessing) {
171                // ourLog.debug("Interceptor {} returned false, not continuing processing");
172                // return;
173                // }
174                // }
175                // theRequest.getResponse().streamResponseAsResource(resBundle, prettyPrint, summaryMode,
176                // Constants.STATUS_HTTP_200_OK, theRequest.isRespondGzip(), false);
177                // }
178        }
179
180        @Override
181        public RestOperationTypeEnum getRestOperationType() {
182                return RestOperationTypeEnum.GET_PAGE;
183        }
184
185        @Override
186        public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) {
187                String[] pageId = theRequest.getParameters().get(Constants.PARAM_PAGINGACTION);
188                if (pageId == null || pageId.length == 0 || isBlank(pageId[0])) {
189                        return false;
190                }
191                if (theRequest.getRequestType() != RequestTypeEnum.GET) {
192                        return false;
193                }
194                return true;
195        }
196
197        @CoverageIgnore
198        @Override
199        public BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
200                throw new UnsupportedOperationException();
201        }
202
203}