001/** 002 * Copyright (C) 2006-2022 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 invoke(PostConstruct.class); 050 } 051 052 @Override 053 public void stop() { 054 invoke(PreDestroy.class); 055 } 056 057 private void invoke(final Class<? extends Annotation> marker) { 058 findMethods(marker).forEach(this::doInvoke); 059 } 060 061 @Override 062 public boolean equals(final Object o) { 063 if (this == o) { 064 return true; 065 } 066 if (o == null || getClass() != o.getClass()) { 067 return false; 068 } 069 final LifecycleImpl lifecycle = LifecycleImpl.class.cast(o); 070 return Objects.equals(delegate, lifecycle.delegate) && Objects.equals(loader, lifecycle.loader); 071 } 072 073 @Override 074 public int hashCode() { 075 return Objects.hash(delegate, loader); 076 } 077 078 protected Object doInvoke(final Method m, final Object... args) { 079 final Thread thread = Thread.currentThread(); 080 final ClassLoader oldLoader = thread.getContextClassLoader(); 081 thread.setContextClassLoader(findLoader()); 082 try { 083 return m.invoke(delegate, args); 084 } catch (final IllegalAccessException e) { 085 throw new IllegalStateException(e); 086 } catch (final InvocationTargetException e) { 087 throw toRuntimeException(e); 088 } finally { 089 thread.setContextClassLoader(oldLoader); 090 } 091 } 092 093 // mainly done by instance to avoid to rely on a registry maybe not initialized 094 // after serialization 095 protected Stream<Method> findMethods(final Class<? extends Annotation> marker) { 096 final Thread thread = Thread.currentThread(); 097 final ClassLoader oldLoader = thread.getContextClassLoader(); 098 thread.setContextClassLoader(findLoader()); 099 try { 100 return Stream.of(delegate.getClass().getMethods()).filter(m -> m.isAnnotationPresent(marker)).peek(m -> { 101 if (!m.isAccessible()) { 102 m.setAccessible(true); 103 } 104 }); 105 } finally { 106 thread.setContextClassLoader(oldLoader); 107 } 108 } 109 110 protected byte[] serializeDelegate() { 111 return Serializer.toBytes(delegate); 112 } 113 114 private ClassLoader findLoader() { 115 if (loader == null) { 116 try { 117 loader = ContainerFinder.Instance.get().find(plugin()).classloader(); 118 } catch (final IllegalStateException ise) { 119 // probably better to register a finder but if not don't fail and use TCCL 120 } 121 if (loader == null) { 122 loader = Thread.currentThread().getContextClassLoader(); 123 } 124 } 125 return loader; 126 } 127}