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.manager.service;
017
018import java.io.ObjectStreamException;
019import java.util.Iterator;
020import java.util.Map;
021import java.util.NoSuchElementException;
022import java.util.function.Function;
023
024import org.talend.sdk.component.api.record.Record;
025import org.talend.sdk.component.api.service.source.ProducerFinder;
026import org.talend.sdk.component.runtime.input.Input;
027import org.talend.sdk.component.runtime.input.Mapper;
028import org.talend.sdk.component.runtime.manager.ComponentManager;
029import org.talend.sdk.component.runtime.manager.service.api.ComponentInstantiator;
030import org.talend.sdk.component.runtime.serialization.SerializableService;
031
032import lombok.RequiredArgsConstructor;
033import lombok.extern.slf4j.Slf4j;
034
035@Slf4j
036public class ProducerFinderImpl implements ProducerFinder {
037
038    protected String plugin;
039
040    protected ComponentInstantiator.Builder mapperFinder;
041
042    protected Function<Object, Record> recordConverter;
043
044    @Override
045    public ProducerFinder init(final String plugin, final Object builder, final Function<Object, Record> converter) {
046        this.plugin = plugin;
047        mapperFinder = ComponentInstantiator.Builder.class.cast(builder);
048        recordConverter = converter;
049        return this;
050    }
051
052    @Override
053    public Iterator<Record> find(final String familyName, final String inputName, final int version,
054            final Map<String, String> configuration) {
055        final ComponentInstantiator instantiator = getInstantiator(familyName, inputName);
056        final Mapper mapper = findMapper(instantiator, version, configuration);
057
058        return iterator(mapper.create());
059    }
060
061    protected ComponentInstantiator getInstantiator(final String familyName, final String inputName) {
062        final ComponentInstantiator.MetaFinder datasetFinder = new ComponentInstantiator.ComponentNameFinder(inputName);
063        final ComponentInstantiator instantiator =
064                this.mapperFinder.build(familyName, datasetFinder, ComponentManager.ComponentType.MAPPER);
065        if (instantiator == null) {
066            log.error("Can't find {} for family {}.", inputName, familyName);
067            throw new IllegalArgumentException(
068                    String.format("Can't find %s for family %s.", inputName, familyName));
069        }
070        return instantiator;
071    }
072
073    protected Mapper findMapper(final ComponentInstantiator instantiator, final int version,
074            final Map<String, String> configuration) {
075        return (Mapper) instantiator.instantiate(configuration, version);
076    }
077
078    protected Iterator<Record> iterator(final Input input) {
079        final Iterator<Object> iteratorObject = new InputIterator(input);
080
081        return new IteratorMap<>(iteratorObject, recordConverter);
082    }
083
084    private Object writeReplace() throws ObjectStreamException {
085        return new SerializableService(plugin, ProducerFinder.class.getName());
086    }
087
088    static class InputIterator implements Iterator<Object> {
089
090        private final Input input;
091
092        private Object nextObject;
093
094        private boolean init;
095
096        InputIterator(final Input input) {
097            this.input = input;
098        }
099
100        private static Object findNext(final Input input) {
101            return input.next();
102        }
103
104        @Override
105        public boolean hasNext() {
106            synchronized (input) {
107                if (!init) {
108                    init = true;
109                    input.start();
110                    nextObject = findNext(input);
111                }
112                if (nextObject == null) {
113                    input.stop();
114                }
115            }
116            return nextObject != null;
117        }
118
119        @Override
120        public Object next() {
121            if (!hasNext()) {
122                throw new NoSuchElementException();
123            }
124            final Object current = nextObject;
125            nextObject = findNext(input);
126            return current;
127        }
128    }
129
130    @RequiredArgsConstructor
131    static class IteratorMap<T, U> implements Iterator<U> {
132
133        private final Iterator<T> wrappedIterator;
134
135        private final Function<T, U> converter;
136
137        @Override
138        public boolean hasNext() {
139            return this.wrappedIterator.hasNext();
140        }
141
142        @Override
143        public U next() {
144            final T next = this.wrappedIterator.next();
145            return this.converter.apply(next);
146        }
147    }
148}