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.client.classic;
018
019import java.util.function.*;
020
021import org.apache.juneau.marshall.stream.*;
022
023/**
024 * Wrapper that lets a caller pass a {@code Consumer<RecordWriter>} or
025 * {@code Consumer<TokenWriter>} as the body of a {@code @RemoteOp} request.
026 *
027 * <p>
028 * The framework resolves the matching serializer for the request's {@code Content-Type},
029 * opens a writer of the appropriate kind, and hands it to the caller's consumer.  When the
030 * consumer returns, the writer is closed and the request body is dispatched.
031 *
032 * <h5 class='section'>Usage:</h5>
033 * <p class='bjava'>
034 *    <ja>@Remote</ja>
035 *    <jk>public interface</jk> MyApi {
036 *
037 *       <ja>@RemotePost</ja>(<js>"/bulk-upload"</js>)
038 *       <jk>void</jk> upload(<ja>@Content</ja> RecordStreamBody <jv>body</jv>);
039 *    }
040 *
041 *    <jc>// Caller</jc>
042 *    <jv>api</jv>.upload(RecordStreamBody.<jsm>records</jsm>(<jv>w</jv> -&gt; {
043 *       <jk>for</jk> (Bean <jv>b</jv> : <jv>source</jv>())
044 *          <jv>w</jv>.write(<jv>b</jv>);
045 *    }));
046 * </p>
047 */
048@SuppressWarnings({
049   "java:S1452" // getConsumer returns Consumer<?> and getWriterKind returns Class<?> because the record type is unknown at this level
050})
051public final class RecordStreamBody {
052
053   /**
054    * Creates a body that streams via a {@link RecordWriter} (whole-value record cursor).
055    *
056    * @param consumer The callback that emits records.  Must not be <jk>null</jk>.
057    * @return A new body wrapper.
058    */
059   public static RecordStreamBody records(Consumer<RecordWriter> consumer) {
060      return new RecordStreamBody(consumer, RecordWriter.class);
061   }
062
063   /**
064    * Creates a body that streams via a {@link TokenWriter} (fine-grained structural cursor).
065    *
066    * @param consumer The callback that emits structural events.  Must not be <jk>null</jk>.
067    * @return A new body wrapper.
068    */
069   public static RecordStreamBody token(Consumer<TokenWriter> consumer) {
070      return new RecordStreamBody(consumer, TokenWriter.class);
071   }
072
073   private final Consumer<?> consumer;
074   private final Class<?> writerKind;
075
076   private RecordStreamBody(Consumer<?> consumer, Class<?> writerKind) {
077      this.consumer = consumer;
078      this.writerKind = writerKind;
079   }
080
081   /** @return The caller's consumer (typed as Consumer&lt;RecordWriter&gt; or Consumer&lt;TokenWriter&gt;). */
082   public Consumer<?> getConsumer() { return consumer; }
083
084   /** @return Either {@code RecordWriter.class} or {@code TokenWriter.class}. */
085   public Class<?> getWriterKind() { return writerKind; }
086}