001/** 002 * Copyright (C) 2006-2025 Talend Inc. - www.talend.com 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.talend.sdk.component.runtime.input; 017 018import static java.util.Collections.emptyMap; 019import static java.util.Optional.ofNullable; 020import static java.util.stream.Collectors.toList; 021 022import java.io.ByteArrayInputStream; 023import java.io.IOException; 024import java.io.InvalidObjectException; 025import java.io.ObjectInputStream; 026import java.io.ObjectStreamException; 027import java.io.Serializable; 028import java.lang.reflect.Method; 029import java.util.Collection; 030import java.util.List; 031import java.util.Map; 032import java.util.function.Function; 033 034import org.talend.sdk.component.api.input.Assessor; 035import org.talend.sdk.component.api.input.Emitter; 036import org.talend.sdk.component.api.input.Split; 037import org.talend.sdk.component.runtime.base.Delegated; 038import org.talend.sdk.component.runtime.base.LifecycleImpl; 039import org.talend.sdk.component.runtime.serialization.ContainerFinder; 040import org.talend.sdk.component.runtime.serialization.EnhancedObjectInputStream; 041 042import lombok.AllArgsConstructor; 043import lombok.extern.slf4j.Slf4j; 044 045@Slf4j 046public class PartitionMapperImpl extends LifecycleImpl implements Mapper, Delegated { 047 048 private static final Object[] NO_ARG = new Object[0]; 049 050 private String inputName; 051 052 private boolean stream; 053 054 private transient Method assessor; 055 056 private transient Method split; 057 058 private transient Method inputFactory; 059 060 private transient Function<Long, Object[]> splitArgSupplier; 061 062 private final Map<String, String> internalConfiguration; 063 064 public PartitionMapperImpl(final String rootName, final String name, final String inputName, final String plugin, 065 final boolean stream, final Map<String, String> internalConfiguration, final Serializable instance) { 066 super(instance, rootName, name, plugin); 067 this.stream = stream; 068 this.inputName = inputName; 069 this.internalConfiguration = internalConfiguration; 070 log.debug("[PartitionMapperImpl] {} internalConfig:{}.", name, internalConfiguration); 071 } 072 073 public PartitionMapperImpl(final String rootName, final String name, final String inputName, final String plugin, 074 final boolean stream, final Serializable instance) { 075 this(rootName, name, inputName, plugin, stream, emptyMap(), instance); 076 } 077 078 protected PartitionMapperImpl() { 079 internalConfiguration = emptyMap(); 080 } 081 082 @Override 083 public long assess() { 084 lazyInit(); 085 if (assessor != null) { 086 return Number.class.cast(doInvoke(assessor)).longValue(); 087 } 088 return 1; 089 } 090 091 @Override 092 public List<Mapper> split(final long desiredSize) { 093 lazyInit(); 094 return ((Collection<?>) doInvoke(split, splitArgSupplier.apply(desiredSize))) 095 .stream() 096 .map(Serializable.class::cast) 097 .map(mapper -> new PartitionMapperImpl(rootName(), name(), inputName, plugin(), stream, 098 internalConfiguration, mapper)) 099 .collect(toList()); 100 } 101 102 @Override 103 public Input create() { 104 lazyInit(); 105 // note: we can surely mutualize/cache the reflection a bit here but let's wait 106 // to see it is useful before doing it, 107 // java 7/8 made enough progress to probably make it smooth OOTB 108 final Serializable input = Serializable.class.cast(doInvoke(inputFactory)); 109 log.debug("[PartitionMapperImpl#create] isStream? {}.", isStream()); 110 if (isStream()) { 111 return new StreamingInputImpl(rootName(), inputName, plugin(), input, 112 Streaming.loadRetryConfiguration(plugin()), 113 Streaming.loadStopStrategy(plugin(), internalConfiguration)); 114 } 115 return new InputImpl(rootName(), inputName, plugin(), input); 116 } 117 118 @Override 119 public boolean isStream() { 120 return stream; 121 } 122 123 @Override 124 public Object getDelegate() { 125 return delegate; 126 } 127 128 public Map<String, String> getInternalConfiguration() { 129 return ofNullable(internalConfiguration).orElse(emptyMap()); 130 } 131 132 private void lazyInit() { 133 if (split == null || inputFactory == null) { 134 inputName = inputName == null || inputName.isEmpty() ? name() : inputName; 135 assessor = findMethods(Assessor.class).findFirst().orElse(null); 136 split = findMethods(Split.class).findFirst().get(); 137 inputFactory = findMethods(Emitter.class).findFirst().get(); 138 139 switch (split.getParameterCount()) { 140 case 1: 141 if (int.class == split.getParameterTypes()[0]) { 142 splitArgSupplier = desiredSize -> new Object[] { desiredSize.intValue() }; 143 } else if (long.class == split.getParameterTypes()[0]) { 144 splitArgSupplier = desiredSize -> new Object[] { desiredSize }; 145 } else { 146 throw new IllegalArgumentException("@PartitionSize only supports int and long"); 147 } 148 break; 149 case 0: 150 default: 151 splitArgSupplier = desiredSize -> NO_ARG; 152 } 153 } 154 } 155 156 Object writeReplace() throws ObjectStreamException { 157 return new SerializationReplacer(plugin(), rootName(), name(), inputName, stream, serializeDelegate(), 158 internalConfiguration); 159 } 160 161 @AllArgsConstructor 162 private static class SerializationReplacer implements Serializable { 163 164 private final String plugin; 165 166 private final String component; 167 168 private final String name; 169 170 private final String input; 171 172 private final boolean stream; 173 174 private final byte[] value; 175 176 private final Map<String, String> internalConfiguration; 177 178 Object readResolve() throws ObjectStreamException { 179 try { 180 return new PartitionMapperImpl(component, name, input, plugin, stream, internalConfiguration, 181 loadDelegate()); 182 } catch (final IOException | ClassNotFoundException e) { 183 final InvalidObjectException invalidObjectException = new InvalidObjectException(e.getMessage()); 184 invalidObjectException.initCause(e); 185 throw invalidObjectException; 186 } 187 } 188 189 private Serializable loadDelegate() throws IOException, ClassNotFoundException { 190 try (final ObjectInputStream ois = new EnhancedObjectInputStream(new ByteArrayInputStream(value), 191 ContainerFinder.Instance.get().find(plugin).classloader())) { 192 final Object obj = ois.readObject(); 193 return Serializable.class.cast(obj); 194 } 195 } 196 } 197}