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.commons.function;
018
019/**
020 * A lifecycle-aware consumer for use as a parser target, receiving deserialized beans one at a time.
021 *
022 * <p>
023 * Extends {@link ThrowingConsumer} so that {@link #acceptThrows(Object)} supports checked exceptions
024 * directly — no wrapping in {@link RuntimeException} required for operations such as JDBC inserts.
025 *
026 * <p>
027 * The parser drives the full lifecycle:
028 * <ol>
029 *    <li>Calls {@link #begin()} before the first element
030 *    <li>Calls {@link #acceptThrows(Object)} for each parsed element
031 *    <li>If {@link #acceptThrows(Object)} throws, calls {@link #onError(Exception)}:
032 *        <ul>
033 *          <li>If {@code onError()} absorbs the exception (doesn't rethrow), parsing continues to the
034 *              next element (skip-and-continue / fault-tolerant ingestion)
035 *          <li>If {@code onError()} rethrows, parsing stops and the exception propagates
036 *        </ul>
037 *    <li>Always calls {@link #complete()} after all elements (like {@code finally}) — whether parsing
038 *        succeeded or {@code onError()} rethrew. Use this for resource cleanup (close connections, etc.)
039 * </ol>
040 *
041 * <p>
042 * For round-trip support (same property used for both serialization and parsing), use
043 * {@link BeanChannel} instead, which extends both {@link BeanConsumer} and {@link BeanSupplier}.
044 *
045 * <p>
046 * If a {@link BeanConsumer} is encountered during serialization (not parsing), the serializer will
047 * throw an {@link IllegalArgumentException} with a message recommending {@link BeanSupplier} or
048 * {@link BeanChannel}.
049 *
050 * <h5 class='section'>Example (DB-backed consumer with batch commits):</h5>
051 * <p class='bjava'>
052 *    <ja>@Marshalled</ja>(factory=ItemConsumerFactory.<jk>class</jk>)
053 *    <jk>public class</jk> ItemConsumer <jk>implements</jk> BeanConsumer&lt;Item&gt; {
054 *       <jk>private</jk> Connection <jv>conn</jv>;
055 *       <jk>private</jk> PreparedStatement <jv>stmt</jv>;
056 *       <jk>private int</jk> <jv>count</jv>;
057 *
058 *       <ja>@Override</ja> <jk>public void</jk> begin() <jk>throws</jk> Exception {
059 *          <jv>conn</jv> = <jv>ds</jv>.getConnection();
060 *          <jv>conn</jv>.setAutoCommit(<jk>false</jk>);
061 *          <jv>stmt</jv> = <jv>conn</jv>.prepareStatement(<js>"INSERT INTO items (name) VALUES (?)"</js>);
062 *       }
063 *
064 *       <ja>@Override</ja> <jk>public void</jk> acceptThrows(Item <jv>item</jv>) <jk>throws</jk> Exception {
065 *          <jv>stmt</jv>.setString(1, <jv>item</jv>.getName());
066 *          <jv>stmt</jv>.executeUpdate();
067 *          <jk>if</jk> (++<jv>count</jv> % 500 == 0) <jv>conn</jv>.commit(); <jc>// batch commit</jc>
068 *       }
069 *
070 *       <ja>@Override</ja> <jk>public void</jk> onError(Exception <jv>e</jv>) <jk>throws</jk> Exception {
071 *          <jv>conn</jv>.rollback(); <jk>throw</jk> <jv>e</jv>;
072 *       }
073 *
074 *       <ja>@Override</ja> <jk>public void</jk> complete() <jk>throws</jk> Exception {
075 *          <jv>conn</jv>.commit(); <jv>stmt</jv>.close(); <jv>conn</jv>.close();
076 *       }
077 *    }
078 * </p>
079 *
080 * <h5 class='section'>See Also:</h5><ul>
081 *    <li class='jc'>{@link BeanSupplier} - Serialize-only lifecycle interface
082 *    <li class='jc'>{@link BeanChannel} - Round-trip lifecycle interface (extends both)
083 *    <li class='jc'>{@link BeanFactory} - Universal factory for DI framework integration
084 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/Marshalling">Marshalling</a>
085 * </ul>
086 *
087 * @param <T> The type of bean received by this consumer.
088 */
089@SuppressWarnings({
090   "java:S112" // throws Exception intentional - lifecycle methods may throw any checked exception
091})
092public interface BeanConsumer<T> extends ThrowingConsumer<T> {
093
094   /**
095    * Called before the first element is accepted.
096    *
097    * <p>
098    * Use this to open database connections, prepare statements, or perform other setup.
099    *
100    * @throws Exception If setup fails.
101    */
102   default void begin() throws Exception {}
103
104   /**
105    * Always called after all elements have been processed (like {@code finally}).
106    *
107    * <p>
108    * Use this to close database connections, statements, or release other resources.
109    * This method is called whether parsing succeeded or {@link #onError(Exception)} rethrew.
110    *
111    * @throws Exception If cleanup fails.
112    */
113   default void complete() throws Exception {}
114
115   /**
116    * Called when {@link #acceptThrows(Object)} throws an exception.
117    *
118    * <p>
119    * The default implementation rethrows the exception, stopping parsing immediately.
120    * Override to absorb the exception for fault-tolerant ingestion (e.g., log and skip bad records).
121    * Note that {@link #complete()} is always called afterward regardless.
122    *
123    * @param e The exception thrown by {@link #acceptThrows(Object)}.
124    * @throws Exception If the error cannot be recovered from. Rethrow {@code e} to stop parsing.
125    */
126   default void onError(Exception e) throws Exception {
127      throw e;
128   }
129}