/**
* This file is part of Graylog.
*
* Graylog is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Graylog is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Graylog. If not, see <http://www.gnu.org/licenses/>.
*/
package org.graylog2.plugin.lookup;
import com.google.common.util.concurrent.AbstractIdleService;
import com.google.inject.assistedinject.Assisted;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.graylog2.lookup.LookupTable;
import org.joda.time.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Optional;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import javax.inject.Named;
import static com.google.common.base.Preconditions.checkState;
public abstract class LookupDataAdapter extends AbstractIdleService {
private static final Logger LOG = LoggerFactory.getLogger(LookupDataAdapter.class);
private final String id;
private LookupTable lookupTable;
private final String name;
private final LookupDataAdapterConfiguration config;
private final ScheduledExecutorService scheduler;
private ScheduledFuture<?> refreshFuture = null;
private AtomicReference<Throwable> dataSourceError = new AtomicReference<>();
protected LookupDataAdapter(String id, String name, LookupDataAdapterConfiguration config,
@Named("daemonScheduler") ScheduledExecutorService scheduler) {
this.id = id;
this.name = name;
this.config = config;
this.scheduler = scheduler;
}
@Override
protected void startUp() throws Exception {
doStart();
final Duration interval = refreshInterval();
if (!interval.equals(Duration.ZERO)) {
LOG.debug("Schedule data adapter refresh method every {}ms", interval.getMillis());
this.refreshFuture = scheduler.scheduleAtFixedRate(this::refresh, interval.getMillis(), interval.getMillis(), TimeUnit.MILLISECONDS);
}
}
protected abstract void doStart() throws Exception;
@Override
protected void shutDown() throws Exception {
if (refreshFuture != null && !refreshFuture.isCancelled()) {
refreshFuture.cancel(true);
}
doStop();
}
protected abstract void doStop() throws Exception;
/**
* Returns the refresh interval for this data adapter. Use {@link Duration#ZERO} if refresh should be disabled.
* @return the refresh interval
*/
protected abstract Duration refreshInterval();
public void refresh() {
try {
doRefresh();
} catch (Exception e) {
LOG.error("Couldn't refresh data adapter", e);
}
}
protected abstract void doRefresh() throws Exception;
protected void clearError() {
dataSourceError.set(null);
}
public Optional<Throwable> getError() {
return Optional.ofNullable(dataSourceError.get());
}
protected void setError(Throwable throwable) {
dataSourceError.set(throwable);
}
public String id() {
return id;
}
public String name() {
return name;
}
public LookupTable getLookupTable() {
checkState(lookupTable != null, "lookup table cannot be null");
return lookupTable;
}
public void setLookupTable(LookupTable lookupTable) {
this.lookupTable = lookupTable;
}
public LookupResult get(Object key) {
if (state() == State.FAILED) {
return LookupResult.empty();
}
checkState(isRunning(), "Data adapter needs to be started before it can be used");
return doGet(key);
}
protected abstract LookupResult doGet(Object key);
public abstract void set(Object key, Object value);
public LookupDataAdapterConfiguration getConfig() {
return config;
}
public interface Factory<T extends LookupDataAdapter> {
T create(@Assisted("id") String id, @Assisted("name") String name, LookupDataAdapterConfiguration configuration);
Descriptor getDescriptor();
}
public abstract static class Descriptor<C extends LookupDataAdapterConfiguration> {
private final String type;
private final Class<C> configClass;
public Descriptor(String type, Class<C> configClass) {
this.type = type;
this.configClass = configClass;
}
@JsonProperty("type")
public String getType() {
return type;
}
@JsonProperty("config_class")
public Class<C> getConfigClass() {
return configClass;
}
@JsonProperty("default_config")
public abstract C defaultConfiguration();
}
}