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<Item> { 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}