/*
* Copyright 2012 Nodeable Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.streamreduce.core.model;
import com.google.code.morphia.annotations.Embedded;
import com.google.code.morphia.annotations.Transient;
import com.google.common.collect.Lists;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.hibernate.validator.constraints.NotEmpty;
import javax.validation.Valid;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.Set;
/**
* <p>Encapsulation of several general attributes used to define how messages and payloads are sent outbound from
* Nodeable. An OutboundConfiguration is meant to always be associated with an inbound Connection and
* represents the destination where @{@link Event}s and {@link com.streamreduce.core.model.messages.SobaMessage}s created in
* Nodeable that are related to the Connection are also sent to.</p>
*
*<p>Specifically an OutboundConfiguration contains a reference to ConnectionCredentials, a Connection where payloads
* originate from, and also the following fields that configure the nature of the OutboundConfiguration:</p>
*
* <ul>
* <li><b>protocol (required)</b> - The protocol used for the connection. For instance an AWS Connection might have
* "s3" or "dynamodb" as the protocol.</li>
* <li><b>destination</b> - A destination within the connection accessed through the protocol where
* the messages will go. This field is may be optional, depending on the protocol, and a sane default for
* destination will be used if necessary. Examples of a destination would be an S3 region or a DynamoDB database.
* </li>
* <li><b>namespace</b> - A location within a destination where events and messages will be placed. This field is
* optional and a sane default for namespace will be used if necessary. Examples of a namespace would be an S3
* bucket or a DynamoDB table.</li>
* <li><b>dataTypes</b> - The type of messages/events/payloads that will be delivered to this OutboundConfiguration.
* All available datatypes are listed as enum values on {@link OutboundDataType}</li>
* </ul>
*
* @see com.streamreduce.core.model.Connection#getOutboundConfigurations()
*/
public class OutboundConfiguration {
@NotEmpty
private String protocol;
private String destination;
private String namespace;
@Embedded
@Valid
private ConnectionCredentials credentials;
private Set<OutboundDataType> dataTypes; // user configured values they want to receive
@Transient
private transient Connection originatingConnection;
private OutboundConfiguration() {}
/**
* The protocol supported by a specific Connection used. This is a required attribute and is guaranteed
* to be non-null and non-blank.
*
* @return The protocol supported by a specific Connection used.
*/
public String getProtocol() {
return protocol;
}
/**
* The destination where messages will be sent on the Connection/protocol. This is an optional field.
* @return - The string identifying the destination, or an empty String if the destination is not set.
*/
public String getDestination() {
if (StringUtils.isNotBlank(destination)) { return destination;}
else { return "";}
}
/**
* The namespace underneath the destination within a Connection/protocol where messages will be sent.
* This is an optional field.
* @return - The string identifying the namespace, or an empty String if the namespace is not set.
*/
public String getNamespace() {
if (StringUtils.isNotBlank(namespace)) { return namespace;}
else { return "";}
}
public Set<OutboundDataType> getDataTypes() {
return dataTypes;
}
/**
* Returns a reference to the Connection that represents the inbound Connection feeding payloads to this Outbound
* Connection
* @return The inbound Connection
*/
public Connection getOriginatingConnection() {
return originatingConnection;
}
/**
* Returns the {@link ConnectionCredentials} used for authentication by this OutboundConfiguration.
* @return The ConnectionCredentials.
*/
public ConnectionCredentials getCredentials() {
return credentials;
}
/**
* Sets the inbound Connection that is feeding payloads to this OutboundConfiguration. This field is marked with
* package-private access as it should only be set by the containing inbound Connection.
* @param originatingConnection - The inbound Connection
*/
void setOriginatingConnection(Connection originatingConnection) {
this.originatingConnection = originatingConnection;
}
/**
* Merges the following properties from a JSON object in to the Connection object:
* <ul>
* <li>credentials</li>
* <li>destination</li>
* <li>namespace</li>
* <li>dataTypes - overwrite only</li>
* </ul>
*
* @param json {@link JSONObject} used to defined properties of this Connection.
*/
public void mergeWithJSON(JSONObject json) {
destination = json.containsKey("destination") ? json.getString("destination") : destination;
namespace = json.containsKey("namespace") ? json.getString("namespace") : namespace;
if (json.containsKey("credentials")) {
JSONObject credentialsJSON = json.getJSONObject("credentials");
if (credentialsJSON.containsKey("username")) {
credentials.setIdentity(credentialsJSON.getString("username"));
}
if (credentialsJSON.containsKey("password")) {
credentials.setCredential(credentialsJSON.getString("password"));
}
}
if (json.containsKey("dataTypes")) {
JSONArray dataTypesJSON = json.getJSONArray("dataTypes");
if (dataTypesJSON.size() > 0) {
dataTypes.clear();
for (Iterator iter = dataTypesJSON.iterator(); iter.hasNext();) {
String dataType = ((String) iter.next()).toUpperCase();
dataTypes.add(OutboundDataType.valueOf(dataType));
}
}
}
}
@Override
public boolean equals(Object o) {
if (o instanceof OutboundConfiguration) {
OutboundConfiguration that = (OutboundConfiguration) o;
return new EqualsBuilder()
.append(this.protocol, that.protocol)
.append(this.destination, that.destination)
.append(this.namespace, that.namespace)
.append(this.dataTypes, that.dataTypes)
.append(this.credentials, that.credentials)
.isEquals();
}
return false;
}
@Override
public int hashCode() {
return new HashCodeBuilder()
.append(protocol)
.append(destination)
.append(namespace)
.append(dataTypes)
.append(credentials)
.toHashCode();
}
@Override
public String toString() {
return "OutboundConfiguration {" +
"protocol='" + protocol + '\'' +
", destination='" + destination + '\'' +
", namespace='" + namespace + '\'' +
", credentials.identity=" + credentials.getIdentity() +
", dataTypes=" + dataTypes +
", originatingConnection.id=" + originatingConnection.toString() +
'}';
}
public static class Builder {
private OutboundConfiguration theObject;
public Builder() {
theObject = new OutboundConfiguration();
}
public OutboundConfiguration build() {
if (StringUtils.isBlank(theObject.protocol) || CollectionUtils.isEmpty(theObject.dataTypes) ||
theObject.credentials == null || StringUtils.isBlank(theObject.credentials.getIdentity())) {
throw new IllegalStateException("OutboundConfiguration must have a non-blank protocol, " +
"a non-empty set of dataTypes, and non-null credentials with the identity set.");
}
return theObject;
}
public Builder destination(String destination) {
theObject.destination = destination;
return this;
}
public Builder protocol(String protocol) {
theObject.protocol = protocol;
return this;
}
public Builder namespace(String namespace) {
theObject.namespace = namespace;
return this;
}
public Builder dataTypes(OutboundDataType... dataTypes) {
if (dataTypes.length > 0) {
theObject.dataTypes = EnumSet.copyOf(Lists.newArrayList(dataTypes));
}
return this;
}
public Builder credentials(ConnectionCredentials credentials) {
theObject.credentials = credentials;
return this;
}
public Builder originatingConnection(Connection connection) {
theObject.originatingConnection = connection;
return this;
}
}
}