ConnectionPersister is a class that creates, reads, updates, and deletes the ConnectionInfo objects. ConnectionPersister is a wrapper of IEndpointConfigurationService and hides the conversions between IEndpointConfigurationService and ConnectionInfo.
Defining the Configuration Persister Interface
public interface ConnectionPersister { /* * Returns a collection of all stored configurations (resources under * a folder with the plug-in name) */ public List<ConnectionInfo> findAll(); /* * Returns a collection by its ID or null if not found */ public ConnectionInfo findById(Sid id); /* * Stores a connection info or updates it if already available. * The persister checks the availability of a connection by its ID */ public ConnectionInfo save(ConnectionInfo connection); /* * Deletes a connection info. The persister will use the ID of the connection */ public void delete(ConnectionInfo connectionInfo); /* * Allows us to subscribe to the events of the persister. * For example, if a connection is deleted, the persister will * trigger an event, notifying all subscribers. * This is an implementation of the observer pattern. */ void addChangeListener(ConfigurationChangeListener listener); /* * Forces the persister to read all the configurations and trigger * the events. This method is invoked when the plug-in is loaded * on server start-up. */ public void load(); }
Implementing the Default Connection Persister Interface
@Component public class DefaultConnectionPersister implements ConnectionPersister { private static final String CHARSET = "UTF-8"; /* * A list of listeners, who have subscribed to any configuration events, such as * connection updates and deletions. */ private final Collection<ConfigurationChangeListener> listeners; /* * Always use loggers */ private static final Logger log = LoggerFactory.getLogger(DefaultConnectionPersister.class); /* * Constants of the key names under which the connection values will be stored. */ private static final String ID = "connectionId"; private static final String NAME = "name"; private static final String SUBSCRIPTION_ID = "sunscriptionId"; private static final String KEYSTORE_CONTENT = "keystoreContent"; private static final String KEYSTORE_PASSWORD = "keystorePassword"; private static final String SERVICE_URI = "serviceUri"; /* * The IEndpointConfigurationService will be injected through spring * if the plug-in has a spring context. */ @Autowired private IEndpointConfigurationService endpointConfigurationService; /* * Persister constructor */ public DefaultConnectionPersister() { //Initialize the listeners listeners = new CopyOnWriteArrayList<ConfigurationChangeListener>(); } /* * Returns a collection of all stored configurations for this plug-in only * The service is aware of the plug-in name, thus will return only configurations for this plug-in. */ @Override public List<ConnectionInfo> findAll() { Collection<IEndpointConfiguration> configs; try { //Use the configuration service to retrieve all configurations. //The service is aware of the plug-in name, thus will return only configurations for this plug-in. configs = endpointConfigurationService.getEndpointConfigurations(); List<ConnectionInfo> result = new ArrayList<>(configs.size()); //Iterate all the connections for (IEndpointConfiguration config : configs) { //Convert the IEndpointConfiguration to our domain object - the ConnectionInfo ConnectionInfo connectionInfo = getConnectionInfo(config); if (connectionInfo != null) { log.debug("Adding connection info to result map: " + connectionInfo); result.add(connectionInfo); } } return result; } catch (IOException e) { log.debug("Error reading connections.", e); throw new RuntimeException(e); } } /* * Returns a ConnectionInfo by its ID * The service is aware of the plug-in name, thus cannot return a configuration for another plug-in. */ @Override public ConnectionInfo findById(Sid id) { //Sanity checks Validate.notNull(id, "Sid cannot be null."); IEndpointConfiguration endpointConfiguration; try { //Use the configuration service to retrieve the configuration service by its ID endpointConfiguration = endpointConfigurationService.getEndpointConfiguration(id.toString()); //Convert the IEndpointConfiguration to our domain object - the ConnectionInfo return getConnectionInfo(endpointConfiguration); } catch (IOException e) { log.debug("Error finding connection by id: " + id.toString(), e); throw new RuntimeException(e); } } /* * Save or update a connection info. * The service is aware of the plug-in name, thus cannot save the configuration * under the name of another plug-in. */ @Override public ConnectionInfo save(ConnectionInfo connectionInfo) { //Sanity checks Validate.notNull(connectionInfo, "Connection info cannot be null."); Validate.notNull(connectionInfo.getId(), "Connection info must have an id."); //Additional validation - in this case we want the name of the connection to be unique validateConnectionName(connectionInfo); try { //Find a connection with the provided ID. We don't expect to have an empty ID IEndpointConfiguration endpointConfiguration = endpointConfigurationService .getEndpointConfiguration(connectionInfo.getId().toString()); //If the configuration is null, then we are performing a save operation if (endpointConfiguration == null) { //Use the configuration service to create a new (empty) IEndpointConfiguration. //In this case, we are responsible for assigning the ID of the configuration, //which is done in the constructor of the ConnectionInfo endpointConfiguration = endpointConfigurationService.newEndpointConfiguration(connectionInfo.getId() .toString()); } //Convert the ConnectionInfo the IEndpointConfiguration addConnectionInfoToConfig(endpointConfiguration, connectionInfo); //Use the configuration service to save the endpoint configuration endpointConfigurationService.saveEndpointConfiguration(endpointConfiguration); //Fire an event to all subscribers, that we have updated a configuration. //Pass the entire connectionInfo object and let the subscribers decide if they need to do something fireConnectionUpdated(connectionInfo); return connectionInfo; } catch (IOException e) { log.error("Error saving connection " + connectionInfo, e); throw new RuntimeException(e); } } /* * Delete a connection info. The service is aware of the plug-in name, thus cannot delete a configuration * from another plug-in. */ @Override public void delete(ConnectionInfo connectionInfo) { try { //Use the configuration service to delete the connection info. The service uses the ID endpointConfigurationService.deleteEndpointConfiguration(connectionInfo.getId( ).toString()); //Fire an event to all subscribers, that we have deleted a configuration. //Pass the entire connectionInfo object and let the subscribers decide if they need to do something fireConnectionRemoved(connectionInfo); } catch (IOException e) { log.error("Error deleting endpoint configuration: " + connectionInfo, e); throw new RuntimeException(e); } } /* * This method is used to load the entire configuration set of the plugin. * As a second step we fire a notification to all subscribers. This method * is used when the plug-in is being loaded (on server startup). */ @Override public void load() { List<ConnectionInfo> findAll = findAll(); for (ConnectionInfo connectionInfo : findAll) { fireConnectionUpdated(connectionInfo); } } /* * Attach a configuration listener. */ @Override public void addChangeListener(ConfigurationChangeListener listener) { listeners.add(listener); } /* * A helper method which iterates all event subscribers and fires the * update notification for the provided connection info. */ private void fireConnectionUpdated(ConnectionInfo connectionInfo) { for (ConfigurationChangeListener li : listeners) { li.connectionUpdated(connectionInfo); } } /* * A helper method which iterates all event subscribers and fires the * delete notification for the provided connection info. */ private void fireConnectionRemoved(ConnectionInfo connectionInfo) { for (ConfigurationChangeListener li : listeners) { li.connectionRemoved(connectionInfo); } } /* * A helper method which converts our domain object the ConnectionInfo to an IEndpointConfiguration */ private void addConnectionInfoToConfig(IEndpointConfiguration config, ConnectionInfo info) { try { config.setString(ID, info.getId().toString()); config.setString(NAME, info.getName()); config.setString(SUBSCRIPTION_ID, info.getSubscriptionId()); config.setString(KEYSTORE_CONTENT, new String(info.getKeystoreContent(), CHARSET)); config.setPassword(KEYSTORE_PASSWORD, info.getKeystorePassword()); config.setString(SERVICE_URI, info.getUri()); } catch (UnsupportedEncodingException e) { log.error("Error converting ConnectionInfo to IEndpointConfiguration.", e); throw new RuntimeException(e); } } /* * A helper method which converts the IEndpointConfiguration to our domain object the ConnectionInfo */ private ConnectionInfo getConnectionInfo(IEndpointConfiguration config) { ConnectionInfo info = null; try { Sid id = Sid.valueOf(config.getString(ID)); info = new ConnectionInfo(id); info.setName(config.getString(NAME)); info.setUri(config.getString(SERVICE_URI)); info.setSubscriptionId(config.getString(SUBSCRIPTION_ID)); info.setKeystorePassword(config.getPassword(KEYSTORE_PASSWORD)); info.setKeystoreContent(config.getString(KEYSTORE_CONTENT).getBytes(CHARSET)); } catch (IllegalArgumentException | UnsupportedEncodingException e) { log.warn("Cannot convert IEndpointConfiguration to ConnectionInfo: " + config.getId(), e); } return info; } private void validateConnectionName(ConnectionInfo connectionInfo) { ConnectionInfo configurationByName = getConfigurationByName(connectionInfo.getName()); if (configurationByName != null && !configurationByName.getId().toString().equals(connectionInfo.getId().toString ())) { throw new RuntimeException("Connection with the same name already exists: " + connectionInfo); } } private ConnectionInfo getConfigurationByName(String name) { Validate.notNull(name, "Connection name cannot be null."); Collection<ConnectionInfo> findAllClientInfos = findAll(); for (ConnectionInfo info : findAllClientInfos) { if (name.equals(info.getName())) { return info; } } return null; } }