package com.verisign.iot.discovery.commons; import com.verisign.iot.discovery.exceptions.ConfigurationException; import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.nio.file.Files; import java.util.Observable; import java.util.Observer; /** * An abstract configurable entity that can be introspected: it extends {@link Observable} and in * case of verbosity it is able to push its observer {@link Observer}. * * @author pmaresca <pmaresca@verisign.com> * @version 1.0 * @since 2015/05/02 */ public abstract class Configurable { /** * DNS Server to be addressed. */ protected InetAddress dnsServer; /** * Secured DNS Domain to be used. */ // TODO To be reworded protected String dnsSecDomain; /** * Trust Anchor to be used, aka Cryptographic Public Key of the Signed Zone. * * @see <a href="https://tools.ietf.org/html/draft-ietf-dnsop-dnssec-trust-anchor-04">DNSSEC * Trust Anchor</a> */ protected String trustAnchorDefault; /** * File containing the Trust Anchor. */ protected File trustAnchorFile; /** * To push client notifications upon internal events. */ protected boolean introspected; /** * Configuration validation */ protected boolean checked; /** * Private Helper to push client notifications about any state change */ protected Notifier notifier; protected Configurable() { this.introspected = false; this.checked = false; this.notifier = this.new Notifier(); } /** * Configure the target Resolution Server. * * @param host Server's address * @return This instance to further configure */ public final Configurable dnsServer(InetAddress host) { this.dnsServer = host; this.checked = false; return this; } /** * Configure the default DNSSEC domain. * * @param domain A <code>String</code> containing the domain name * @return This instance to further configure */ public final Configurable dnsSecDomain(String domain) { this.dnsSecDomain = domain; this.checked = false; return this; } /** * Configure the default DNSSEC domain. * * @param anchor A <code>String</code> containing the public key * @return This instance to further configure */ public final Configurable trustAnchorDefault(String anchor) { this.trustAnchorDefault = anchor; this.checked = false; return this; } /** * Configure the default trust anchor by providing a <code>File</code> containing it. * * @param anchorContainer A <code>Filer</code> storing the public key * @return This instance to further configure */ public final Configurable trustAnchorFile(File anchorContainer) { this.trustAnchorFile = anchorContainer; this.checked = false; return this; } /** * Configure its introspection property. * * @param isIt If <code>true</code> it will push asynchronously its observers upon internal * events. * @return This instance to further configure */ public final Configurable introspected(boolean isIt) { this.introspected = isIt; this.checked = false; return this; } /** * Set a status change observe encapsulating the client handler. * * @param handler A client status change handler * @return This instance to further configure */ public final Configurable observer(Observer handler) { if (handler != null) { this.notifier.addObserver(handler); this.checked = false; } return this; } /** * To check the actual configuration. * * @param reloadConfig <code>true</code> iff the configuration has to be reloaded * @throws ConfigurationException In case this instance has not been configured properly */ public synchronized void checkConfiguration(boolean reloadConfig) throws ConfigurationException { if (!reloadConfig && this.checked) { return; } if (this.trustAnchorFile != null) { try { this.trustAnchorDefault = new String(Files.readAllBytes(this.trustAnchorFile.toPath())); } catch (IOException ex) { throw new ConfigurationException("Unable to read the Trust Anchor from " + this.trustAnchorFile.getName()); } } else { try { File defaultTrustAnchorLocation = new File(Constants.DEFAULT_TRUST_ANCHOR_LOCATION); if (defaultTrustAnchorLocation.exists()) { this.trustAnchorDefault = new String(Files.readAllBytes(defaultTrustAnchorLocation.toPath())); } else { this.trustAnchorDefault = Constants.DEFAULT_TRUST_ANCHOR; } } catch (IOException ex) { throw new ConfigurationException("Unable to read the Trust Anchor from " + this.trustAnchorFile.getName()); } } this.checked = true; } /** * Check whether the configuration has been validated or not. * * @throws ConfigurationException In case configurations have been done, but not checked. */ protected void validatedConf() throws ConfigurationException { checkConfiguration(false); } /** * Notify this instance's observers. * * @param what A <code>String</code> containing the status change event */ protected void statusChange(String what) { if (what == null || what.isEmpty()) { this.notifier.notifyObservers(); } else { this.notifier.notifyObservers(what); } } /** * Notify this instance's observers. * * @param what An <code>Object</code> containing the status change event */ protected void statusChange(Object what) { if (what != null) { this.notifier.notifyObservers(what); } } /** * Helper class encapsulating the introspective capabilities (asynchronous notification on * status changes). * * @author pmaresca <pmaresca@verisign.com> * @version 1.0 * @since 2015/05/02 */ private class Notifier extends Observable { @Override public final void notifyObservers() { setChanged(); if (Configurable.this.introspected) { super.notifyObservers(); } } @Override public final void notifyObservers(Object arg) { setChanged(); if (Configurable.this.introspected) { super.notifyObservers(arg); } } } }