001/** 002 * Copyright (C) 2006-2021 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.server.service.jcache; 017 018import static java.util.Optional.ofNullable; 019 020import java.lang.annotation.Annotation; 021import java.util.concurrent.TimeUnit; 022import java.util.stream.StreamSupport; 023 024import javax.annotation.PostConstruct; 025import javax.annotation.PreDestroy; 026import javax.cache.Cache; 027import javax.cache.CacheException; 028import javax.cache.CacheManager; 029import javax.cache.annotation.CacheMethodDetails; 030import javax.cache.annotation.CacheResolver; 031import javax.cache.annotation.CacheResolverFactory; 032import javax.cache.annotation.CacheResult; 033import javax.cache.configuration.Configuration; 034import javax.enterprise.context.ApplicationScoped; 035import javax.inject.Inject; 036 037import org.apache.geronimo.jcache.simple.cdi.CacheResolverImpl; 038import org.eclipse.microprofile.config.inject.ConfigProperty; 039import org.talend.sdk.component.api.meta.Documentation; 040import org.talend.sdk.component.server.front.EnvironmentResourceImpl; 041import org.talend.sdk.component.server.front.model.Environment; 042import org.talend.sdk.components.vault.jcache.CacheConfigurationFactory; 043import org.talend.sdk.components.vault.jcache.CacheSizeManager; 044 045import lombok.extern.slf4j.Slf4j; 046 047@Slf4j 048@ApplicationScoped 049public class FrontCacheResolver implements CacheResolverFactory { 050 051 @Inject 052 private CacheManager cacheManager; 053 054 @Inject 055 private CacheConfigurationFactory cacheConfiguration; 056 057 @Inject 058 @Documentation("How often (in ms) should we invalidate the credentials caches.") 059 @ConfigProperty(name = "talend.vault.cache.jcache.refresh.period", defaultValue = "30000") 060 private Long refreshPeriod; 061 062 @Inject 063 EnvironmentResourceImpl env; 064 065 private long lastUpdated; 066 067 private volatile boolean running = true; 068 069 private Thread thread; 070 071 @PostConstruct 072 private void startRefresh() { 073 lastUpdated = System.currentTimeMillis(); 074 thread = new Thread(() -> refreshThread(refreshPeriod)); 075 thread.setName(getClass().getName() + "-refresher"); 076 thread.setPriority(Thread.NORM_PRIORITY); 077 thread.setDaemon(false); 078 thread.setUncaughtExceptionHandler((t, e) -> log.error(e.getMessage(), e)); 079 thread.start(); 080 } 081 082 @PreDestroy 083 private void stopRefresh() { 084 running = false; 085 ofNullable(thread).ifPresent(it -> { 086 try { 087 it.interrupt(); 088 it.join(TimeUnit.SECONDS.toMillis(5)); // not super important here 089 } catch (final InterruptedException e) { 090 log.warn(e.getMessage()); 091 Thread.currentThread().interrupt(); 092 } 093 }); 094 } 095 096 private void refreshThread(final long delay) { 097 try { 098 while (running) { 099 try { 100 updateIfNeeded(); 101 } catch (final Exception e) { 102 log.warn(e.getMessage(), e); 103 } 104 Thread.sleep(delay); 105 } 106 } catch (final InterruptedException ie) { 107 Thread.currentThread().interrupt(); 108 } 109 } 110 111 private void updateIfNeeded() { 112 final Environment environment = env.get(); 113 // assumes time are synch-ed but not a high assumption 114 if (lastUpdated < environment.getLastUpdated().getTime()) { 115 clearCaches(); 116 lastUpdated = System.currentTimeMillis(); 117 } 118 } 119 120 private void clearCaches() { 121 StreamSupport 122 .stream(cacheManager.getCacheNames().spliterator(), false) 123 .filter(name -> name.startsWith("org.talend.sdk.component.server.front.")) 124 .peek(c -> log.warn("[clearCaches] clear cache {}.")) 125 .forEach(r -> cacheManager.getCache(r).clear()); 126 } 127 128 @Override 129 public CacheResolver getCacheResolver(final CacheMethodDetails<? extends Annotation> cacheMethodDetails) { 130 return findCacheResolver(cacheMethodDetails.getCacheName()); 131 } 132 133 @Override 134 public CacheResolver getExceptionCacheResolver(final CacheMethodDetails<CacheResult> cacheMethodDetails) { 135 return findCacheResolver(cacheMethodDetails.getCacheAnnotation().exceptionCacheName()); 136 } 137 138 private CacheResolver findCacheResolver(final String exceptionCacheName) { 139 Cache<?, ?> cache = cacheManager.getCache(exceptionCacheName); 140 if (cache == null) { 141 try { 142 cache = createCache(exceptionCacheName); 143 } catch (final CacheException ce) { 144 cache = cacheManager.getCache(exceptionCacheName); 145 } 146 } 147 return new CacheResolverImpl(cache); 148 } 149 150 private Cache<?, ?> createCache(final String exceptionCacheName) { 151 log.debug("[createCache] {}", exceptionCacheName); 152 final CacheSizeManager<Object, Object> listener = new CacheSizeManager<>(cacheConfiguration.maxSize()); 153 final Configuration<Object, Object> configuration = cacheConfiguration.createConfiguration(listener); 154 final Cache<Object, Object> instance = cacheManager.createCache(exceptionCacheName, configuration); 155 listener.accept(instance); 156 return instance; 157 } 158}