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.base; 017 018import static org.talend.sdk.component.runtime.base.lang.exception.InvocationExceptionWrapper.toRuntimeException; 019 020import java.lang.annotation.Annotation; 021import java.lang.reflect.InvocationTargetException; 022import java.lang.reflect.Method; 023import java.util.Objects; 024import java.util.stream.Stream; 025 026import javax.annotation.PostConstruct; 027import javax.annotation.PreDestroy; 028 029import org.talend.sdk.component.runtime.serialization.ContainerFinder; 030 031// base class to handle postconstruct/predestroy 032public class LifecycleImpl extends Named implements Lifecycle { 033 034 protected Object delegate; 035 036 private transient ClassLoader loader; 037 038 public LifecycleImpl(final Object delegate, final String rootName, final String name, final String plugin) { 039 super(rootName, name, plugin); 040 this.delegate = delegate; 041 } 042 043 protected LifecycleImpl() { 044 // no-op 045 } 046 047 @Override 048 public void start() { 049 findMethods(PostConstruct.class).forEach(it -> doInvoke(it, evaluateParameters(PostConstruct.class, it))); 050 } 051 052 protected Object[] evaluateParameters(final Class<? extends Annotation> marker, final Method method) { 053 return new Object[0]; 054 } 055 056 @Override 057 public void stop() { 058 invoke(PreDestroy.class); 059 } 060 061 private void invoke(final Class<? extends Annotation> marker) { 062 findMethods(marker).forEach(this::doInvoke); 063 } 064 065 @Override 066 public boolean equals(final Object o) { 067 if (this == o) { 068 return true; 069 } 070 if (o == null || getClass() != o.getClass()) { 071 return false; 072 } 073 final LifecycleImpl lifecycle = LifecycleImpl.class.cast(o); 074 return Objects.equals(delegate, lifecycle.delegate) && Objects.equals(loader, lifecycle.loader); 075 } 076 077 @Override 078 public int hashCode() { 079 return Objects.hash(delegate, loader); 080 } 081 082 protected Object doInvoke(final Method m, final Object... args) { 083 final Thread thread = Thread.currentThread(); 084 final ClassLoader oldLoader = thread.getContextClassLoader(); 085 thread.setContextClassLoader(findLoader()); 086 try { 087 return m.invoke(delegate, args); 088 } catch (final IllegalAccessException e) { 089 throw new IllegalStateException(e); 090 } catch (final InvocationTargetException e) { 091 throw toRuntimeException(e); 092 } finally { 093 thread.setContextClassLoader(oldLoader); 094 } 095 } 096 097 // mainly done by instance to avoid to rely on a registry maybe not initialized 098 // after serialization 099 protected Stream<Method> findMethods(final Class<? extends Annotation> marker) { 100 final Thread thread = Thread.currentThread(); 101 final ClassLoader oldLoader = thread.getContextClassLoader(); 102 thread.setContextClassLoader(findLoader()); 103 try { 104 return Stream.of(delegate.getClass().getMethods()).filter(m -> m.isAnnotationPresent(marker)).peek(m -> { 105 if (!m.isAccessible()) { 106 m.setAccessible(true); 107 } 108 }); 109 } finally { 110 thread.setContextClassLoader(oldLoader); 111 } 112 } 113 114 protected byte[] serializeDelegate() { 115 return Serializer.toBytes(delegate); 116 } 117 118 private ClassLoader findLoader() { 119 if (loader == null) { 120 try { 121 loader = ContainerFinder.Instance.get().find(plugin()).classloader(); 122 } catch (final IllegalStateException ise) { 123 // probably better to register a finder but if not don't fail and use TCCL 124 } 125 if (loader == null) { 126 loader = Thread.currentThread().getContextClassLoader(); 127 } 128 } 129 return loader; 130 } 131}