001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.rest;
018
019import static org.apache.juneau.commons.utils.AssertionUtils.*;
020
021import java.io.*;
022
023import org.apache.http.*;
024import org.apache.juneau.*;
025import org.apache.juneau.commons.collections.FluentMap;
026import org.apache.juneau.cp.*;
027import org.apache.juneau.http.response.*;
028import org.apache.juneau.rest.logger.*;
029
030/**
031 * A session for a single HTTP request.
032 *
033 * <p>
034 * This session object gets created by {@link RestSession} once the Java method to be invoked has been determined.
035 *
036 * <h5 class='section'>Notes:</h5><ul>
037 *    <li class='warn'>This class is not thread safe.
038 * </ul>
039 *
040 */
041public class RestOpSession extends ContextSession {
042   /**
043    * Builder class.
044    */
045   public static class Builder extends ContextSession.Builder {
046
047      protected final RestOpContext ctx;
048      protected final RestSession session;
049
050      /**
051       * Constructor.
052       *
053       * @param ctx The context object of the Java method being invoked.
054       *    <br>Cannot be <jk>null</jk>.
055       * @param session The REST session object creating this object.
056       *    <br>Cannot be <jk>null</jk>.
057       */
058      public Builder(RestOpContext ctx, RestSession session) {
059         super(assertArgNotNull("ctx", ctx));
060         this.ctx = ctx;
061         this.session = assertArgNotNull("session", session);
062      }
063
064      @Override /* Overridden from Session.Builder */
065      public RestOpSession build() {
066         return new RestOpSession(this);
067      }
068
069      /**
070       * Enables or disabled debug mode on this call.
071       *
072       * @param value The new value for this setting.
073       * @return This object.
074       * @throws IOException Occurs if request content could not be cached into memory.
075       */
076      public Builder debug(boolean value) throws IOException {
077         session.debug(value);
078         return this;
079      }
080
081      /**
082       * Sets the logger to use when logging this call.
083       *
084       * @param value The new value for this setting.
085       *    <br>Can be <jk>null</jk> (will use the default logger from the context if available).
086       * @return This object.
087       */
088      public Builder logger(CallLogger value) {
089         session.logger(value);
090         return this;
091      }
092   }
093
094   /**
095    * Static creator.
096    *
097    * @param ctx The context object of the Java method being invoked.
098    *    <br>Cannot be <jk>null</jk>.
099    * @param session The REST session object creating this object.
100    *    <br>Cannot be <jk>null</jk>.
101    * @return A new builder.
102    */
103   public static Builder create(RestOpContext ctx, RestSession session) {
104      return new Builder(assertArgNotNull("ctx", ctx), assertArgNotNull("session", session));
105   }
106
107   private final RestOpContext ctx;
108   private final RestRequest req;
109   private final RestResponse res;
110   private final RestSession session;
111
112   /**
113    * Constructor.
114    *
115    * @param builder The builder for this object.
116    */
117   protected RestOpSession(Builder builder) {
118      super(builder);
119      ctx = builder.ctx;
120      session = builder.session;
121      try {
122         req = session.getBeanStore().add(RestRequest.class, ctx.createRequest(session));
123         res = session.getBeanStore().add(RestResponse.class, ctx.createResponse(session, req));
124      } catch (RuntimeException e) {
125         throw e;
126      } catch (Exception e) {
127         throw new InternalServerError(e);
128      }
129   }
130
131   /**
132    * Called at the end of a call to finish any remaining tasks such as flushing buffers and logging the response.
133    *
134    * @return This object.
135    */
136   public RestOpSession finish() {
137      try {
138         res.flushBuffer();
139         req.close();
140      } catch (Exception e) {
141         session.exception(e);
142      }
143      return this;
144   }
145
146   /**
147    * Returns the bean store for this session.
148    *
149    * @return The bean store for this session.
150    */
151   public BeanStore getBeanStore() { return session.getBeanStore(); }
152
153   @Override /* Overridden from ContextSession */
154   public RestOpContext getContext() { return ctx; }
155
156   /**
157    * Returns the REST request object for this session.
158    *
159    * @return The REST request object for this session.
160    */
161   public RestRequest getRequest() { return req; }
162
163   /**
164    * Returns the REST response object for this session.
165    *
166    * @return The REST response object for this session.
167    */
168   public RestResponse getResponse() { return res; }
169
170   /**
171    * Returns the context of the parent class of this Java method.
172    *
173    * @return The context of the parent class of this Java method.
174    */
175   public RestContext getRestContext() { return session.getContext(); }
176
177   /**
178    * Returns the session of the parent class of this Java method.
179    *
180    * @return The session of the parent class of this Java method.
181    */
182   public RestSession getRestSession() { return session; }
183
184   /**
185    * Runs this session.
186    *
187    * <p>
188    * Does the following:
189    * <ol>
190    *    <li>Runs the guards on the method.
191    *    <li>Finds the parameter values to pass to the Java method.
192    *    <li>Invokes the Java method.
193    *    <li>Sets the output and status on the response.
194    *    <li>Calls the converters on the Java method.
195    * </ol>
196    *
197    * @throws Throwable Any throwable can be thrown.
198    */
199   public void run() throws Throwable {
200      for (var guard : ctx.getGuards()) {
201         if (! guard.guard(req, res))
202            return;
203      }
204
205      ctx.getMethodInvoker().invoke(this);
206
207      if (res.hasContent())
208         for (var converter : ctx.getConverters())
209            res.setContent(converter.convert(req, res.getContent().orElse(null)));
210   }
211
212   /**
213    * Sets the status of the response.
214    *
215    * @param value The new status.
216    *    <br>Can be <jk>null</jk> (ignored).
217    * @return This object.
218    */
219   public RestOpSession status(StatusLine value) {
220      session.status(value);
221      return this;
222   }
223
224   @Override /* Overridden from ContextSession */
225   protected FluentMap<String,Object> properties() {
226      return super.properties()
227         .a("ctx", ctx)
228         .a("session", session);
229   }
230}