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.xbean;
017
018import java.io.IOException;
019import java.io.InputStream;
020import java.net.URL;
021import java.util.Iterator;
022import java.util.jar.JarInputStream;
023import java.util.zip.ZipEntry;
024
025import org.apache.xbean.finder.archive.Archive;
026import org.talend.sdk.component.classloader.ConfigurableClassLoader;
027
028import lombok.Getter;
029import lombok.RequiredArgsConstructor;
030
031@RequiredArgsConstructor
032public class NestedJarArchive implements Archive, AutoCloseable {
033
034    @Getter
035    private final URL rootMarker;
036
037    private final JarInputStream jarStream;
038
039    // this classloader already handles all the logic we need
040    private final ConfigurableClassLoader loader;
041
042    @Override
043    public InputStream getBytecode(final String className) {
044        return loader.getResourceAsStream(className);
045    }
046
047    @Override
048    public Class<?> loadClass(final String className) throws ClassNotFoundException {
049        return loader.loadClass(className);
050    }
051
052    @Override
053    public Iterator<Entry> iterator() {
054        return new Iterator<Entry>() {
055
056            private ZipEntry entry;
057
058            @Override
059            public boolean hasNext() {
060                if (entry == null) {
061                    try {
062                        while ((entry = jarStream.getNextEntry()) != null && !entry.getName().endsWith(".class")) {
063                            // next
064                        }
065                    } catch (final IOException e) {
066                        throw new IllegalStateException("Invalid nested jar", e);
067                    }
068                }
069                return entry != null;
070            }
071
072            @Override
073            public Entry next() {
074                final ZipEntry returned = entry;
075                entry = null;
076
077                final String name = returned
078                        .getName()
079                        .replace('.', '/')
080                        .substring(0, returned.getName().length()
081                                - (returned.getName().endsWith(".class") ? ".class".length() : 0));
082                return new Entry() {
083
084                    @Override
085                    public String getName() {
086                        return name;
087                    }
088
089                    @Override
090                    public InputStream getBytecode() {
091                        return new KeepingOpenInputStream(jarStream);
092                    }
093                };
094            }
095        };
096    }
097
098    @Override
099    public void close() throws Exception {
100        jarStream.close();
101    }
102}