aboutsummaryrefslogtreecommitdiffstats
path: root/datarouter-prov/src
diff options
context:
space:
mode:
authorsg481n <sg481n@att.com>2017-08-03 17:56:38 -0400
committerSai Gandham <sg481n@att.com>2017-08-04 15:41:45 +0000
commitaaf2df8b908fcb48043d2cd51803d8fd99f18b43 (patch)
tree5f90faea16d339e30ee2b7dc6607d7fdbfaeaaa7 /datarouter-prov/src
parent6ec9a9ce6c1062efa99da501fe8c6ea116aebf6f (diff)
 [DMAAP-48] Initial code import
Change-Id: I3e65371093487d7de167ec6c29f327f366f1e299 Signed-off-by: sg481n <sg481n@att.com>
Diffstat (limited to 'datarouter-prov/src')
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/authz/AuthorizationResponse.java58
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/authz/AuthorizationResponseSupplement.java52
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/authz/Authorizer.java62
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/authz/impl/AuthRespImpl.java97
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/authz/impl/AuthRespSupplementImpl.java71
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/authz/impl/AuthzResource.java100
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/authz/impl/ProvAuthorizer.java179
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/authz/impl/ProvDataProvider.java66
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/authz/impl/package.html68
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/authz/package.html38
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/BaseServlet.java869
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/DRFeedsServlet.java300
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/FeedLogServlet.java38
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/FeedServlet.java362
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/GroupServlet.java386
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/InternalServlet.java506
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/LogServlet.java433
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/Main.java245
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/Poker.java318
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/ProxyServlet.java304
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/PublishServlet.java192
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/RouteServlet.java429
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/StatisticsServlet.java588
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/SubLogServlet.java39
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/SubscribeServlet.java288
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/SubscriptionServlet.java476
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/SynchronizerTask.java614
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/BaseLogRecord.java184
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Deleteable.java41
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/DeliveryExtraRecord.java68
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/DeliveryRecord.java137
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/EgressRoute.java227
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/EventLogRecord.java84
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/ExpiryRecord.java141
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Feed.java760
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/FeedAuthorization.java96
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/FeedEndpointID.java87
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/FeedLinks.java103
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Group.java417
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/IngressRoute.java542
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Insertable.java41
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/JSONable.java40
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/LOGJSONable.java40
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Loadable.java65
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/LogRecord.java235
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/NetworkRoute.java230
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/NodeClass.java179
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Parameters.java257
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/PubFailRecord.java85
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/PublishRecord.java153
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/SubDelivery.java109
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/SubLinks.java95
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Subscription.java511
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Syncable.java57
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Updateable.java40
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/package.html31
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/eelf/EelfMsgs.java56
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/eelf/JettyFilter.java38
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/package.html123
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/DB.java711
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/DRRouteCLI.java456
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/JSONUtilities.java76
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/LogfileLoader.java549
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/PurgeLogDirTask.java70
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/RLEBitSet.java418
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/ThrottleFilter.java316
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/URLUtilities.java130
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/package.html30
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/reports/DailyLatencyReport.java194
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/reports/FeedReport.java395
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/reports/LatencyReport.java179
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/reports/Report.java155
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/reports/ReportBase.java63
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/reports/SubscriberReport.java157
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/reports/VolumeReport.java140
-rw-r--r--datarouter-prov/src/main/java/com/att/research/datarouter/reports/package.html43
-rw-r--r--datarouter-prov/src/main/java/org/json/CDL.java301
-rw-r--r--datarouter-prov/src/main/java/org/json/Cookie.java191
-rw-r--r--datarouter-prov/src/main/java/org/json/CookieList.java112
-rw-r--r--datarouter-prov/src/main/java/org/json/HTTP.java185
-rw-r--r--datarouter-prov/src/main/java/org/json/HTTPTokener.java99
-rw-r--r--datarouter-prov/src/main/java/org/json/JSONArray.java970
-rw-r--r--datarouter-prov/src/main/java/org/json/JSONException.java63
-rw-r--r--datarouter-prov/src/main/java/org/json/JSONML.java489
-rw-r--r--datarouter-prov/src/main/java/org/json/JSONObject.java1653
-rw-r--r--datarouter-prov/src/main/java/org/json/JSONString.java40
-rw-r--r--datarouter-prov/src/main/java/org/json/JSONStringer.java100
-rw-r--r--datarouter-prov/src/main/java/org/json/JSONTokener.java468
-rw-r--r--datarouter-prov/src/main/java/org/json/JSONWriter.java349
-rw-r--r--datarouter-prov/src/main/java/org/json/LOGJSONObject.java1653
-rw-r--r--datarouter-prov/src/main/java/org/json/None.java31
-rw-r--r--datarouter-prov/src/main/java/org/json/XML.java530
-rw-r--r--datarouter-prov/src/main/java/org/json/XMLTokener.java390
-rw-r--r--datarouter-prov/src/main/java/org/json/package.html40
-rw-r--r--datarouter-prov/src/main/resources/EelfMessages.properties58
-rw-r--r--datarouter-prov/src/main/resources/authz.jarbin0 -> 8387 bytes
-rw-r--r--datarouter-prov/src/main/resources/docker-compose/database/install_db.sql143
-rw-r--r--datarouter-prov/src/main/resources/docker-compose/docker-compose.yml69
-rw-r--r--datarouter-prov/src/main/resources/docker-compose/node_data/node.properties112
-rw-r--r--datarouter-prov/src/main/resources/docker-compose/node_data/self_signed/cacerts.jksbin0 -> 1936 bytes
-rw-r--r--datarouter-prov/src/main/resources/docker-compose/node_data/self_signed/keystore.jksbin0 -> 2273 bytes
-rw-r--r--datarouter-prov/src/main/resources/docker-compose/node_data/self_signed/mykey.cerbin0 -> 921 bytes
-rw-r--r--datarouter-prov/src/main/resources/docker-compose/node_data/self_signed/nodekey.cerbin0 -> 921 bytes
-rw-r--r--datarouter-prov/src/main/resources/docker-compose/prov_data/addFeed3.txt44
-rw-r--r--datarouter-prov/src/main/resources/docker-compose/prov_data/addSubscriber.txt36
-rw-r--r--datarouter-prov/src/main/resources/docker-compose/prov_data/provserver.properties59
-rw-r--r--datarouter-prov/src/main/resources/docker-compose/prov_data/self_signed/cacerts.jksbin0 -> 983 bytes
-rw-r--r--datarouter-prov/src/main/resources/docker-compose/prov_data/self_signed/keystore.jksbin0 -> 2272 bytes
-rw-r--r--datarouter-prov/src/main/resources/docker-compose/prov_data/self_signed/mykey.cerbin0 -> 921 bytes
-rw-r--r--datarouter-prov/src/main/resources/docker/Dockerfile9
-rw-r--r--datarouter-prov/src/main/resources/docker/startup.sh16
-rw-r--r--datarouter-prov/src/main/resources/log4j.properties68
-rw-r--r--datarouter-prov/src/main/resources/logback.xml405
-rw-r--r--datarouter-prov/src/main/resources/misc/doaction53
-rw-r--r--datarouter-prov/src/main/resources/misc/dr-route26
-rw-r--r--datarouter-prov/src/main/resources/misc/drtrprov131
-rw-r--r--datarouter-prov/src/main/resources/misc/havecert.tmpl11
-rw-r--r--datarouter-prov/src/main/resources/misc/log4j.drroute.properties41
-rw-r--r--datarouter-prov/src/main/resources/misc/log4j.properties.tmpl68
-rw-r--r--datarouter-prov/src/main/resources/misc/mysql_dr_schema.sql139
-rw-r--r--datarouter-prov/src/main/resources/misc/notes78
-rw-r--r--datarouter-prov/src/main/resources/misc/provcmd163
-rw-r--r--datarouter-prov/src/main/resources/misc/runreports54
-rw-r--r--datarouter-prov/src/main/resources/provserver.properties48
-rw-r--r--datarouter-prov/src/main/resources/startup.sh0
-rw-r--r--datarouter-prov/src/main/resources/subscriber.jarbin0 -> 2150417 bytes
-rw-r--r--datarouter-prov/src/test/java/datarouter/provisioning/AllTests.java48
-rw-r--r--datarouter-prov/src/test/java/datarouter/provisioning/FillDB.java125
-rw-r--r--datarouter-prov/src/test/java/datarouter/provisioning/package.html29
-rw-r--r--datarouter-prov/src/test/java/datarouter/provisioning/testBase.java158
-rw-r--r--datarouter-prov/src/test/java/datarouter/provisioning/testCleanup.java85
-rw-r--r--datarouter-prov/src/test/java/datarouter/provisioning/testDRFeedsDelete.java59
-rw-r--r--datarouter-prov/src/test/java/datarouter/provisioning/testDRFeedsGet.java188
-rw-r--r--datarouter-prov/src/test/java/datarouter/provisioning/testDRFeedsPost.java282
-rw-r--r--datarouter-prov/src/test/java/datarouter/provisioning/testDRFeedsPut.java59
-rw-r--r--datarouter-prov/src/test/java/datarouter/provisioning/testFeedDelete.java98
-rw-r--r--datarouter-prov/src/test/java/datarouter/provisioning/testFeedPut.java202
-rw-r--r--datarouter-prov/src/test/java/datarouter/provisioning/testInternalGet.java105
-rw-r--r--datarouter-prov/src/test/java/datarouter/provisioning/testInternalMisc.java151
-rw-r--r--datarouter-prov/src/test/java/datarouter/provisioning/testLogGet.java181
-rw-r--r--datarouter-prov/src/test/java/datarouter/provisioning/testPublish.java119
-rw-r--r--datarouter-prov/src/test/java/datarouter/provisioning/testRLEBitSet.java231
-rw-r--r--datarouter-prov/src/test/java/datarouter/provisioning/testRouteAPI.java30
-rw-r--r--datarouter-prov/src/test/java/datarouter/provisioning/testSubscribePost.java180
144 files changed, 28357 insertions, 0 deletions
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/authz/AuthorizationResponse.java b/datarouter-prov/src/main/java/com/att/research/datarouter/authz/AuthorizationResponse.java
new file mode 100644
index 00000000..26956f82
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/authz/AuthorizationResponse.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package com.att.research.datarouter.authz;
+
+import java.util.List;
+
+/**
+ * The <code>AuthorizationResponse</code> interface gives the caller access to information about an authorization
+ * decision. This information includes the permit/deny decision itself, along with supplementary information in the form of
+ * advice and obligations. (The advice and obligations will not be used in Data Router R1.)
+ *
+ * @author J. F. Lucas
+ *
+ */
+public interface AuthorizationResponse {
+ /**
+ * Indicates whether the request is authorized or not.
+ *
+ * @return a boolean flag that is <code>true</code> if the request is permitted, and <code>false</code> otherwise.
+ */
+ public boolean isAuthorized();
+
+ /**
+ * Returns any advice elements that were included in the authorization response.
+ *
+ * @return A list of objects implementing the <code>AuthorizationResponseSupplement</code> interface, with each object representing an
+ * advice element from the authorization response.
+ */
+ public List<AuthorizationResponseSupplement> getAdvice();
+
+ /**
+ * Returns any obligation elements that were included in the authorization response.
+ *
+ * @return A list of objects implementing the <code>AuthorizationResponseSupplement</code> interface, with each object representing an
+ * obligation element from the authorization response.
+ */
+ public List<AuthorizationResponseSupplement> getObligations();
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/authz/AuthorizationResponseSupplement.java b/datarouter-prov/src/main/java/com/att/research/datarouter/authz/AuthorizationResponseSupplement.java
new file mode 100644
index 00000000..2829c507
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/authz/AuthorizationResponseSupplement.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.authz;
+
+import java.util.Map;
+
+/** An object that meets the <code>AuthorizationResponseSupplement</code> interface carries supplementary
+ * information for an authorization response. In a XACML-based system, a response to an authorization request
+ * carries not just the permit/deny decision but, optionally, supplemental information in the form of advice and
+ * obligation elements. The structure of a XACML advice element and a XACML obligation element are similar: each has an identifier and
+ * a set of attributes (name-value) pairs. (The difference between a XACML advice element and a XACML obligation element is in
+ * how the recipient of the response--the Policy Enforcement Point, in XACML terminology--handles the element.)
+ *
+ * @author J. F. Lucas
+ *
+ */
+public interface AuthorizationResponseSupplement {
+ /** Return the identifier for the supplementary information element.
+ *
+ * @return a <code>String</code> containing the identifier.
+ */
+ public String getId();
+
+ /** Return the attributes for the supplementary information element, as a <code>Map</code> in which
+ * keys represent attribute identifiers and values represent attribute values.
+ *
+ * @return attributes for the supplementary information element.
+ */
+ public Map<String, String> getAttributes();
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/authz/Authorizer.java b/datarouter-prov/src/main/java/com/att/research/datarouter/authz/Authorizer.java
new file mode 100644
index 00000000..bfed5c37
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/authz/Authorizer.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.authz;
+
+import java.util.Map;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * A Data Router API that requires authorization of incoming requests creates an instance of a class that implements
+ * the <code>Authorizer</code> interface. The class implements all of the logic necessary to determine if an API
+ * request is permitted. In Data Router R1, the classes that implement the <code>Authorizer</code> interface will have
+ * local logic that makes the authorization decision. After R1, these classes will instead have logic that creates XACML
+ * authorization requests, sends these requests to a Policy Decision Point (PDP), and parses the XACML responses.
+ *
+ * @author J. F. Lucas
+ *
+ */
+public interface Authorizer {
+ /**
+ * Determine if the API request carried in the <code>request</code> parameter is permitted.
+ *
+ * @param request the HTTP request for which an authorization decision is needed
+ * @return an object implementing the <code>AuthorizationResponse</code> interface. This object includes the
+ * permit/deny decision for the request and (after R1) supplemental information related to the response in the form
+ * of advice and obligations.
+ */
+ public AuthorizationResponse decide(HttpServletRequest request);
+
+ /**
+ * Determine if the API request carried in the <code>request</code> parameter, with additional attributes provided in
+ * the <code>additionalAttrs</code> parameter, is permitted.
+ *
+ * @param request the HTTP request for which an authorization decision is needed
+ * @param additionalAttrs additional attributes that the <code>Authorizer</code> can in making an authorization decision
+ * @return an object implementing the <code>AuthorizationResponse</code> interface. This object includes the
+ * permit/deny decision for the request and (after R1) supplemental information related to the response in the form
+ * of advice and obligations.
+ */
+ public AuthorizationResponse decide(HttpServletRequest request, Map<String,String> additionalAttrs);
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/authz/impl/AuthRespImpl.java b/datarouter-prov/src/main/java/com/att/research/datarouter/authz/impl/AuthRespImpl.java
new file mode 100644
index 00000000..db318d39
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/authz/impl/AuthRespImpl.java
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.authz.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.att.research.datarouter.authz.AuthorizationResponse;
+import com.att.research.datarouter.authz.AuthorizationResponseSupplement;
+
+
+/** A representation of an authorization response returned by a XACML Policy Decision Point.
+ * In Data Router R1, advice and obligations are not used.
+ * @author J. F. Lucas
+ *
+ */
+public class AuthRespImpl implements AuthorizationResponse {
+ private boolean authorized;
+ private List<AuthorizationResponseSupplement> advice;
+ private List<AuthorizationResponseSupplement> obligations;
+
+ /** Constructor. This version will not be used in Data Router R1 since we will not have advice and obligations.
+ *
+ * @param authorized flag indicating whether the response carried a permit response (<code>true</code>)
+ * or something else (<code>false</code>).
+ * @param advice list of advice elements returned in the response.
+ * @param obligations list of obligation elements returned in the response.
+ */
+ public AuthRespImpl(boolean authorized, List<AuthorizationResponseSupplement> advice, List<AuthorizationResponseSupplement> obligations) {
+ this.authorized = authorized;
+ this.advice = (advice == null ? null : new ArrayList<AuthorizationResponseSupplement> (advice));
+ this.obligations = (obligations == null ? null : new ArrayList<AuthorizationResponseSupplement> (obligations));
+ }
+
+ /** Constructor. Simple version for authorization responses that have no advice and no obligations.
+ *
+ * @param authorized flag indicating whether the response carried a permit (<code>true</code>) or something else (<code>false</code>).
+ */
+ public AuthRespImpl(boolean authorized) {
+ this(authorized, null, null);
+ }
+
+ /**
+ * Indicates whether the request is authorized or not.
+ *
+ * @return a boolean flag that is <code>true</code> if the request is permitted, and <code>false</code> otherwise.
+ */
+ @Override
+ public boolean isAuthorized() {
+ return authorized;
+ }
+
+ /**
+ * Returns any advice elements that were included in the authorization response.
+ *
+ * @return A list of objects implementing the <code>AuthorizationResponseSupplement</code> interface, with each object representing an
+ * advice element from the authorization response.
+ */
+ @Override
+ public List<AuthorizationResponseSupplement> getAdvice() {
+ return advice;
+ }
+
+ /**
+ * Returns any obligation elements that were included in the authorization response.
+ *
+ * @return A list of objects implementing the <code>AuthorizationResponseSupplement</code> interface, with each object representing an
+ * obligation element from the authorization response.
+ */
+ @Override
+ public List<AuthorizationResponseSupplement> getObligations() {
+ return obligations;
+ }
+
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/authz/impl/AuthRespSupplementImpl.java b/datarouter-prov/src/main/java/com/att/research/datarouter/authz/impl/AuthRespSupplementImpl.java
new file mode 100644
index 00000000..5d2b61c8
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/authz/impl/AuthRespSupplementImpl.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.authz.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.att.research.datarouter.authz.AuthorizationResponseSupplement;
+
+/** Carries supplementary information--an advice or an obligation--from the authorization response returned
+ * by a XACML Policy Decision Point. Not used in Data Router R1.
+ * @author J. F. Lucas
+ *
+ */
+public class AuthRespSupplementImpl implements AuthorizationResponseSupplement {
+
+ private String id = null;
+ private Map<String, String> attributes = null;
+
+ /** Constructor, available within the package.
+ *
+ * @param id The identifier for the advice or obligation element
+ * @param attributes The attributes (name-value pairs) for the advice or obligation element.
+ */
+ AuthRespSupplementImpl (String id, Map<String, String> attributes) {
+ this.id = id;
+ this.attributes = new HashMap<String,String>(attributes);
+ }
+
+ /** Return the identifier for the supplementary information element.
+ *
+ * @return a <code>String</code> containing the identifier.
+ */
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ /** Return the attributes for the supplementary information element, as a <code>Map</code> in which
+ * keys represent attribute identifiers and values represent attribute values.
+ *
+ * @return attributes for the supplementary information element.
+ */
+ @Override
+ public Map<String, String> getAttributes() {
+ return attributes;
+ }
+
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/authz/impl/AuthzResource.java b/datarouter-prov/src/main/java/com/att/research/datarouter/authz/impl/AuthzResource.java
new file mode 100644
index 00000000..1a201b7e
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/authz/impl/AuthzResource.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.authz.impl;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/** Internal representation of an authorization resource (the entity to which access is being requested). Consists
+ * of a type and an identifier. The constructor takes the request URI from an HTTP request and checks it against
+ * patterns for the the different resource types. In DR R1, there are four resource types:
+ * <ul>
+ * <li>the feeds collection resource, the target of POST requests to create a new feed and GET requests to list
+ * the existing feeds. This is the root resource for the DR provisioning system, and it has no explicit id.
+ * </li>
+ * <li>a feed resource, the target of GET, PUT, and DELETE requests used to manage an existing feed. Each feed
+ * has a unique feed ID.
+ * </li>
+ * <li>a subscription collection resource, the target of POST requests to create a new subscription and GET requests
+ * to list the subscriptions for a feed. Each feed has a subscription collection, and the ID associated with a
+ * subscription collection is the ID of the feed.
+ * </li>
+ * <li>a subscription resource, the target of GET, PUT, and DELETE requests used to manage an existing subscription.
+ * Each subscription has a unique subscription ID.
+ * </li>
+ *
+ * @author J. F. Lucas
+ *
+ */
+public class AuthzResource {
+ private ResourceType type = null;
+ private String id = "";
+
+ /* Construct an AuthzResource by matching a request URI against the various patterns */
+ public AuthzResource(String rURI) {
+ if (rURI != null) {
+ for (ResourceType t : ResourceType.values()) {
+ Matcher m = t.getPattern().matcher(rURI);
+ if (m.find(0)) {
+ this.type = t;
+ if (m.group("id") != null) {
+ this.id = m.group("id");
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ public ResourceType getType() {
+ return this.type;
+ }
+
+ public String getId() {
+ return this.id;
+ }
+
+ /* Enumeration that helps turn a request URI into something more useful for
+ * authorization purposes by given a type name and a pattern for determining if the URI
+ * represents that resource type.
+ * Highly dependent on the URL scheme, could be parameterized.
+ */
+ public enum ResourceType {
+ FEEDS_COLLECTION("((://[^/]+/)|(^/))(?<id>)$"),
+ SUBS_COLLECTION ("((://[^/]+/)|(^/{0,1}))subscribe/(?<id>[^/]+)$"),
+ FEED("((://[^/]+/)|(^/{0,1}))feed/(?<id>[^/]+)$"),
+ SUB("((://[^/]+/)|(^/{0,1}))subs/(?<id>[^/]+)$");
+
+ private Pattern uriPattern;
+
+ private ResourceType(String patternString) {
+ this.uriPattern = Pattern.compile(patternString);
+ }
+
+ Pattern getPattern() {
+ return this.uriPattern;
+ }
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/authz/impl/ProvAuthorizer.java b/datarouter-prov/src/main/java/com/att/research/datarouter/authz/impl/ProvAuthorizer.java
new file mode 100644
index 00000000..d6683d5c
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/authz/impl/ProvAuthorizer.java
@@ -0,0 +1,179 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package com.att.research.datarouter.authz.impl;
+
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.log4j.Logger;
+
+import com.att.research.datarouter.authz.AuthorizationResponse;
+import com.att.research.datarouter.authz.Authorizer;
+import com.att.research.datarouter.authz.impl.AuthzResource.ResourceType;
+
+/** Authorizer for the provisioning API for Data Router R1
+ *
+ * @author J. F. Lucas
+ *
+ */
+public class ProvAuthorizer implements Authorizer {
+
+ private Logger log;
+ private ProvDataProvider provData;
+
+ private static final String SUBJECT_HEADER = "X-ATT-DR-ON-BEHALF-OF"; // HTTP header carrying requester identity
+ private static final String SUBJECT_HEADER_GROUP = "X-ATT-DR-ON-BEHALF-OF-GROUP"; // HTTP header carrying requester identity by group Rally : US708115
+ /** Constructor. For the moment, do nothing special. Make it a singleton?
+ *
+ */
+ public ProvAuthorizer(ProvDataProvider provData) {
+ this.provData = provData;
+ this.log = Logger.getLogger(this.getClass());
+ }
+
+ /**
+ * Determine if the API request carried in the <code>request</code> parameter is permitted.
+ *
+ * @param request the HTTP request for which an authorization decision is needed
+ * @return an object implementing the <code>AuthorizationResponse</code> interface. This object includes the
+ * permit/deny decision for the request and (after R1) supplemental information related to the response in the form
+ * of advice and obligations.
+ */
+ @Override
+ public AuthorizationResponse decide(HttpServletRequest request) {
+ return this.decide(request, null);
+ }
+
+ /**
+ * Determine if the API request carried in the <code>request</code> parameter, with additional attributes provided in
+ * the <code>additionalAttrs</code> parameter, is permitted. <code>additionalAttrs</code> isn't used in R1.
+ *
+ * @param request the HTTP request for which an authorization decision is needed
+ * @param additionalAttrs additional attributes that the <code>Authorizer</code> can in making an authorization decision
+ * @return an object implementing the <code>AuthorizationResponse</code> interface. This object includes the
+ * permit/deny decision for the request and (after R1) supplemental information related to the response in the form
+ * of advice and obligations.
+ */
+ @Override
+ public AuthorizationResponse decide(HttpServletRequest request,
+ Map<String, String> additionalAttrs) {
+ log.trace ("Entering decide()");
+
+ boolean decision = false;
+
+ // Extract interesting parts of the HTTP request
+ String method = request.getMethod();
+ AuthzResource resource = new AuthzResource(request.getRequestURI());
+ String subject = (request.getHeader(SUBJECT_HEADER)); // identity of the requester
+ String subjectgroup = (request.getHeader(SUBJECT_HEADER_GROUP)); // identity of the requester by group Rally : US708115
+
+ log.trace("Method: " + method + " -- Type: " + resource.getType() + " -- Id: " + resource.getId() +
+ " -- Subject: " + subject);
+
+ // Choose authorization method based on the resource type
+ ResourceType resourceType = resource.getType();
+ if (resourceType != null) {
+
+ switch (resourceType) {
+
+ case FEEDS_COLLECTION:
+ decision = allowFeedsCollectionAccess(resource, method, subject, subjectgroup);
+ break;
+
+ case SUBS_COLLECTION:
+ decision = allowSubsCollectionAccess(resource, method, subject, subjectgroup);
+ break;
+
+ case FEED:
+ decision = allowFeedAccess(resource, method, subject, subjectgroup);
+ break;
+
+ case SUB:
+ decision = allowSubAccess(resource, method, subject, subjectgroup);
+ break;
+
+ default:
+ decision = false;
+ break;
+ }
+ }
+ log.debug("Exit decide(): " + method + "|" + resourceType + "|" + resource.getId() + "|" + subject + " ==> " + decision);
+
+ return new AuthRespImpl(decision);
+ }
+
+ private boolean allowFeedsCollectionAccess(AuthzResource resource, String method, String subject, String subjectgroup) {
+
+ // Allow GET or POST unconditionally
+ return method != null && (method.equalsIgnoreCase("GET") || method.equalsIgnoreCase("POST"));
+ }
+
+ private boolean allowSubsCollectionAccess(AuthzResource resource, String method, String subject, String subjectgroup) {
+
+ // Allow GET or POST unconditionally
+ return method != null && (method.equalsIgnoreCase("GET") || method.equalsIgnoreCase("POST"));
+ }
+
+ private boolean allowFeedAccess(AuthzResource resource, String method, String subject, String subjectgroup) {
+ boolean decision = false;
+
+ // Allow GET, PUT, or DELETE if requester (subject) is the owner (publisher) of the feed
+ if ( method != null && (method.equalsIgnoreCase("GET") || method.equalsIgnoreCase("PUT") ||
+ method.equalsIgnoreCase("DELETE"))) {
+
+ String owner = provData.getFeedOwner(resource.getId());
+ decision = (owner != null) && owner.equals(subject);
+
+ //Verifying by group Rally : US708115
+ if(subjectgroup != null) {
+ String feedowner = provData.getGroupByFeedGroupId(subject, resource.getId());
+ decision = (feedowner != null) && feedowner.equals(subjectgroup);
+ }
+ }
+
+ return decision;
+ }
+
+ private boolean allowSubAccess(AuthzResource resource, String method, String subject, String subjectgroup) {
+ boolean decision = false;
+
+ // Allow GET, PUT, or DELETE if requester (subject) is the owner of the subscription (subscriber)
+ if (method != null && (method.equalsIgnoreCase("GET") || method.equalsIgnoreCase("PUT") ||
+ method.equalsIgnoreCase("DELETE") || method.equalsIgnoreCase("POST"))) {
+
+ String owner = provData.getSubscriptionOwner(resource.getId());
+ decision = (owner != null) && owner.equals(subject);
+
+ //Verifying by group Rally : US708115
+ if(subjectgroup != null) {
+ String feedowner = provData.getGroupBySubGroupId(subject, resource.getId());
+ decision = (feedowner != null) && feedowner.equals(subjectgroup);
+ }
+ }
+
+ return decision;
+ }
+
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/authz/impl/ProvDataProvider.java b/datarouter-prov/src/main/java/com/att/research/datarouter/authz/impl/ProvDataProvider.java
new file mode 100644
index 00000000..76ae0344
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/authz/impl/ProvDataProvider.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package com.att.research.datarouter.authz.impl;
+
+/** Interface to access data about subscriptions and feeds. A software component that
+ * uses the <code>ProvAuthorizer</code> needs to supply an implementation of this interface.
+ * @author J. F. Lucas
+ *
+ */
+public interface ProvDataProvider {
+
+ /** Get the identity of the owner of a feed.
+ *
+ * @param feedId the feed ID of the feed whose owner is being looked up.
+ * @return the feed owner's identity
+ */
+ public String getFeedOwner(String feedId);
+
+ /** Get the security classification of a feed.
+ *
+ * @param feedId the ID of the feed whose classification is being looked up.
+ * @return the classification of the feed.
+ */
+ public String getFeedClassification(String feedId);
+
+ /** Get the identity of the owner of a feed
+ *
+ * @param subId the ID of the subscripition whose owner is being looked up.
+ * @return the subscription owner's identity.
+ */
+ public String getSubscriptionOwner(String subId);
+
+ /** Get the identity of the owner of a feed by group id - Rally : US708115
+ *
+ * @param feedid, user the ID of the feed whose owner is being looked up.
+ * @return the feed owner's identity by group.
+ */
+ public String getGroupByFeedGroupId(String owner, String feedId);
+
+ /** Get the identity of the owner of a sub by group id Rally : US708115
+ *
+ * @param subid, user the ID of the feed whose owner is being looked up.
+ * @return the feed owner's identity by group.
+ */
+ public String getGroupBySubGroupId(String owner, String subId);
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/authz/impl/package.html b/datarouter-prov/src/main/java/com/att/research/datarouter/authz/impl/package.html
new file mode 100644
index 00000000..fae27ee0
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/authz/impl/package.html
@@ -0,0 +1,68 @@
+#-------------------------------------------------------------------------------
+# ============LICENSE_START==================================================
+# * org.onap.dmaap
+# * ===========================================================================
+# * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+# * ===========================================================================
+# * 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.
+# * ============LICENSE_END====================================================
+# *
+# * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+# *
+#-------------------------------------------------------------------------------
+
+<html>
+<head>
+</head>
+<body>
+<p>
+This package provides an implementation of the authorization-related interfaces
+defined by the <code>com.att.research.datarouter.authz</code> package, intended for
+use with the provisioning server for Data Router Release 1. In DR R1, we do not
+have an external policy engine, so this implementation performs the authorization
+locally.
+</p>
+<p>
+In order to perform the authorization, this package needs access to provisioning data
+about feeds and subscriptions. This package defines an interface
+(<code>com.att.research.datarouter.authz.impl.ProvDataProvider</code>) through which it
+expects to get this data. The provisioning server code must provide an implementation
+of this interface.
+</p>
+<p>
+A software component that wishes to use this implementation must:
+<ul>
+<li>Provide an implementation of the
+<code>com.att.research.datarouter.authz.impl.ProvDataProvider</code>
+interface.
+</li>
+<li>
+Create an instance of the <code>ProvDataProvider</code> implementation.
+<li>
+Create an instance of the
+<code>com.att.research.datarouter.authz.impl.ProvAuthorizer</code>
+class defined in this package, passing it an instance of the <code>ProvDataProvider</code>
+implementation.
+</li>
+</ul>
+</p>
+<p>
+Example:
+<pre>
+<code>
+ProvDataProvider dataProv = new MyDataProvider();
+Authorizer authz = new ProvAuthorizer(dataProv);
+</code>
+</pre>
+</body>
+</html>
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/authz/package.html b/datarouter-prov/src/main/java/com/att/research/datarouter/authz/package.html
new file mode 100644
index 00000000..7628ae82
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/authz/package.html
@@ -0,0 +1,38 @@
+#-------------------------------------------------------------------------------
+# ============LICENSE_START==================================================
+# * org.onap.dmaap
+# * ===========================================================================
+# * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+# * ===========================================================================
+# * 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.
+# * ============LICENSE_END====================================================
+# *
+# * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+# *
+#-------------------------------------------------------------------------------
+
+<html>
+<head>
+</head>
+<body>
+<p>
+This package defines an interface that can be used by servlet-based HTTP APIs to
+make authorization requests and receive authorization responses from an external
+authorization entity such as a XACML Policy Decision Point (PDP).
+</p>
+<p>
+In Data Router Release 1, there is no external authorization system. The provisioning server
+will use an implementation of this interface for local authorization of provisioning requests.
+</p>
+</body>
+</html>
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/BaseServlet.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/BaseServlet.java
new file mode 100644
index 00000000..a4784936
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/BaseServlet.java
@@ -0,0 +1,869 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning;
+
+import static com.att.eelf.configuration.Configuration.MDC_SERVER_FQDN;
+
+import static com.att.eelf.configuration.Configuration.MDC_SERVER_IP_ADDRESS;
+import static com.att.eelf.configuration.Configuration.MDC_SERVICE_NAME;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.security.cert.X509Certificate;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.List;
+import java.util.ArrayList;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.log4j.Logger;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+import org.json.JSONException;
+import org.slf4j.MDC;
+
+import com.att.research.datarouter.authz.Authorizer;
+import com.att.research.datarouter.authz.impl.ProvAuthorizer;
+import com.att.research.datarouter.authz.impl.ProvDataProvider;
+import com.att.research.datarouter.provisioning.beans.Deleteable;
+import com.att.research.datarouter.provisioning.beans.Feed;
+import com.att.research.datarouter.provisioning.beans.Insertable;
+import com.att.research.datarouter.provisioning.beans.NodeClass;
+import com.att.research.datarouter.provisioning.beans.Parameters;
+import com.att.research.datarouter.provisioning.beans.Subscription;
+import com.att.research.datarouter.provisioning.beans.Updateable;
+import com.att.research.datarouter.provisioning.utils.DB;
+import com.att.research.datarouter.provisioning.utils.ThrottleFilter;
+import com.att.research.datarouter.provisioning.beans.Group; //Groups feature Rally:US708115 - 1610
+
+import java.util.Properties;
+import java.util.regex.Pattern;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.Multipart;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.AddressException;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+/**
+ * This is the base class for all Servlets in the provisioning code.
+ * It provides standard constants and some common methods.
+ *
+ * @author Robert Eby
+ * @version $Id: BaseServlet.java,v 1.16 2014/03/12 19:45:40 eby Exp $
+ */
+@SuppressWarnings("serial")
+public class BaseServlet extends HttpServlet implements ProvDataProvider {
+ public static final String BEHALF_HEADER = "X-ATT-DR-ON-BEHALF-OF";
+ public static final String FEED_BASECONTENT_TYPE = "application/vnd.att-dr.feed";
+ public static final String FEED_CONTENT_TYPE = "application/vnd.att-dr.feed; version=2.0";
+ public static final String FEEDFULL_CONTENT_TYPE = "application/vnd.att-dr.feed-full; version=2.0";
+ public static final String FEEDLIST_CONTENT_TYPE = "application/vnd.att-dr.feed-list; version=1.0";
+ public static final String SUB_BASECONTENT_TYPE = "application/vnd.att-dr.subscription";
+ public static final String SUB_CONTENT_TYPE = "application/vnd.att-dr.subscription; version=2.0";
+ public static final String SUBFULL_CONTENT_TYPE = "application/vnd.att-dr.subscription-full; version=2.0";
+ public static final String SUBLIST_CONTENT_TYPE = "application/vnd.att-dr.subscription-list; version=1.0";
+
+
+ //Adding groups functionality, ...1610
+ public static final String GROUP_BASECONTENT_TYPE = "application/vnd.att-dr.group";
+ public static final String GROUP_CONTENT_TYPE = "application/vnd.att-dr.group; version=2.0";
+ public static final String GROUPFULL_CONTENT_TYPE = "application/vnd.att-dr.group-full; version=2.0";
+ public static final String GROUPLIST_CONTENT_TYPE = "application/vnd.att-dr.fegrouped-list; version=1.0";
+
+
+ public static final String LOGLIST_CONTENT_TYPE = "application/vnd.att-dr.log-list; version=1.0";
+ public static final String PROVFULL_CONTENT_TYPE1 = "application/vnd.att-dr.provfeed-full; version=1.0";
+ public static final String PROVFULL_CONTENT_TYPE2 = "application/vnd.att-dr.provfeed-full; version=2.0";
+ public static final String CERT_ATTRIBUTE = "javax.servlet.request.X509Certificate";
+
+ public static final String DB_PROBLEM_MSG = "There has been a problem with the DB. It is suggested you try the operation again.";
+
+ public static final int DEFAULT_MAX_FEEDS = 10000;
+ public static final int DEFAULT_MAX_SUBS = 100000;
+ public static final int DEFAULT_POKETIMER1 = 5;
+ public static final int DEFAULT_POKETIMER2 = 30;
+ public static final String DEFAULT_DOMAIN = "web.att.com";
+ public static final String DEFAULT_PROVSRVR_NAME = "feeds-drtr.web.att.com";
+ public static final String RESEARCH_SUBNET = "135.207.136.128/25";
+ public static final String STATIC_ROUTING_NODES = ""; //Adding new param for static Routing - Rally:US664862-1610
+
+ /** A boolean to trigger one time "provisioning changed" event on startup */
+ private static boolean startmsg_flag = true;
+ /** This POD should require SSL connections from clients; pulled from the DB (PROV_REQUIRE_SECURE) */
+ private static boolean require_secure = true;
+ /** This POD should require signed, recognized certificates from clients; pulled from the DB (PROV_REQUIRE_CERT) */
+ private static boolean require_cert = true;
+ /** The set of authorized addresses and networks; pulled from the DB (PROV_AUTH_ADDRESSES) */
+ private static Set<String> authorizedAddressesAndNetworks = new HashSet<String>();
+ /** The set of authorized names; pulled from the DB (PROV_AUTH_SUBJECTS) */
+ private static Set<String> authorizedNames = new HashSet<String>();
+ /** The FQDN of the initially "active" provisioning server in this Data Router ecosystem */
+ private static String initial_active_pod;
+ /** The FQDN of the initially "standby" provisioning server in this Data Router ecosystem */
+ private static String initial_standby_pod;
+ /** The FQDN of this provisioning server in this Data Router ecosystem */
+ private static String this_pod;
+ /** "Timer 1" - used to determine when to notify nodes of provisioning changes */
+ private static long poke_timer1;
+ /** "Timer 2" - used to determine when to notify nodes of provisioning changes */
+ private static long poke_timer2;
+ /** Array of nodes names and/or FQDNs */
+ private static String[] nodes = new String[0];
+ /** Array of node IP addresses */
+ private static InetAddress[] nodeAddresses = new InetAddress[0];
+ /** Array of POD IP addresses */
+ private static InetAddress[] podAddresses = new InetAddress[0];
+ /** The maximum number of feeds allowed; pulled from the DB (PROV_MAXFEED_COUNT) */
+ protected static int max_feeds = 0;
+ /** The maximum number of subscriptions allowed; pulled from the DB (PROV_MAXSUB_COUNT) */
+ protected static int max_subs = 0;
+ /** The current number of feeds in the system */
+ protected static int active_feeds = 0;
+ /** The current number of subscriptions in the system */
+ protected static int active_subs = 0;
+ /** The domain used to generate a FQDN from the "bare" node names */
+ public static String prov_domain = "web.att.com";
+ /** The standard FQDN of the provisioning server in this Data Router ecosystem */
+ public static String prov_name = "feeds-drtr.web.att.com";
+ /** The standard FQDN of the ACTIVE provisioning server in this Data Router ecosystem */
+ public static String active_prov_name = "feeds-drtr.web.att.com";
+ /** Special subnet that is allowed access to /internal */
+ protected static String special_subnet = RESEARCH_SUBNET;
+
+ /** Special subnet that is allowed access to /internal to Lab Machine */
+ protected static String special_subnet_secondary = RESEARCH_SUBNET;
+ protected static String static_routing_nodes = STATIC_ROUTING_NODES; //Adding new param for static Routing - Rally:US664862-1610
+
+ /** This logger is used to log provisioning events */
+ protected static Logger eventlogger;
+ /** This logger is used to log internal events (errors, etc.) */
+ protected static Logger intlogger;
+ /** Authorizer - interface to the Policy Engine */
+ protected static Authorizer authz;
+ /** The Synchronizer used to sync active DB to standby one */
+ protected static SynchronizerTask synctask = null;
+
+ //Data Router Subscriber HTTPS Relaxation feature USERSTORYID:US674047.
+ private InetAddress thishost;
+ private InetAddress loopback;
+ private static Boolean mailSendFlag = false;
+
+ public static final String MAILCONFIG_FILE = "mail.properties";
+ private static Properties mailprops;
+ /**
+ * Initialize data common to all the provisioning server servlets.
+ */
+ protected BaseServlet() {
+ if (eventlogger == null)
+ eventlogger = Logger.getLogger("com.att.research.datarouter.provisioning.events");
+ if (intlogger == null)
+ intlogger = Logger.getLogger("com.att.research.datarouter.provisioning.internal");
+ if (authz == null)
+ authz = new ProvAuthorizer(this);
+ if (startmsg_flag) {
+ startmsg_flag = false;
+ provisioningParametersChanged();
+ }
+ if (synctask == null) {
+ synctask = SynchronizerTask.getSynchronizer();
+ }
+ String name = this.getClass().getName();
+ intlogger.info("PROV0002 Servlet "+name+" started.");
+ }
+ @Override
+ public void init(ServletConfig config) throws ServletException {
+ super.init(config);
+ try {
+ thishost = InetAddress.getLocalHost();
+ loopback = InetAddress.getLoopbackAddress();
+ checkHttpsRelaxation(); //Data Router Subscriber HTTPS Relaxation feature USERSTORYID:US674047.
+ } catch (UnknownHostException e) {
+ // ignore
+ }
+ }
+ protected int getIdFromPath(HttpServletRequest req) {
+ String path = req.getPathInfo();
+ if (path == null || path.length() < 2)
+ return -1;
+ try {
+ return Integer.parseInt(path.substring(1));
+ } catch (NumberFormatException e) {
+ return -1;
+ }
+ }
+ /**
+ * Read the request's input stream and return a JSONObject from it
+ * @param req the HTTP request
+ * @return the JSONObject, or null if the stream cannot be parsed
+ */
+ protected JSONObject getJSONfromInput(HttpServletRequest req) {
+ JSONObject jo = null;
+ try {
+ jo = new JSONObject(new JSONTokener(req.getInputStream()));
+ if (intlogger.isDebugEnabled())
+ intlogger.debug("JSON: "+jo.toString());
+ } catch (Exception e) {
+ intlogger.info("Error reading JSON: "+e);
+ }
+ return jo;
+ }
+ /**
+ * Check if the remote host is authorized to perform provisioning.
+ * Is the request secure?
+ * Is it coming from an authorized IP address or network (configured via PROV_AUTH_ADDRESSES)?
+ * Does it have a valid client certificate (configured via PROV_AUTH_SUBJECTS)?
+ * @param request the request
+ * @return an error string, or null if all is OK
+ */
+ protected String isAuthorizedForProvisioning(HttpServletRequest request) {
+ // Is the request https?
+ if (require_secure && !request.isSecure()) {
+ return "Request must be made over an HTTPS connection.";
+ }
+
+ // Is remote IP authorized?
+ String remote = request.getRemoteAddr();
+ try {
+ boolean found = false;
+ InetAddress ip = InetAddress.getByName(remote);
+ for (String addrnet : authorizedAddressesAndNetworks) {
+ found |= addressMatchesNetwork(ip, addrnet);
+ }
+ if (!found) {
+ return "Unauthorized address: "+remote;
+ }
+ } catch (UnknownHostException e) {
+ return "Unauthorized address: "+remote;
+ }
+
+ // Does remote have a valid certificate?
+ if (require_cert) {
+ X509Certificate certs[] = (X509Certificate[]) request.getAttribute(CERT_ATTRIBUTE);
+ if (certs == null || certs.length == 0) {
+ return "Client certificate is missing.";
+ }
+ // cert[0] is the client cert
+ // see http://www.proto.research.att.com/java/java7/api/javax/net/ssl/SSLSession.html#getPeerCertificates()
+ String name = certs[0].getSubjectX500Principal().getName();
+ if (!authorizedNames.contains(name)) {
+ return "No authorized certificate found.";
+ }
+ }
+
+ // No problems!
+ return null;
+ }
+ /**
+ * Check if the remote IP address is authorized to see the /internal URL tree.
+ * @param request the HTTP request
+ * @return true iff authorized
+ */
+ protected boolean isAuthorizedForInternal(HttpServletRequest request) {
+ try {
+ InetAddress ip = InetAddress.getByName(request.getRemoteAddr());
+ for (InetAddress node : getNodeAddresses()) {
+ if (node != null && ip.equals(node))
+ return true;
+ }
+ for (InetAddress pod : getPodAddresses()) {
+ if (pod != null && ip.equals(pod))
+ return true;
+ }
+ if (thishost != null && ip.equals(thishost))
+ return true;
+ if (loopback != null && ip.equals(loopback))
+ return true;
+ // Also allow the "special subnet" access
+ if (addressMatchesNetwork(ip, special_subnet_secondary))
+ return true;
+ if (addressMatchesNetwork(ip, special_subnet))
+ return true;
+ } catch (UnknownHostException e) {
+ // ignore
+ }
+ return false;
+ }
+ /**
+ * Check if an IP address matches a network address.
+ * @param ip the IP address
+ * @param s the network address; a bare IP address may be matched also
+ * @return true if they intersect
+ */
+ protected static boolean addressMatchesNetwork(InetAddress ip, String s) {
+ int mlen = -1;
+ int n = s.indexOf("/");
+ if (n >= 0) {
+ mlen = Integer.parseInt(s.substring(n+1));
+ s = s.substring(0, n);
+ }
+ try {
+ InetAddress i2 = InetAddress.getByName(s);
+ byte[] b1 = ip.getAddress();
+ byte[] b2 = i2.getAddress();
+ if (b1.length != b2.length)
+ return false;
+ if (mlen > 0) {
+ byte[] masks = {
+ (byte)0x00, (byte)0x80, (byte)0xC0, (byte)0xE0,
+ (byte)0xF0, (byte)0xF8, (byte)0xFC, (byte)0xFE
+ };
+ byte mask = masks[mlen%8];
+ for (n = mlen/8; n < b1.length; n++) {
+ b1[n] &= mask;
+ b2[n] &= mask;
+ mask = 0;
+ }
+ }
+ for (n = 0; n < b1.length; n++)
+ if (b1[n] != b2[n])
+ return false;
+ } catch (UnknownHostException e) {
+ return false;
+ }
+ return true;
+ }
+ /**
+ * Something has changed in the provisioning data.
+ * Start the timers that will cause the pre-packaged JSON string to be regenerated,
+ * and cause nodes and the other provisioning server to be notified.
+ */
+ public static void provisioningDataChanged() {
+ long now = System.currentTimeMillis();
+ Poker p = Poker.getPoker();
+ p.setTimers(now + (poke_timer1 * 1000L), now + (poke_timer2 * 1000L));
+ }
+ /**
+ * Something in the parameters has changed, reload all parameters from the DB.
+ */
+ public static void provisioningParametersChanged() {
+ Map<String,String> map = Parameters.getParameters();
+ require_secure = getBoolean(map, Parameters.PROV_REQUIRE_SECURE);
+ require_cert = getBoolean(map, Parameters.PROV_REQUIRE_CERT);
+ authorizedAddressesAndNetworks = getSet(map, Parameters.PROV_AUTH_ADDRESSES);
+ authorizedNames = getSet (map, Parameters.PROV_AUTH_SUBJECTS);
+ nodes = getSet (map, Parameters.NODES).toArray(new String[0]);
+ max_feeds = getInt (map, Parameters.PROV_MAXFEED_COUNT, DEFAULT_MAX_FEEDS);
+ max_subs = getInt (map, Parameters.PROV_MAXSUB_COUNT, DEFAULT_MAX_SUBS);
+ poke_timer1 = getInt (map, Parameters.PROV_POKETIMER1, DEFAULT_POKETIMER1);
+ poke_timer2 = getInt (map, Parameters.PROV_POKETIMER2, DEFAULT_POKETIMER2);
+ prov_domain = getString (map, Parameters.PROV_DOMAIN, DEFAULT_DOMAIN);
+ prov_name = getString (map, Parameters.PROV_NAME, DEFAULT_PROVSRVR_NAME);
+ active_prov_name = getString (map, Parameters.PROV_ACTIVE_NAME, prov_name);
+ special_subnet = getString (map, Parameters.PROV_SPECIAL_SUBNET, RESEARCH_SUBNET);
+ static_routing_nodes = getString (map, Parameters.STATIC_ROUTING_NODES, ""); //Adding new param for static Routing - Rally:US664862-1610
+ initial_active_pod = getString (map, Parameters.ACTIVE_POD, "");
+ initial_standby_pod = getString (map, Parameters.STANDBY_POD, "");
+ static_routing_nodes = getString (map, Parameters.STATIC_ROUTING_NODES, ""); //Adding new param for static Routing - Rally:US664862-1610
+ active_feeds = Feed.countActiveFeeds();
+ active_subs = Subscription.countActiveSubscriptions();
+ try {
+ this_pod = InetAddress.getLocalHost().getHostName();
+ } catch (UnknownHostException e) {
+ this_pod = "";
+ intlogger.warn("PROV0014 Cannot determine the name of this provisioning server.");
+ }
+
+ // Normalize the nodes, and fill in nodeAddresses
+ InetAddress[] na = new InetAddress[nodes.length];
+ for (int i = 0; i < nodes.length; i++) {
+ if (nodes[i].indexOf('.') < 0)
+ nodes[i] += "." + prov_domain;
+ try {
+ na[i] = InetAddress.getByName(nodes[i]);
+ intlogger.debug("PROV0003 DNS lookup: "+nodes[i]+" => "+na[i].toString());
+ } catch (UnknownHostException e) {
+ na[i] = null;
+ intlogger.warn("PROV0004 Cannot lookup "+nodes[i]+": "+e);
+ }
+ }
+
+ //Reset Nodes arr after - removing static routing Nodes, Rally Userstory - US664862 .
+ List<String> filterNodes = new ArrayList<>();
+ for (int i = 0; i < nodes.length; i++) {
+ if(!static_routing_nodes.contains(nodes[i])){
+ filterNodes.add(nodes[i]);
+ }
+ }
+ String [] filteredNodes = filterNodes.toArray(new String[filterNodes.size()]);
+ nodes = filteredNodes;
+
+ nodeAddresses = na;
+ NodeClass.setNodes(nodes); // update NODES table
+
+ // Normalize the PODs, and fill in podAddresses
+ String[] pods = getPods();
+ na = new InetAddress[pods.length];
+ for (int i = 0; i < pods.length; i++) {
+ if (pods[i].indexOf('.') < 0)
+ pods[i] += "." + prov_domain;
+ try {
+ na[i] = InetAddress.getByName(pods[i]);
+ intlogger.debug("PROV0003 DNS lookup: "+pods[i]+" => "+na[i].toString());
+ } catch (UnknownHostException e) {
+ na[i] = null;
+ intlogger.warn("PROV0004 Cannot lookup "+pods[i]+": "+e);
+ }
+ }
+ podAddresses = na;
+
+ // Update ThrottleFilter
+ ThrottleFilter.configure();
+
+ // Check if we are active or standby POD
+ if (!isInitialActivePOD() && !isInitialStandbyPOD())
+ intlogger.warn("PROV0015 This machine is neither the active nor the standby POD.");
+ }
+
+
+ /**Data Router Subscriber HTTPS Relaxation feature USERSTORYID:US674047.
+ * Load mail properties.
+ * @author vs215k
+ *
+ **/
+ private void loadMailProperties() {
+ if (mailprops == null) {
+ mailprops = new Properties();
+ InputStream inStream = getClass().getClassLoader().getResourceAsStream(MAILCONFIG_FILE);
+ try {
+ mailprops.load(inStream);
+ } catch (IOException e) {
+ intlogger.fatal("PROV9003 Opening properties: "+e.getMessage());
+ e.printStackTrace();
+ System.exit(1);
+ }
+ finally {
+ try {
+ inStream.close();
+ }
+ catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ /**Data Router Subscriber HTTPS Relaxation feature USERSTORYID:US674047.
+ * Check if HTTPS Relexaction is enabled
+ * @author vs215k
+ *
+ **/
+ private void checkHttpsRelaxation() {
+ if(mailSendFlag == false) {
+ Properties p = (new DB()).getProperties();
+ intlogger.info("HTTPS relaxatio: "+p.get("com.att.research.datarouter.provserver.https.relaxation"));
+
+ if(p.get("com.att.research.datarouter.provserver.https.relaxation").equals("true")) {
+ try {
+ notifyPSTeam(p.get("com.att.research.datarouter.provserver.https.relax.notify").toString());
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ mailSendFlag = true;
+ }
+ }
+
+ /**Data Router Subscriber HTTPS Relaxation feature USERSTORYID:US674047.
+ * @author vs215k
+ * @param email - list of email ids to notify if HTTP relexcation is enabled.
+ **/
+ private void notifyPSTeam(String email) throws Exception {
+ loadMailProperties(); //Load HTTPS Relex mail properties.
+ String[] emails = email.split(Pattern.quote("|"));
+
+ Properties mailproperties = new Properties();
+ mailproperties.put("mail.smtp.host", mailprops.get("com.att.dmaap.datarouter.mail.server"));
+ mailproperties.put("mail.transport.protocol", mailprops.get("com.att.dmaap.datarouter.mail.protocol"));
+
+ Session session = Session.getDefaultInstance(mailproperties, null);
+ Multipart mp = new MimeMultipart();
+ MimeBodyPart htmlPart = new MimeBodyPart();
+
+ try {
+
+ Message msg = new MimeMessage(session);
+ msg.setFrom(new InternetAddress(mailprops.get("com.att.dmaap.datarouter.mail.from").toString()));
+
+ InternetAddress[] addressTo = new InternetAddress[emails.length];
+ for ( int x =0 ; x < emails.length; x++) {
+ addressTo[x] = new InternetAddress(emails[x]);
+ }
+
+ msg.addRecipients(Message.RecipientType.TO, addressTo);
+ msg.setSubject(mailprops.get("com.att.dmaap.datarouter.mail.subject").toString());
+ htmlPart.setContent(mailprops.get("com.att.dmaap.datarouter.mail.body").toString().replace("[SERVER]", InetAddress.getLocalHost().getHostName()), "text/html");
+ mp.addBodyPart(htmlPart);
+ msg.setContent(mp);
+
+ System.out.println(mailprops.get("com.att.dmaap.datarouter.mail.body").toString().replace("[SERVER]", InetAddress.getLocalHost().getHostName()));
+
+ Transport.send(msg);
+ intlogger.info("HTTPS relaxation mail is sent to - : "+email);
+
+ } catch (AddressException e) {
+ intlogger.error("Invalid email address, unable to send https relaxation mail to - : "+email);
+ } catch (MessagingException e) {
+ intlogger.error("Invalid email address, unable to send https relaxation mail to - : "+email);
+ }
+ }
+
+
+ /**
+ * Get an array of all node names in the DR network.
+ * @return an array of Strings
+ */
+ public static String[] getNodes() {
+ return nodes;
+ }
+ /**
+ * Get an array of all node InetAddresses in the DR network.
+ * @return an array of InetAddresses
+ */
+ public static InetAddress[] getNodeAddresses() {
+ return nodeAddresses;
+ }
+ /**
+ * Get an array of all POD names in the DR network.
+ * @return an array of Strings
+ */
+ public static String[] getPods() {
+ return new String[] { initial_active_pod, initial_standby_pod };
+ }
+ /**
+ * Get an array of all POD InetAddresses in the DR network.
+ * @return an array of InetAddresses
+ */
+ public static InetAddress[] getPodAddresses() {
+ return podAddresses;
+ }
+ /**
+ * Gets the FQDN of the initially ACTIVE provisioning server (POD).
+ * Note: this used to be called isActivePOD(), however, that is a misnomer, as the active status
+ * could shift to the standby POD without these parameters changing. Hence, the function names
+ * have been changed to more accurately reflect their purpose.
+ * @return the FQDN
+ */
+ public static boolean isInitialActivePOD() {
+ return this_pod.equals(initial_active_pod);
+ }
+ /**
+ * Gets the FQDN of the initially STANDBY provisioning server (POD).
+ * Note: this used to be called isStandbyPOD(), however, that is a misnomer, as the standby status
+ * could shift to the active POD without these parameters changing. Hence, the function names
+ * have been changed to more accurately reflect their purpose.
+ * @return the FQDN
+ */
+ public static boolean isInitialStandbyPOD() {
+ return this_pod.equals(initial_standby_pod);
+ }
+ /**
+ * INSERT an {@link Insertable} bean into the database.
+ * @param bean the bean representing a row to insert
+ * @return true if the INSERT was successful
+ */
+ protected boolean doInsert(Insertable bean) {
+ boolean rv = false;
+ DB db = new DB();
+ Connection conn = null;
+ try {
+ conn = db.getConnection();
+ rv = bean.doInsert(conn);
+ } catch (SQLException e) {
+ rv = false;
+ intlogger.warn("PROV0005 doInsert: "+e.getMessage());
+ e.printStackTrace();
+ } finally {
+ if (conn != null)
+ db.release(conn);
+ }
+ return rv;
+ }
+ /**
+ * UPDATE an {@link Updateable} bean in the database.
+ * @param bean the bean representing a row to update
+ * @return true if the UPDATE was successful
+ */
+ protected boolean doUpdate(Updateable bean) {
+ boolean rv = false;
+ DB db = new DB();
+ Connection conn = null;
+ try {
+ conn = db.getConnection();
+ rv = bean.doUpdate(conn);
+ } catch (SQLException e) {
+ rv = false;
+ intlogger.warn("PROV0006 doUpdate: "+e.getMessage());
+ e.printStackTrace();
+ } finally {
+ if (conn != null)
+ db.release(conn);
+ }
+ return rv;
+ }
+ /**
+ * DELETE an {@link Deleteable} bean from the database.
+ * @param bean the bean representing a row to delete
+ * @return true if the DELETE was successful
+ */
+ protected boolean doDelete(Deleteable bean) {
+ boolean rv = false;
+ DB db = new DB();
+ Connection conn = null;
+ try {
+ conn = db.getConnection();
+ rv = bean.doDelete(conn);
+ } catch (SQLException e) {
+ rv = false;
+ intlogger.warn("PROV0007 doDelete: "+e.getMessage());
+ e.printStackTrace();
+ } finally {
+ if (conn != null)
+ db.release(conn);
+ }
+ return rv;
+ }
+ private static boolean getBoolean(Map<String,String> map, String name) {
+ String s = map.get(name);
+ return (s != null) && s.equalsIgnoreCase("true");
+ }
+ private static String getString(Map<String,String> map, String name, String dflt) {
+ String s = map.get(name);
+ return (s != null) ? s : dflt;
+ }
+ private static int getInt(Map<String,String> map, String name, int dflt) {
+ try {
+ String s = map.get(name);
+ return Integer.parseInt(s);
+ } catch (NumberFormatException e) {
+ return dflt;
+ }
+ }
+ private static Set<String> getSet(Map<String,String> map, String name) {
+ Set<String> set = new HashSet<String>();
+ String s = map.get(name);
+ if (s != null) {
+ String[] pp = s.split("\\|");
+ if (pp != null) {
+ for (String t : pp) {
+ String t2 = t.trim();
+ if (t2.length() > 0)
+ set.add(t2);
+ }
+ }
+ }
+ return set;
+ }
+
+ /**
+ * A class used to encapsulate a Content-type header, separating out the "version" attribute
+ * (which defaults to "1.0" if missing).
+ */
+ public class ContentHeader {
+ private String type = "";
+ private Map<String, String> map = new HashMap<String, String>();
+ public ContentHeader() {
+ this("", "1.0");
+ }
+ public ContentHeader(String t, String v) {
+ type = t.trim();
+ map.put("version", v);
+ }
+ public String getType() {
+ return type;
+ }
+ public String getAttribute(String key) {
+ String s = map.get(key);
+ if (s == null)
+ s = "";
+ return s;
+ }
+ }
+
+ /**
+ * Get the ContentHeader from an HTTP request.
+ * @param req the request
+ * @return the header, encapsulated in a ContentHeader object
+ */
+ public ContentHeader getContentHeader(HttpServletRequest req) {
+ ContentHeader ch = new ContentHeader();
+ String s = req.getHeader("Content-Type");
+ if (s != null) {
+ String[] pp = s.split(";");
+ ch.type = pp[0].trim();
+ for (int i = 1; i < pp.length; i++) {
+ int ix = pp[i].indexOf('=');
+ if (ix > 0) {
+ String k = pp[i].substring(0, ix).trim();
+ String v = pp[i].substring(ix+1).trim();
+ ch.map.put(k, v);
+ } else {
+ ch.map.put(pp[i].trim(), "");
+ }
+ }
+ }
+ return ch;
+ }
+ // Methods for the Policy Engine classes - ProvDataProvider interface
+ @Override
+ public String getFeedOwner(String feedId) {
+ try {
+ int n = Integer.parseInt(feedId);
+ Feed f = Feed.getFeedById(n);
+ if (f != null)
+ return f.getPublisher();
+ } catch (NumberFormatException e) {
+ // ignore
+ }
+ return null;
+ }
+ @Override
+ public String getFeedClassification(String feedId) {
+ try {
+ int n = Integer.parseInt(feedId);
+ Feed f = Feed.getFeedById(n);
+ if (f != null)
+ return f.getAuthorization().getClassification();
+ } catch (NumberFormatException e) {
+ // ignore
+ }
+ return null;
+ }
+ @Override
+ public String getSubscriptionOwner(String subId) {
+ try {
+ int n = Integer.parseInt(subId);
+ Subscription s = Subscription.getSubscriptionById(n);
+ if (s != null)
+ return s.getSubscriber();
+ } catch (NumberFormatException e) {
+ // ignore
+ }
+ return null;
+ }
+
+ /*
+ * @Method - isUserMemberOfGroup - Rally:US708115
+ * @Params - group object and user to check if exists in given group
+ * @return - boolean value /true/false
+ */
+ private boolean isUserMemberOfGroup(Group group, String user) {
+
+ String groupdetails = group.getMembers().replace("]", "").replace("[", "");
+ String s[] = groupdetails.split("},");
+
+ for(int i=0; i < s.length; i++) {
+ JSONObject jsonObj = null;
+ try {
+ jsonObj = new JSONObject(s[i]+"}");
+ if(jsonObj.get("id").equals(user))
+ return true;
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+ return false;
+
+ }
+
+ /*
+ * @Method - getGroupByFeedGroupId- Rally:US708115
+ * @Params - User to check in group and feedid which is assigned the group.
+ * @return - string value grupid/null
+ */
+ @Override
+ public String getGroupByFeedGroupId(String owner, String feedId) {
+ try {
+ int n = Integer.parseInt(feedId);
+ Feed f = Feed.getFeedById(n);
+ if (f != null) {
+ int groupid = f.getGroupid();
+ if(groupid > 0) {
+ Group group = Group.getGroupById(groupid);
+ if(isUserMemberOfGroup(group, owner)) {
+ return group.getAuthid();
+ }
+ }
+ }
+ } catch (NumberFormatException e) {
+ // ignore
+ }
+ return null;
+ }
+
+ /*
+ * @Method - getGroupBySubGroupId - Rally:US708115
+ * @Params - User to check in group and subid which is assigned the group.
+ * @return - string value grupid/null
+ */
+ @Override
+ public String getGroupBySubGroupId(String owner, String subId) {
+ try {
+ int n = Integer.parseInt(subId);
+ Subscription s = Subscription.getSubscriptionById(n);
+ if (s != null) {
+ int groupid = s.getGroupid();
+ if(groupid > 0) {
+ Group group = Group.getGroupById(groupid);
+ if(isUserMemberOfGroup(group, owner)) {
+ return group.getAuthid();
+ }
+ }
+ }
+ } catch (NumberFormatException e) {
+ // ignore
+ }
+ return null;
+ }
+
+ /*
+ * @Method - setIpAndFqdnForEelf - Rally:US664892
+ * @Params - method, prints method name in EELF log.
+ */
+ protected void setIpAndFqdnForEelf(String method) {
+ MDC.clear();
+ MDC.put(MDC_SERVICE_NAME, method);
+ try {
+ MDC.put(MDC_SERVER_FQDN, InetAddress.getLocalHost().getHostName());
+ MDC.put(MDC_SERVER_IP_ADDRESS, InetAddress.getLocalHost().getHostAddress());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/DRFeedsServlet.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/DRFeedsServlet.java
new file mode 100644
index 00000000..df27042d
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/DRFeedsServlet.java
@@ -0,0 +1,300 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.json.JSONObject;
+
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+import com.att.research.datarouter.authz.AuthorizationResponse;
+import com.att.research.datarouter.provisioning.beans.EventLogRecord;
+import com.att.research.datarouter.provisioning.beans.Feed;
+import com.att.research.datarouter.provisioning.eelf.EelfMsgs;
+import com.att.research.datarouter.provisioning.utils.JSONUtilities;
+
+/**
+ * This servlet handles provisioning for the &lt;drFeedsURL&gt; which is the URL on the
+ * provisioning server used to create new feeds. It supports POST to create new feeds,
+ * and GET to support the Feeds Collection Query function.
+ *
+ * @author Robert Eby
+ * @version $Id$
+ */
+@SuppressWarnings("serial")
+public class DRFeedsServlet extends ProxyServlet {
+ //Adding EELF Logger Rally:US664892
+ private static EELFLogger eelflogger = EELFManager.getInstance().getLogger("com.att.research.datarouter.provisioning.DRFeedsServlet");
+
+ /**
+ * DELETE on the &lt;drFeedsURL&gt; -- not supported.
+ */
+ @Override
+ public void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doDelete");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+"");
+ String message = "DELETE not allowed for the drFeedsURL.";
+ EventLogRecord elr = new EventLogRecord(req);
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, message);
+ }
+ /**
+ * GET on the &lt;drFeedsURL&gt; -- query the list of feeds already existing in the DB.
+ * See the <i>Feeds Collection Queries</i> section in the <b>Provisioning API</b>
+ * document for details on how this method should be invoked.
+ */
+ @Override
+ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doGet");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+"");
+ EventLogRecord elr = new EventLogRecord(req);
+ String message = isAuthorizedForProvisioning(req);
+ if (message != null) {
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }
+ if (isProxyServer()) {
+ super.doGet(req, resp);
+ return;
+ }
+ String bhdr = req.getHeader(BEHALF_HEADER);
+ if (bhdr == null) {
+ message = "Missing "+BEHALF_HEADER+" header.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ String path = req.getRequestURI(); // Note: I think this should be getPathInfo(), but that doesn't work (Jetty bug?)
+ if (path != null && !path.equals("/")) {
+ message = "Bad URL.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_NOT_FOUND);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, message);
+ return;
+ }
+ // Check with the Authorizer
+ AuthorizationResponse aresp = authz.decide(req);
+ if (! aresp.isAuthorized()) {
+ message = "Policy Engine disallows access.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }
+
+ String name = req.getParameter("name");
+ String vers = req.getParameter("version");
+ String publ = req.getParameter("publisher");
+ String subs = req.getParameter("subscriber");
+ if (name != null && vers != null) {
+ // Display a specific feed
+ Feed feed = Feed.getFeedByNameVersion(name, vers);
+ if (feed == null || feed.isDeleted()) {
+ message = "This feed does not exist in the database.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ } else {
+ // send response
+ elr.setResult(HttpServletResponse.SC_OK);
+ eventlogger.info(elr);
+ resp.setStatus(HttpServletResponse.SC_OK);
+ resp.setContentType(FEEDFULL_CONTENT_TYPE);
+ resp.getOutputStream().print(feed.asJSONObject(true).toString());
+ }
+ } else {
+ // Display a list of URLs
+ List<String> list = null;
+ if (name != null) {
+ list = Feed.getFilteredFeedUrlList("name", name);
+ } else if (publ != null) {
+ list = Feed.getFilteredFeedUrlList("publ", publ);
+ } else if (subs != null) {
+ list = Feed.getFilteredFeedUrlList("subs", subs);
+ } else {
+ list = Feed.getFilteredFeedUrlList("all", null);
+ }
+ String t = JSONUtilities.createJSONArray(list);
+ // send response
+ elr.setResult(HttpServletResponse.SC_OK);
+ eventlogger.info(elr);
+ resp.setStatus(HttpServletResponse.SC_OK);
+ resp.setContentType(FEEDLIST_CONTENT_TYPE);
+ resp.getOutputStream().print(t);
+ }
+ }
+ /**
+ * PUT on the &lt;drFeedsURL&gt; -- not supported.
+ */
+ @Override
+ public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doPut");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+"");
+ String message = "PUT not allowed for the drFeedsURL.";
+ EventLogRecord elr = new EventLogRecord(req);
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, message);
+ }
+ /**
+ * POST on the &lt;drFeedsURL&gt; -- create a new feed.
+ * See the <i>Creating a Feed</i> section in the <b>Provisioning API</b>
+ * document for details on how this method should be invoked.
+ */
+ @Override
+ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doPost");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF, req.getHeader(BEHALF_HEADER));
+ EventLogRecord elr = new EventLogRecord(req);
+ String message = isAuthorizedForProvisioning(req);
+ if (message != null) {
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }
+ if (isProxyServer()) {
+ super.doPost(req, resp);
+ return;
+ }
+ String bhdr = req.getHeader(BEHALF_HEADER);
+ if (bhdr == null) {
+ message = "Missing "+BEHALF_HEADER+" header.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ String path = req.getRequestURI(); // Note: I think this should be getPathInfo(), but that doesn't work (Jetty bug?)
+ if (path != null && !path.equals("/")) {
+ message = "Bad URL.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_NOT_FOUND);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, message);
+ return;
+ }
+ // check content type is FEED_CONTENT_TYPE, version 1.0
+ ContentHeader ch = getContentHeader(req);
+ String ver = ch.getAttribute("version");
+ if (!ch.getType().equals(FEED_BASECONTENT_TYPE) || !(ver.equals("1.0") || ver.equals("2.0"))) {
+ message = "Incorrect content-type";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE, message);
+ return;
+ }
+ // Check with the Authorizer
+ AuthorizationResponse aresp = authz.decide(req);
+ if (! aresp.isAuthorized()) {
+ message = "Policy Engine disallows access.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }
+ JSONObject jo = getJSONfromInput(req);
+ if (jo == null) {
+ message = "Badly formed JSON";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ if (intlogger.isDebugEnabled())
+ intlogger.debug(jo.toString());
+ if (++active_feeds > max_feeds) {
+ active_feeds--;
+ message = "Cannot create feed; the maximum number of feeds has been configured.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_CONFLICT);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_CONFLICT, message);
+ return;
+ }
+ Feed feed = null;
+ try {
+ feed = new Feed(jo);
+ } catch (InvalidObjectException e) {
+ message = e.getMessage();
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ feed.setPublisher(bhdr); // set from X-ATT-DR-ON-BEHALF-OF header
+
+ // Check if this feed already exists
+ Feed feed2 = Feed.getFeedByNameVersion(feed.getName(), feed.getVersion());
+ if (feed2 != null) {
+ message = "This feed already exists in the database.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+
+ // Create FEED table entries
+ if (doInsert(feed)) {
+ // send response
+ elr.setResult(HttpServletResponse.SC_CREATED);
+ eventlogger.info(elr);
+ resp.setStatus(HttpServletResponse.SC_CREATED);
+ resp.setContentType(FEEDFULL_CONTENT_TYPE);
+ resp.setHeader("Location", feed.getLinks().getSelf());
+ resp.getOutputStream().print(feed.asLimitedJSONObject().toString());
+ provisioningDataChanged();
+ } else {
+ // Something went wrong with the INSERT
+ elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG);
+ }
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/FeedLogServlet.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/FeedLogServlet.java
new file mode 100644
index 00000000..dd6b75dc
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/FeedLogServlet.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package com.att.research.datarouter.provisioning;
+
+/**
+ * This servlet handles requests to the &lt;feedLogURL&gt;
+ * which are generated by the provisioning server to handle the log query API.
+ *
+ * @author Robert Eby
+ * @version $Id: FeedLogServlet.java,v 1.1 2013/04/26 21:00:24 eby Exp $
+ */
+@SuppressWarnings("serial")
+public class FeedLogServlet extends LogServlet {
+ public FeedLogServlet() {
+ super(true);
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/FeedServlet.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/FeedServlet.java
new file mode 100644
index 00000000..aff68538
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/FeedServlet.java
@@ -0,0 +1,362 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.json.JSONObject;
+
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+import com.att.research.datarouter.authz.AuthorizationResponse;
+import com.att.research.datarouter.provisioning.beans.EventLogRecord;
+import com.att.research.datarouter.provisioning.beans.Feed;
+import com.att.research.datarouter.provisioning.eelf.EelfMsgs;
+
+/**
+ * This servlet handles provisioning for the &lt;feedURL&gt; which is generated by the provisioning
+ * server to handle a particular feed. It supports DELETE to mark the feed as deleted,
+ * and GET to retrieve information about the feed, and PUT to modify the feed.
+ *
+ * @author Robert Eby
+ * @version $Id$
+ */
+@SuppressWarnings("serial")
+public class FeedServlet extends ProxyServlet {
+
+ //Adding EELF Logger Rally:US664892
+ private static EELFLogger eelflogger = EELFManager.getInstance().getLogger("com.att.research.datarouter.provisioning.FeedServlet");
+
+ /**
+ * Delete the Feed at the address /feed/&lt;feednumber&gt;.
+ * See the <i>Deleting a Feed</i> section in the <b>Provisioning API</b>
+ * document for details on how this method should be invoked.
+ */
+ @Override
+ public void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doDelete");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+"");
+ EventLogRecord elr = new EventLogRecord(req);
+ String message = isAuthorizedForProvisioning(req);
+ if (message != null) {
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }
+ if (isProxyServer()) {
+ super.doDelete(req, resp);
+ return;
+ }
+ String bhdr = req.getHeader(BEHALF_HEADER);
+ if (bhdr == null) {
+ message = "Missing "+BEHALF_HEADER+" header.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ int feedid = getIdFromPath(req);
+ if (feedid < 0) {
+ message = "Missing or bad feed number.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ Feed feed = Feed.getFeedById(feedid);
+ if (feed == null || feed.isDeleted()) {
+ message = "Missing or bad feed number.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_NOT_FOUND);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, message);
+ return;
+ }
+ // Check with the Authorizer
+ AuthorizationResponse aresp = authz.decide(req);
+ if (! aresp.isAuthorized()) {
+ message = "Policy Engine disallows access.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }
+
+ // Delete FEED table entry (set DELETED flag)
+ feed.setDeleted(true);
+ if (doUpdate(feed)) {
+ active_feeds--;
+ // send response
+ elr.setResult(HttpServletResponse.SC_NO_CONTENT);
+ eventlogger.info(elr);
+ resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
+ provisioningDataChanged();
+ } else {
+ // Something went wrong with the UPDATE
+ elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG);
+ }
+ }
+ /**
+ * Get information on the feed at the address /feed/&lt;feednumber&gt;.
+ * See the <i>Retrieving Information about a Feed</i> section in the <b>Provisioning API</b>
+ * document for details on how this method should be invoked.
+ */
+ @Override
+ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doGet");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+"");
+ EventLogRecord elr = new EventLogRecord(req);
+ String message = isAuthorizedForProvisioning(req);
+ if (message != null) {
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }
+ if (isProxyServer()) {
+ super.doGet(req, resp);
+ return;
+ }
+ String bhdr = req.getHeader(BEHALF_HEADER);
+ if (bhdr == null) {
+ message = "Missing "+BEHALF_HEADER+" header.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ int feedid = getIdFromPath(req);
+ if (feedid < 0) {
+ message = "Missing or bad feed number.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ Feed feed = Feed.getFeedById(feedid);
+ if (feed == null || feed.isDeleted()) {
+ message = "Missing or bad feed number.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_NOT_FOUND);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, message);
+ return;
+ }
+ // Check with the Authorizer
+ AuthorizationResponse aresp = authz.decide(req);
+ if (! aresp.isAuthorized()) {
+ message = "Policy Engine disallows access.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }
+
+ // send response
+ elr.setResult(HttpServletResponse.SC_OK);
+ eventlogger.info(elr);
+ resp.setStatus(HttpServletResponse.SC_OK);
+ resp.setContentType(FEEDFULL_CONTENT_TYPE);
+ resp.getOutputStream().print(feed.asJSONObject(true).toString());
+ }
+ /**
+ * PUT on the &lt;feedURL&gt; for a feed.
+ * See the <i>Modifying a Feed</i> section in the <b>Provisioning API</b>
+ * document for details on how this method should be invoked.
+ */
+ @Override
+ public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doPut");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+"");
+ EventLogRecord elr = new EventLogRecord(req);
+ String message = isAuthorizedForProvisioning(req);
+ if (message != null) {
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }
+ if (isProxyServer()) {
+ super.doPut(req, resp);
+ return;
+ }
+ String bhdr = req.getHeader(BEHALF_HEADER);
+ if (bhdr == null) {
+ message = "Missing "+BEHALF_HEADER+" header.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ int feedid = getIdFromPath(req);
+ if (feedid < 0) {
+ message = "Missing or bad feed number.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ Feed oldFeed = Feed.getFeedById(feedid);
+ if (oldFeed == null || oldFeed.isDeleted()) {
+ message = "Missing or bad feed number.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_NOT_FOUND);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, message);
+ return;
+ }
+ // check content type is FEED_CONTENT_TYPE, version 1.0
+ ContentHeader ch = getContentHeader(req);
+ String ver = ch.getAttribute("version");
+ if (!ch.getType().equals(FEED_BASECONTENT_TYPE) || !(ver.equals("1.0") || ver.equals("2.0"))) {
+ message = "Incorrect content-type";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE, message);
+ return;
+ }
+ JSONObject jo = getJSONfromInput(req);
+ if (jo == null) {
+ message = "Badly formed JSON";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ if (intlogger.isDebugEnabled())
+ intlogger.debug(jo.toString());
+ Feed feed = null;
+ try {
+ feed = new Feed(jo);
+ } catch (InvalidObjectException e) {
+ message = e.getMessage();
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ feed.setFeedid(feedid);
+ feed.setPublisher(bhdr); // set from X-ATT-DR-ON-BEHALF-OF header
+
+ String subjectgroup = (req.getHeader("X-ATT-DR-ON-BEHALF-OF-GROUP")); //Adding for group feature:Rally US708115
+ if (!oldFeed.getPublisher().equals(feed.getPublisher()) && subjectgroup == null) {
+ message = "This feed must be modified by the same publisher that created it.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ if (!oldFeed.getName().equals(feed.getName())) {
+ message = "The name of the feed may not be updated.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ if (!oldFeed.getVersion().equals(feed.getVersion())) {
+ message = "The version of the feed may not be updated.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ // Check with the Authorizer
+ AuthorizationResponse aresp = authz.decide(req);
+ if (! aresp.isAuthorized()) {
+ message = "Policy Engine disallows access.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }
+
+ // Update FEEDS table entries
+ if (doUpdate(feed)) {
+ // send response
+ elr.setResult(HttpServletResponse.SC_OK);
+ eventlogger.info(elr);
+ resp.setStatus(HttpServletResponse.SC_OK);
+ resp.setContentType(FEEDFULL_CONTENT_TYPE);
+ resp.getOutputStream().print(feed.asLimitedJSONObject().toString());
+
+
+ /**Change Owner ship of Feed //Adding for group feature:Rally US708115*/
+ if (jo.has("changeowner") && subjectgroup != null) {
+ Boolean changeowner = (Boolean) jo.get("changeowner");
+ if (changeowner != null && changeowner.equals(true)) {
+ feed.setPublisher(req.getHeader(BEHALF_HEADER));
+ feed.changeOwnerShip();
+ }
+ }
+ /***End of change ownership*/
+
+ provisioningDataChanged();
+ } else {
+ // Something went wrong with the UPDATE
+ elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG);
+ }
+ }
+ /**
+ * POST on the &lt;feedURL&gt; -- not supported.
+ */
+ @Override
+ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doPost");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF, req.getHeader(BEHALF_HEADER));
+ String message = "POST not allowed for the feedURL.";
+ EventLogRecord elr = new EventLogRecord(req);
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, message);
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/GroupServlet.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/GroupServlet.java
new file mode 100644
index 00000000..84ec3d2c
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/GroupServlet.java
@@ -0,0 +1,386 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.util.Collection;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.json.JSONObject;
+
+import com.att.research.datarouter.authz.AuthorizationResponse;
+import com.att.research.datarouter.provisioning.BaseServlet.ContentHeader;
+import com.att.research.datarouter.provisioning.beans.EventLogRecord;
+import com.att.research.datarouter.provisioning.beans.Group;
+import com.att.research.datarouter.provisioning.beans.Subscription;
+import com.att.research.datarouter.provisioning.utils.JSONUtilities;
+
+/**
+ * This servlet handles provisioning for the &lt;groups&gt; which is generated by the provisioning
+ * server to handle the creation and inspection of groups for FEEDS and SUBSCRIPTIONS.
+ *
+ * @author Vikram Singh
+ * @version $Id$
+ * @version $Id: Group.java,v 1.0 2016/07/19
+ */
+@SuppressWarnings("serial")
+public class GroupServlet extends ProxyServlet {
+ /**
+ * DELETE on the &lt;GRUPS&gt; -- not supported.
+ */
+ @Override
+ public void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ String message = "DELETE not allowed for the GROUPS.";
+ EventLogRecord elr = new EventLogRecord(req);
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, message);
+ }
+ /**
+ * GET on the the list of groups to a feed/sub.
+ * See the <i>Groups Collection Query</i> section in the <b>Provisioning API</b>
+ * document for details on how this method should be invoked.
+ */
+ @Override
+ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ EventLogRecord elr = new EventLogRecord(req);
+ String message = isAuthorizedForProvisioning(req);
+ if (message != null) {
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }
+ if (isProxyServer()) {
+ super.doGet(req, resp);
+ return;
+ }
+ String bhdr = req.getHeader(BEHALF_HEADER);
+ if (bhdr == null) {
+ message = "Missing "+BEHALF_HEADER+" header.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+
+ // Check with the Authorizer
+ /*AuthorizationResponse aresp = authz.decide(req);
+ if (! aresp.isAuthorized()) {
+ message = "Policy Engine disallows access.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }*/
+
+
+ /*ContentHeader ch = getContentHeader(req);
+ String ver = ch.getAttribute("version");
+ if (!ch.getType().equals(GROUPLIST_CONTENT_TYPE) || !(ver.equals("1.0") || ver.equals("2.0"))) {
+ intlogger.debug("Content-type is: "+req.getHeader("Content-Type"));
+ message = "Incorrect content-type";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE, message);
+ return;
+ }*/
+
+
+ int groupid = getIdFromPath(req);
+ if (groupid < 0) {
+ message = "Missing or bad group number.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+
+ Group gup = Group.getGroupById(groupid);
+ // send response
+ elr.setResult(HttpServletResponse.SC_OK);
+ eventlogger.info(elr);
+ resp.setStatus(HttpServletResponse.SC_OK);
+ resp.setContentType(GROUPFULL_CONTENT_TYPE);
+ resp.getOutputStream().print(gup.asJSONObject().toString());
+
+ // Display a list of Groups
+ /*Collection<Group> list = Group.getGroupById(groupid);
+ String t = JSONUtilities.createJSONArray(list);
+
+ // send response
+ elr.setResult(HttpServletResponse.SC_OK);
+ eventlogger.info(elr);
+ resp.setStatus(HttpServletResponse.SC_OK);
+ resp.setContentType(GROUPLIST_CONTENT_TYPE);
+ resp.getOutputStream().print(t);*/
+ }
+ /**
+ * PUT on the &lt;GROUPS&gt; -- not supported.
+ */
+ @Override
+ public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ EventLogRecord elr = new EventLogRecord(req);
+ String message = isAuthorizedForProvisioning(req);
+ if (message != null) {
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }
+ if (isProxyServer()) {
+ super.doPut(req, resp);
+ return;
+ }
+ String bhdr = req.getHeader(BEHALF_HEADER);
+ if (bhdr == null) {
+ message = "Missing "+BEHALF_HEADER+" header.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ int groupid = getIdFromPath(req);
+ if (groupid < 0) {
+ message = "Missing or bad groupid.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ Group oldgup = Group.getGroupById(groupid);
+ if (oldgup == null) {
+ message = "Missing or bad group number.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_NOT_FOUND);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, message);
+ return;
+ }
+ // Check with the Authorizer
+ /*AuthorizationResponse aresp = authz.decide(req);
+ if (! aresp.isAuthorized()) {
+ message = "Policy Engine disallows access.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }*/
+ // check content type is SUB_CONTENT_TYPE, version 1.0
+ ContentHeader ch = getContentHeader(req);
+ String ver = ch.getAttribute("version");
+ if (!ch.getType().equals(GROUP_BASECONTENT_TYPE) || !(ver.equals("1.0") || ver.equals("2.0"))) {
+ message = "Incorrect content-type";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE, message);
+ return;
+ }
+ JSONObject jo = getJSONfromInput(req);
+ if (jo == null) {
+ message = "Badly formed JSON";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ if (intlogger.isDebugEnabled())
+ intlogger.debug(jo.toString());
+ Group gup = null;
+ try {
+ gup = new Group(jo);
+ } catch (InvalidObjectException e) {
+ message = e.getMessage();
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ gup.setGroupid(oldgup.getGroupid());
+
+
+ Group gb2 = Group.getGroupMatching(gup, oldgup.getGroupid());
+ if (gb2 != null) {
+ eventlogger.warn("PROV0011 Creating a duplicate Group: "+gup.getName());
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Duplicate Group:"+gup.getName());
+ return;
+ }
+
+ // Update Groups table entries
+ if (doUpdate(gup)) {
+ // send response
+ elr.setResult(HttpServletResponse.SC_OK);
+ eventlogger.info(elr);
+ resp.setStatus(HttpServletResponse.SC_OK);
+ resp.setContentType(GROUPFULL_CONTENT_TYPE);
+ resp.getOutputStream().print(gup.asJSONObject().toString());
+ provisioningDataChanged();
+ } else {
+ // Something went wrong with the UPDATE
+ elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG);
+ }
+ }
+ /**
+ * POST on the &lt;groups&gt; -- create a new GROUPS to a feed.
+ * See the <i>Creating a GROUPS</i> section in the <b>Provisioning API</b>
+ * document for details on how this method should be invoked.
+ */
+ @Override
+ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ EventLogRecord elr = new EventLogRecord(req);
+ String message = isAuthorizedForProvisioning(req);
+ if (message != null) {
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }
+ if (isProxyServer()) {
+ super.doPost(req, resp);
+ return;
+ }
+ String bhdr = req.getHeader(BEHALF_HEADER);
+ if (bhdr == null) {
+ message = "Missing "+BEHALF_HEADER+" header.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ /*int feedid = getIdFromPath(req);
+ if (feedid < 0) {
+ message = "Missing or bad feed number.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ Feed feed = Feed.getFeedById(feedid);
+ if (feed == null || feed.isDeleted()) {
+ message = "Missing or bad feed number.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_NOT_FOUND);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, message);
+ return;
+ }*/
+ // Check with the Authorizer
+ /*AuthorizationResponse aresp = authz.decide(req);
+ if (! aresp.isAuthorized()) {
+ message = "Policy Engine disallows access.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }*/
+
+ // check content type is SUB_CONTENT_TYPE, version 1.0
+ ContentHeader ch = getContentHeader(req);
+ String ver = ch.getAttribute("version");
+ if (!ch.getType().equals(GROUP_BASECONTENT_TYPE) || !(ver.equals("1.0") || ver.equals("2.0"))) {
+ intlogger.debug("Content-type is: "+req.getHeader("Content-Type"));
+ message = "Incorrect content-type";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE, message);
+ return;
+ }
+ JSONObject jo = getJSONfromInput(req);
+ if (jo == null) {
+ message = "Badly formed JSON";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ if (intlogger.isDebugEnabled())
+ intlogger.debug(jo.toString());
+
+ Group gup = null;
+ try {
+ gup = new Group(jo);
+ } catch (InvalidObjectException e) {
+ message = e.getMessage();
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ //gup.setFeedid(feedid);
+ //sub.setSubscriber(bhdr); // set from X-ATT-DR-ON-BEHALF-OF header
+
+ // Check if this group already exists; not an error (yet), just warn
+ Group gb2 = Group.getGroupMatching(gup);
+ if (gb2 != null) {
+ eventlogger.warn("PROV0011 Creating a duplicate Group: "+gup.getName());
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Duplicate Group:"+gup.getName());
+ return;
+ }
+
+
+ // Create GROUPS table entries
+ if (doInsert(gup)) {
+ // send response
+ elr.setResult(HttpServletResponse.SC_CREATED);
+ eventlogger.info(elr);
+ resp.setStatus(HttpServletResponse.SC_CREATED);
+ resp.setContentType(GROUPFULL_CONTENT_TYPE);
+ resp.getOutputStream().print(gup.asJSONObject().toString());
+ provisioningDataChanged();
+ } else {
+ // Something went wrong with the INSERT
+ elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG);
+ }
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/InternalServlet.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/InternalServlet.java
new file mode 100644
index 00000000..e50a478e
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/InternalServlet.java
@@ -0,0 +1,506 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.FileStore;
+import java.nio.file.FileSystem;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.Properties;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.json.JSONArray;
+
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+import com.att.research.datarouter.provisioning.beans.EventLogRecord;
+import com.att.research.datarouter.provisioning.beans.LogRecord;
+import com.att.research.datarouter.provisioning.beans.Parameters;
+import com.att.research.datarouter.provisioning.eelf.EelfMsgs;
+import com.att.research.datarouter.provisioning.utils.DB;
+import com.att.research.datarouter.provisioning.utils.RLEBitSet;
+import com.att.research.datarouter.provisioning.utils.LogfileLoader;
+
+/**
+ * <p>
+ * This servlet handles requests to URLs under /internal on the provisioning server.
+ * These include:
+ * </p>
+ * <div class="contentContainer">
+ * <table class="packageSummary" border="0" cellpadding="3" cellspacing="0">
+ * <caption><span>URL Path Summary</span><span class="tabEnd">&nbsp;</span></caption>
+ * <tr>
+ * <th class="colFirst" width="15%">URL Path</th>
+ * <th class="colOne">Method</th>
+ * <th class="colLast">Purpose</th>
+ * </tr>
+ * <tr class="altColor">
+ * <td class="colFirst">/internal/prov</td>
+ * <td class="colOne">GET</td>
+ * <td class="colLast">used to GET a full JSON copy of the provisioning data.</td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td class="colFirst">/internal/fetchProv</td>
+ * <td class="colOne">GET</td>
+ * <td class="colLast">used to signal to a standby POD that the provisioning data should be fetched from the active POD.</td>
+ * </tr>
+ * <tr class="altColor">
+ * <td class="colFirst" rowspan="2">/internal/logs</td>
+ * <td class="colOne">GET</td>
+ * <td class="colLast">used to GET an index of log files and individual logs for this provisioning server.</td>
+ * </tr>
+ * <tr class="altColor">
+ * <td class="colOne">POST</td>
+ * <td class="colLast">used to POST log files from the individual nodes to this provisioning server.</td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td class="colFirst" rowspan="4">/internal/api</td>
+ * <td class="colOne">GET</td>
+ * <td class="colLast">used to GET an individual parameter value. The parameter name is specified by the path after /api/.</td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td class="colOne">PUT</td>
+ * <td class="colLast">used to set an individual parameter value. The parameter name is specified by the path after /api/.</td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td class="colOne">DELETE</td>
+ * <td class="colLast">used to remove an individual parameter value. The parameter name is specified by the path after /api/.</td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td class="colOne">POST</td>
+ * <td class="colLast">used to create a new individual parameter value. The parameter name is specified by the path after /api/.</td>
+ * </tr>
+ * <tr class="altColor">
+ * <td class="colFirst">/internal/halt</td>
+ * <td class="colOne">GET</td>
+ * <td class="colLast">used to halt the server (must be accessed from 127.0.0.1).</td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td class="colFirst" rowspan="2">/internal/drlogs</td>
+ * <td class="colOne">GET</td>
+ * <td class="colLast">used to get a list of DR log entries available for retrieval.
+ * Note: these are the actual data router log entries sent to the provisioning server
+ * by the nodes, not the provisioning server's internal logs (access via /internal/logs above).
+ * The range is returned as a list of record sequence numbers.</td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td class="colOne">POST</td>
+ * <td class="colLast">used to retrieve specific log entries.
+ * The sequence numbers of the records to fetch are POST-ed; the records matching the sequence numbers are returned.</td>
+ * </tr>
+ * <tr class="altColor">
+ * <td class="colFirst">/internal/route/*</td>
+ * <td class="colOne">*</td>
+ * <td class="colLast">URLs under this path are handled via the {@link com.att.research.datarouter.provisioning.RouteServlet}</td>
+ * </tr>
+ * </table>
+ * </div>
+ * <p>
+ * Authorization to use these URLs is a little different than for other URLs on the provisioning server.
+ * For the most part, the IP address that the request comes from should be either:
+ * </p>
+ * <ol>
+ * <li>an IP address of a provisioning server, or</li>
+ * <li>the IP address of a node (to allow access to /internal/prov), or</li>
+ * <li>an IP address from the "<i>special subnet</i>" which is configured with
+ * the PROV_SPECIAL_SUBNET parameter.
+ * </ol>
+ * <p>
+ * In addition, requests to /internal/halt can ONLY come from localhost (127.0.0.1) on the HTTP port.
+ * </p>
+ * <p>
+ * All DELETE/GET/PUT/POST requests made to /internal/api on this servlet on the standby server are
+ * proxied to the active server (using the {@link ProxyServlet}) if it is up and reachable.
+ * </p>
+ *
+ * @author Robert Eby
+ * @version $Id: InternalServlet.java,v 1.23 2014/03/24 18:47:10 eby Exp $
+ */
+@SuppressWarnings("serial")
+public class InternalServlet extends ProxyServlet {
+ private static Integer logseq = new Integer(0); // another piece of info to make log spool file names unique
+ //Adding EELF Logger Rally:US664892
+ private static EELFLogger eelflogger = EELFManager.getInstance().getLogger("com.att.research.datarouter.provisioning.InternalServlet");
+
+ /**
+ * Delete a parameter at the address /internal/api/&lt;parameter&gt;.
+ * See the <b>Internal API</b> document for details on how this method should be invoked.
+ */
+ @Override
+ public void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doDelete");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+"");
+ EventLogRecord elr = new EventLogRecord(req);
+ if (!isAuthorizedForInternal(req)) {
+ elr.setMessage("Unauthorized.");
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Unauthorized.");
+ return;
+ }
+
+ String path = req.getPathInfo();
+ if (path.startsWith("/api/")) {
+ if (isProxyOK(req) && isProxyServer()) {
+ super.doDelete(req, resp);
+ return;
+ }
+ String key = path.substring(5);
+ if (key.length() > 0) {
+ Parameters param = Parameters.getParameter(key);
+ if (param != null) {
+ if (doDelete(param)) {
+ elr.setResult(HttpServletResponse.SC_OK);
+ eventlogger.info(elr);
+ resp.setStatus(HttpServletResponse.SC_OK);
+ provisioningDataChanged();
+ provisioningParametersChanged();
+ } else {
+ // Something went wrong with the DELETE
+ elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG);
+ }
+ return;
+ }
+ }
+ }
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Bad URL.");
+ }
+ /**
+ * Get some information (such as a parameter) underneath the /internal/ namespace.
+ * See the <b>Internal API</b> document for details on how this method should be invoked.
+ */
+ @Override
+ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doGet");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+"");
+ String path = req.getPathInfo();
+ if (path.equals("/halt") && !req.isSecure()) {
+ // request to halt the server - can ONLY come from localhost
+ String remote = req.getRemoteAddr();
+ if (remote.equals("127.0.0.1")) {
+ intlogger.info("PROV0009 Request to HALT received.");
+ resp.setStatus(HttpServletResponse.SC_OK);
+ Main.shutdown();
+ } else {
+ intlogger.info("PROV0010 Disallowed request to HALT received from "+remote);
+ resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
+ }
+ return;
+ }
+
+ EventLogRecord elr = new EventLogRecord(req);
+ if (!isAuthorizedForInternal(req)) {
+ elr.setMessage("Unauthorized.");
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Unauthorized.");
+ return;
+ }
+ if (path.equals("/fetchProv") && !req.isSecure()) {
+ // if request came from active_pod or standby_pod and it is not us, reload prov data
+ SynchronizerTask s = SynchronizerTask.getSynchronizer();
+ s.doFetch();
+ resp.setStatus(HttpServletResponse.SC_OK);
+ return;
+ }
+ if (path.equals("/prov")) {
+ if (isProxyOK(req) && isProxyServer()) {
+ if (super.doGetWithFallback(req, resp))
+ return;
+ // fall back to returning the local data if the remote is unreachable
+ intlogger.info("Active server unavailable; falling back to local copy.");
+ }
+ Poker p = Poker.getPoker();
+ resp.setStatus(HttpServletResponse.SC_OK);
+ resp.setContentType(PROVFULL_CONTENT_TYPE2);
+ resp.getOutputStream().print(p.getProvisioningString());
+ return;
+ }
+ if (path.equals("/logs") || path.equals("/logs/")) {
+ resp.setStatus(HttpServletResponse.SC_OK);
+ resp.setContentType("application/json");
+ resp.getOutputStream().print(generateLogfileList().toString());
+ return;
+ }
+ if (path.startsWith("/logs/")) {
+ Properties p = (new DB()).getProperties();
+ String logdir = p.getProperty("com.att.research.datarouter.provserver.accesslog.dir");
+ String logfile = path.substring(6);
+ if (logdir != null && logfile != null && logfile.indexOf('/') < 0) {
+ File log = new File(logdir + "/" + logfile);
+ if (log.exists() && log.isFile()) {
+ resp.setStatus(HttpServletResponse.SC_OK);
+ resp.setContentType("text/plain");
+ Path logpath = Paths.get(log.getAbsolutePath());
+ Files.copy(logpath, resp.getOutputStream());
+ return;
+ }
+ }
+ resp.sendError(HttpServletResponse.SC_NO_CONTENT, "No file.");
+ return;
+ }
+ if (path.startsWith("/api/")) {
+ if (isProxyOK(req) && isProxyServer()) {
+ super.doGet(req, resp);
+ return;
+ }
+ String key = path.substring(5);
+ if (key.length() > 0) {
+ Parameters param = Parameters.getParameter(key);
+ if (param != null) {
+ resp.setStatus(HttpServletResponse.SC_OK);
+ resp.setContentType("text/plain");
+ resp.getOutputStream().print(param.getValue() + "\n");
+ return;
+ }
+ }
+ }
+ if (path.equals("/drlogs") || path.equals("/drlogs/")) {
+ // Special POD <=> POD API to determine what log file records are loaded here
+ LogfileLoader lfl = LogfileLoader.getLoader();
+ resp.setStatus(HttpServletResponse.SC_OK);
+ resp.setContentType("text/plain");
+ resp.getOutputStream().print(lfl.getBitSet().toString());
+ return;
+ }
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Bad URL.");
+ }
+ /**
+ * Modify a parameter at the address /internal/api/&lt;parameter&gt;.
+ * See the <b>Internal API</b> document for details on how this method should be invoked.
+ */
+ @Override
+ public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doPut");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+"");
+ EventLogRecord elr = new EventLogRecord(req);
+ if (!isAuthorizedForInternal(req)) {
+ elr.setMessage("Unauthorized.");
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Unauthorized.");
+ return;
+ }
+ String path = req.getPathInfo();
+ if (path.startsWith("/api/")) {
+ if (isProxyOK(req) && isProxyServer()) {
+ super.doPut(req, resp);
+ return;
+ }
+ String key = path.substring(5);
+ if (key.length() > 0) {
+ Parameters param = Parameters.getParameter(key);
+ if (param != null) {
+ String t = catValues(req.getParameterValues("val"));
+ param.setValue(t);
+ if (doUpdate(param)) {
+ elr.setResult(HttpServletResponse.SC_OK);
+ eventlogger.info(elr);
+ resp.setStatus(HttpServletResponse.SC_OK);
+ provisioningDataChanged();
+ provisioningParametersChanged();
+ } else {
+ // Something went wrong with the UPDATE
+ elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG);
+ }
+ return;
+ }
+ }
+ }
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Bad URL.");
+ }
+ /**
+ * Create some new information (such as a parameter or log entries) underneath the /internal/ namespace.
+ * See the <b>Internal API</b> document for details on how this method should be invoked.
+ */
+ @SuppressWarnings("resource")
+ @Override
+ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doPost");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF, req.getHeader(BEHALF_HEADER));
+ EventLogRecord elr = new EventLogRecord(req);
+ if (!isAuthorizedForInternal(req)) {
+ elr.setMessage("Unauthorized.");
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Unauthorized.");
+ return;
+ }
+
+ String path = req.getPathInfo();
+ if (path.startsWith("/api/")) {
+ if (isProxyOK(req) && isProxyServer()) {
+ super.doPost(req, resp);
+ return;
+ }
+ String key = path.substring(5);
+ if (key.length() > 0) {
+ Parameters param = Parameters.getParameter(key);
+ if (param == null) {
+ String t = catValues(req.getParameterValues("val"));
+ param = new Parameters(key, t);
+ if (doInsert(param)) {
+ elr.setResult(HttpServletResponse.SC_OK);
+ eventlogger.info(elr);
+ resp.setStatus(HttpServletResponse.SC_OK);
+ provisioningDataChanged();
+ provisioningParametersChanged();
+ } else {
+ // Something went wrong with the INSERT
+ elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG);
+ }
+ return;
+ }
+ }
+ }
+
+ if (path.equals("/logs") || path.equals("/logs/")) {
+ String ctype = req.getHeader("Content-Type");
+ if (ctype == null || !ctype.equals("text/plain")) {
+ elr.setResult(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
+ elr.setMessage("Bad media type: "+ctype);
+ resp.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
+ eventlogger.info(elr);
+ return;
+ }
+ String spooldir = (new DB()).getProperties().getProperty("com.att.research.datarouter.provserver.spooldir");
+ String spoolname = String.format("%d-%d-", System.currentTimeMillis(), Thread.currentThread().getId());
+ synchronized (logseq) {
+ // perhaps unnecessary, but it helps make the name unique
+ spoolname += logseq.toString();
+ logseq++;
+ }
+ String encoding = req.getHeader("Content-Encoding");
+ if (encoding != null) {
+ if (encoding.trim().equals("gzip")) {
+ spoolname += ".gz";
+ } else {
+ elr.setResult(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
+ resp.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
+ eventlogger.info(elr);
+ return;
+ }
+ }
+ // Determine space available -- available space must be at least 5%
+ FileSystem fs = (Paths.get(spooldir)).getFileSystem();
+ long total = 0;
+ long avail = 0;
+ for (FileStore store: fs.getFileStores()) {
+ total += store.getTotalSpace();
+ avail += store.getUsableSpace();
+ }
+ try { fs.close(); } catch (Exception e) { }
+ if (((avail * 100) / total) < 5) {
+ elr.setResult(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
+ resp.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
+ eventlogger.info(elr);
+ return;
+ }
+ Path tmppath = Paths.get(spooldir, spoolname);
+ Path donepath = Paths.get(spooldir, "IN."+spoolname);
+ Files.copy(req.getInputStream(), Paths.get(spooldir, spoolname), StandardCopyOption.REPLACE_EXISTING);
+ Files.move(tmppath, donepath, StandardCopyOption.REPLACE_EXISTING);
+ elr.setResult(HttpServletResponse.SC_CREATED);
+ resp.setStatus(HttpServletResponse.SC_CREATED);
+ eventlogger.info(elr);
+ LogfileLoader.getLoader(); // This starts the logfile loader "task"
+ return;
+ }
+
+ if (path.equals("/drlogs") || path.equals("/drlogs/")) {
+ // Receive post request and generate log entries
+ String ctype = req.getHeader("Content-Type");
+ if (ctype == null || !ctype.equals("text/plain")) {
+ elr.setResult(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
+ elr.setMessage("Bad media type: "+ctype);
+ resp.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
+ eventlogger.info(elr);
+ return;
+ }
+ InputStream is = req.getInputStream();
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ int ch = 0;
+ while ((ch = is.read()) >= 0)
+ bos.write(ch);
+ RLEBitSet bs = new RLEBitSet(bos.toString()); // The set of records to retrieve
+ elr.setResult(HttpServletResponse.SC_OK);
+ resp.setStatus(HttpServletResponse.SC_OK);
+ resp.setContentType("text/plain");
+ LogRecord.printLogRecords(resp.getOutputStream(), bs);
+ eventlogger.info(elr);
+ return;
+ }
+
+ elr.setResult(HttpServletResponse.SC_NOT_FOUND);
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Bad URL.");
+ eventlogger.info(elr);
+ }
+
+ private String catValues(String[] v) {
+ StringBuilder sb = new StringBuilder();
+ if (v != null) {
+ String pfx = "";
+ for (String s : v) {
+ sb.append(pfx);
+ sb.append(s);
+ pfx = "|";
+ }
+ }
+ return sb.toString();
+ }
+ private JSONArray generateLogfileList() {
+ JSONArray ja = new JSONArray();
+ Properties p = (new DB()).getProperties();
+ String s = p.getProperty("com.att.research.datarouter.provserver.accesslog.dir");
+ if (s != null) {
+ String[] dirs = s.split(",");
+ for (String dir : dirs) {
+ File f = new File(dir);
+ String[] list = f.list();
+ if (list != null) {
+ for (String s2 : list) {
+ if (!s2.startsWith("."))
+ ja.put(s2);
+ }
+ }
+ }
+ }
+ return ja;
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/LogServlet.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/LogServlet.java
new file mode 100644
index 00000000..7ef74d12
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/LogServlet.java
@@ -0,0 +1,433 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.json.LOGJSONObject;
+
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+import com.att.research.datarouter.provisioning.beans.DeliveryRecord;
+import com.att.research.datarouter.provisioning.beans.EventLogRecord;
+import com.att.research.datarouter.provisioning.beans.ExpiryRecord;
+import com.att.research.datarouter.provisioning.beans.LOGJSONable;
+import com.att.research.datarouter.provisioning.beans.PublishRecord;
+import com.att.research.datarouter.provisioning.beans.Subscription;
+import com.att.research.datarouter.provisioning.eelf.EelfMsgs;
+import com.att.research.datarouter.provisioning.utils.DB;
+
+/**
+ * This servlet handles requests to the &lt;feedLogURL&gt; and &lt;subLogURL&gt;,
+ * which are generated by the provisioning server to handle the log query API.
+ *
+ * @author Robert Eby
+ * @version $Id: LogServlet.java,v 1.11 2014/03/28 17:27:02 eby Exp $
+ */
+@SuppressWarnings("serial")
+public class LogServlet extends BaseServlet {
+ //Adding EELF Logger Rally:US664892
+ private static EELFLogger eelflogger = EELFManager.getInstance().getLogger("com.att.research.datarouter.provisioning.LogServlet");
+
+ private static final long TWENTYFOUR_HOURS = (24 * 60 * 60 * 1000L);
+ private static final String fmt1 = "yyyy-MM-dd'T'HH:mm:ss'Z'";
+ private static final String fmt2 = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
+
+ private boolean isfeedlog;
+
+ public abstract class RowHandler {
+ private final ServletOutputStream out;
+ private final String[] fields;
+ public boolean firstrow;
+
+ public RowHandler(ServletOutputStream out, String fieldparam, boolean b) {
+ this.out = out;
+ this.firstrow = b;
+ this.fields = (fieldparam != null) ? fieldparam.split(":") : null;
+ }
+ public void handleRow(ResultSet rs) {
+ try {
+ LOGJSONable js = buildJSONable(rs);
+ LOGJSONObject jo = js.asJSONObject();
+ if (fields != null) {
+ // filter out unwanted fields
+ LOGJSONObject j2 = new LOGJSONObject();
+ for (String key : fields) {
+ Object v = jo.opt(key);
+ if (v != null)
+ j2.put(key, v);
+ }
+ jo = j2;
+ }
+ String t = firstrow ? "\n" : ",\n";
+ t += jo.toString();
+ out.print(t);
+ firstrow = false;
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+ public abstract LOGJSONable buildJSONable(ResultSet rs) throws SQLException;
+ }
+ public class PublishRecordRowHandler extends RowHandler {
+ public PublishRecordRowHandler(ServletOutputStream out, String fields, boolean b) {
+ super(out, fields, b);
+ }
+ @Override
+ public LOGJSONable buildJSONable(ResultSet rs) throws SQLException {
+ return new PublishRecord(rs);
+ }
+ }
+ public class DeliveryRecordRowHandler extends RowHandler {
+ public DeliveryRecordRowHandler(ServletOutputStream out, String fields, boolean b) {
+ super(out, fields, b);
+ }
+ @Override
+ public LOGJSONable buildJSONable(ResultSet rs) throws SQLException {
+ return new DeliveryRecord(rs);
+ }
+ }
+ public class ExpiryRecordRowHandler extends RowHandler {
+ public ExpiryRecordRowHandler(ServletOutputStream out, String fields, boolean b) {
+ super(out, fields, b);
+ }
+ @Override
+ public LOGJSONable buildJSONable(ResultSet rs) throws SQLException {
+ return new ExpiryRecord(rs);
+ }
+ }
+
+ /**
+ * This class must be created from either a {@link FeedLogServlet} or a {@link SubLogServlet}.
+ * @param isFeedLog boolean to handle those places where a feedlog request is different from
+ * a sublog request
+ */
+ protected LogServlet(boolean isFeedLog) {
+ this.isfeedlog = isFeedLog;
+ }
+
+ /**
+ * DELETE a logging URL -- not supported.
+ */
+ @Override
+ public void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doDelete");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+"");
+ String message = "DELETE not allowed for the logURL.";
+ EventLogRecord elr = new EventLogRecord(req);
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, message);
+ }
+ /**
+ * GET a logging URL -- retrieve logging data for a feed or subscription.
+ * See the <b>Logging API</b> document for details on how this method should be invoked.
+ */
+ @Override
+ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doGet");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+"");
+ int id = getIdFromPath(req);
+ if (id < 0) {
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Missing or bad feed/subscription number.");
+ return;
+ }
+ Map<String, String> map = buildMapFromRequest(req);
+ if (map.get("err") != null) {
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid arguments: "+map.get("err"));
+ return;
+ }
+ // check Accept: header??
+
+ resp.setStatus(HttpServletResponse.SC_OK);
+ resp.setContentType(LOGLIST_CONTENT_TYPE);
+ @SuppressWarnings("resource")
+ ServletOutputStream out = resp.getOutputStream();
+ final String fields = req.getParameter("fields");
+
+ out.print("[");
+ if (isfeedlog) {
+ // Handle /feedlog/feedid request
+ boolean firstrow = true;
+
+ // 1. Collect publish records for this feed
+ RowHandler rh = new PublishRecordRowHandler(out, fields, firstrow);
+ getPublishRecordsForFeed(id, rh, map);
+ firstrow = rh.firstrow;
+
+ // 2. Collect delivery records for subscriptions to this feed
+ rh = new DeliveryRecordRowHandler(out, fields, firstrow);
+ getDeliveryRecordsForFeed(id, rh, map);
+ firstrow = rh.firstrow;
+
+ // 3. Collect expiry records for subscriptions to this feed
+ rh = new ExpiryRecordRowHandler(out, fields, firstrow);
+ getExpiryRecordsForFeed(id, rh, map);
+ } else {
+ // Handle /sublog/subid request
+ Subscription sub = Subscription.getSubscriptionById(id);
+ if (sub != null) {
+ // 1. Collect publish records for the feed this subscription feeds
+ RowHandler rh = new PublishRecordRowHandler(out, fields, true);
+ getPublishRecordsForFeed(sub.getFeedid(), rh, map);
+
+ // 2. Collect delivery records for this subscription
+ rh = new DeliveryRecordRowHandler(out, fields, rh.firstrow);
+ getDeliveryRecordsForSubscription(id, rh, map);
+
+ // 3. Collect expiry records for this subscription
+ rh = new ExpiryRecordRowHandler(out, fields, rh.firstrow);
+ getExpiryRecordsForSubscription(id, rh, map);
+ }
+ }
+ out.print("\n]");
+ }
+ /**
+ * PUT a logging URL -- not supported.
+ */
+ @Override
+ public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doPut");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+"");
+ String message = "PUT not allowed for the logURL.";
+ EventLogRecord elr = new EventLogRecord(req);
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, message);
+ }
+ /**
+ * POST a logging URL -- not supported.
+ */
+ @Override
+ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doPost");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF, req.getHeader(BEHALF_HEADER));
+ String message = "POST not allowed for the logURL.";
+ EventLogRecord elr = new EventLogRecord(req);
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, message);
+ }
+
+ private Map<String, String> buildMapFromRequest(HttpServletRequest req) {
+ Map<String, String> map = new HashMap<String, String>();
+ String s = req.getParameter("type");
+ if (s != null) {
+ if (s.equals("pub") || s.equals("del") || s.equals("exp")) {
+ map.put("type", s);
+ } else {
+ map.put("err", "bad type");
+ return map;
+ }
+ } else
+ map.put("type", "all");
+ map.put("publishSQL", "");
+ map.put("statusSQL", "");
+ map.put("resultSQL", "");
+ map.put("reasonSQL", "");
+
+ s = req.getParameter("publishId");
+ if (s != null) {
+ if (s.indexOf("'") >= 0) {
+ map.put("err", "bad publishId");
+ return map;
+ }
+ map.put("publishSQL", " AND PUBLISH_ID = '"+s+"'");
+ }
+
+ s = req.getParameter("statusCode");
+ if (s != null) {
+ String sql = null;
+ if (s.equals("success")) {
+ sql = " AND STATUS >= 200 AND STATUS < 300";
+ } else if (s.equals("redirect")) {
+ sql = " AND STATUS >= 300 AND STATUS < 400";
+ } else if (s.equals("failure")) {
+ sql = " AND STATUS >= 400";
+ } else {
+ try {
+ Integer n = Integer.parseInt(s);
+ if ((n >= 100 && n < 600) || (n == -1))
+ sql = " AND STATUS = " + n;
+ } catch (NumberFormatException e) {
+ }
+ }
+ if (sql == null) {
+ map.put("err", "bad statusCode");
+ return map;
+ }
+ map.put("statusSQL", sql);
+ map.put("resultSQL", sql.replaceAll("STATUS", "RESULT"));
+ }
+
+ s = req.getParameter("expiryReason");
+ if (s != null) {
+ map.put("type", "exp");
+ if (s.equals("notRetryable")) {
+ map.put("reasonSQL", " AND REASON = 'notRetryable'");
+ } else if (s.equals("retriesExhausted")) {
+ map.put("reasonSQL", " AND REASON = 'retriesExhausted'");
+ } else if (s.equals("diskFull")) {
+ map.put("reasonSQL", " AND REASON = 'diskFull'");
+ } else if (s.equals("other")) {
+ map.put("reasonSQL", " AND REASON = 'other'");
+ } else {
+ map.put("err", "bad expiryReason");
+ return map;
+ }
+ }
+
+ long stime = getTimeFromParam(req.getParameter("start"));
+ if (stime < 0) {
+ map.put("err", "bad start");
+ return map;
+ }
+ long etime = getTimeFromParam(req.getParameter("end"));
+ if (etime < 0) {
+ map.put("err", "bad end");
+ return map;
+ }
+ if (stime == 0 && etime == 0) {
+ etime = System.currentTimeMillis();
+ stime = etime - TWENTYFOUR_HOURS;
+ } else if (stime == 0) {
+ stime = etime - TWENTYFOUR_HOURS;
+ } else if (etime == 0) {
+ etime = stime + TWENTYFOUR_HOURS;
+ }
+ map.put("timeSQL", String.format(" AND EVENT_TIME >= %d AND EVENT_TIME <= %d", stime, etime));
+ return map;
+ }
+ private long getTimeFromParam(final String s) {
+ if (s == null)
+ return 0;
+ try {
+ // First, look for an RFC 3339 date
+ String fmt = (s.indexOf('.') > 0) ? fmt2 : fmt1;
+ SimpleDateFormat sdf = new SimpleDateFormat(fmt);
+ Date d = sdf.parse(s);
+ return d.getTime();
+ } catch (ParseException e) {
+ }
+ try {
+ // Also allow a long (in ms); useful for testing
+ long n = Long.parseLong(s);
+ return n;
+ } catch (NumberFormatException e) {
+ }
+ intlogger.info("Error parsing time="+s);
+ return -1;
+ }
+
+ private void getPublishRecordsForFeed(int feedid, RowHandler rh, Map<String, String> map) {
+ String type = map.get("type");
+ if (type.equals("all") || type.equals("pub")) {
+ String sql = "select * from LOG_RECORDS where FEEDID = "+feedid
+ + " AND TYPE = 'pub'"
+ + map.get("timeSQL") + map.get("publishSQL") + map.get("statusSQL");
+ getRecordsForSQL(sql, rh);
+ }
+ }
+ private void getDeliveryRecordsForFeed(int feedid, RowHandler rh, Map<String, String> map) {
+ String type = map.get("type");
+ if (type.equals("all") || type.equals("del")) {
+ String sql = "select * from LOG_RECORDS where FEEDID = "+feedid
+ + " AND TYPE = 'del'"
+ + map.get("timeSQL") + map.get("publishSQL") + map.get("resultSQL");
+ getRecordsForSQL(sql, rh);
+ }
+ }
+ private void getDeliveryRecordsForSubscription(int subid, RowHandler rh, Map<String, String> map) {
+ String type = map.get("type");
+ if (type.equals("all") || type.equals("del")) {
+ String sql = "select * from LOG_RECORDS where DELIVERY_SUBID = "+subid
+ + " AND TYPE = 'del'"
+ + map.get("timeSQL") + map.get("publishSQL") + map.get("resultSQL");
+ getRecordsForSQL(sql, rh);
+ }
+ }
+ private void getExpiryRecordsForFeed(int feedid, RowHandler rh, Map<String, String> map) {
+ String type = map.get("type");
+ if (type.equals("all") || type.equals("exp")) {
+ String st = map.get("statusSQL");
+ if (st == null || st.length() == 0) {
+ String sql = "select * from LOG_RECORDS where FEEDID = "+feedid
+ + " AND TYPE = 'exp'"
+ + map.get("timeSQL") + map.get("publishSQL") + map.get("reasonSQL");
+ getRecordsForSQL(sql, rh);
+ }
+ }
+ }
+ private void getExpiryRecordsForSubscription(int subid, RowHandler rh, Map<String, String> map) {
+ String type = map.get("type");
+ if (type.equals("all") || type.equals("exp")) {
+ String st = map.get("statusSQL");
+ if (st == null || st.length() == 0) {
+ String sql = "select * from LOG_RECORDS where DELIVERY_SUBID = "+subid
+ + " AND TYPE = 'exp'"
+ + map.get("timeSQL") + map.get("publishSQL") + map.get("reasonSQL");
+ getRecordsForSQL(sql, rh);
+ }
+ }
+ }
+ private void getRecordsForSQL(String sql, RowHandler rh) {
+ intlogger.debug(sql);
+ long start = System.currentTimeMillis();
+ DB db = new DB();
+ Connection conn = null;
+ try {
+ conn = db.getConnection();
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery(sql);
+ while (rs.next()) {
+ rh.handleRow(rs);
+ }
+ rs.close();
+ stmt.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ } finally {
+ if (conn != null)
+ db.release(conn);
+ }
+ intlogger.debug("Time: " + (System.currentTimeMillis()-start) + " ms");
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/Main.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/Main.java
new file mode 100644
index 00000000..5911ecd8
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/Main.java
@@ -0,0 +1,245 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning;
+
+import java.security.Security;
+import java.util.Properties;
+import java.util.Timer;
+
+import org.apache.log4j.Logger;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.NCSARequestLog;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.ContextHandlerCollection;
+import org.eclipse.jetty.server.handler.DefaultHandler;
+import org.eclipse.jetty.server.handler.HandlerCollection;
+import org.eclipse.jetty.server.handler.RequestLogHandler;
+import org.eclipse.jetty.server.nio.SelectChannelConnector;
+import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
+import org.eclipse.jetty.servlet.FilterHolder;
+import org.eclipse.jetty.servlet.FilterMapping;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+
+import com.att.research.datarouter.provisioning.utils.DB;
+import com.att.research.datarouter.provisioning.utils.LogfileLoader;
+import com.att.research.datarouter.provisioning.utils.PurgeLogDirTask;
+import com.att.research.datarouter.provisioning.utils.ThrottleFilter;
+
+/**
+ * <p>
+ * A main class which may be used to start the provisioning server with an "embedded" Jetty server.
+ * Configuration is done via the properties file <i>provserver.properties</i>, which should be in the CLASSPATH.
+ * The provisioning server may also be packaged with a web.xml and started as a traditional webapp.
+ * </p>
+ * <p>
+ * Most of the work of the provisioning server is carried out within the eight servlets (configured below)
+ * that are used to handle each of the eight types of requests the server may receive.
+ * In addition, there are background threads started to perform other tasks:
+ * </p>
+ * <ul>
+ * <li>One background Thread runs the {@link LogfileLoader} in order to process incoming logfiles.
+ * This Thread is created as a side effect of the first successful POST to the /internal/logs/ servlet.</li>
+ * <li>One background Thread runs the {@link SynchronizerTask} which is used to periodically
+ * synchronize the database between active and standby servers.</li>
+ * <li>One background Thread runs the {@link Poker} which is used to notify the nodes whenever
+ * provisioning data changes.</li>
+ * <li>One task is run once a day to run {@link PurgeLogDirTask} which purges older logs from the
+ * /opt/app/datartr/logs directory.</li>
+ * </ul>
+ * <p>
+ * The provisioning server is stopped by issuing a GET to the URL http://127.0.0.1/internal/halt
+ * using <i>curl</i> or some other such tool.
+ * </p>
+ *
+ * @author Robert Eby
+ * @version $Id: Main.java,v 1.12 2014/03/12 19:45:41 eby Exp $
+ */
+public class Main {
+ /** The truststore to use if none is specified */
+ public static final String DEFAULT_TRUSTSTORE = "/opt/java/jdk/jdk180/jre/lib/security/cacerts";
+ public static final String KEYSTORE_TYPE_PROPERTY = "com.att.research.datarouter.provserver.keystore.type";
+ public static final String KEYSTORE_PATH_PROPERTY = "com.att.research.datarouter.provserver.keystore.path";
+ public static final String KEYSTORE_PASSWORD_PROPERTY = "com.att.research.datarouter.provserver.keystore.password";
+ public static final String TRUSTSTORE_PATH_PROPERTY = "com.att.research.datarouter.provserver.truststore.path";
+ public static final String TRUSTSTORE_PASSWORD_PROPERTY = "com.att.research.datarouter.provserver.truststore.password";
+
+ /** The one and only {@link Server} instance in this JVM */
+ private static Server server;
+
+ /**
+ * Starts the Data Router Provisioning server.
+ * @param args not used
+ * @throws Exception if Jetty has a problem starting
+ */
+ public static void main(String[] args) throws Exception {
+ Security.setProperty("networkaddress.cache.ttl", "4");
+ Logger logger = Logger.getLogger("com.att.research.datarouter.provisioning.internal");
+
+ // Check DB is accessible and contains the expected tables
+ if (! checkDatabase(logger))
+ System.exit(1);
+
+ logger.info("PROV0000 **** AT&T Data Router Provisioning Server starting....");
+
+ // Get properties
+ Properties p = (new DB()).getProperties();
+ int http_port = Integer.parseInt(p.getProperty("com.att.research.datarouter.provserver.http.port", "8080"));
+ int https_port = Integer.parseInt(p.getProperty("com.att.research.datarouter.provserver.https.port", "8443"));
+
+ // HTTP connector
+ SelectChannelConnector http = new SelectChannelConnector();
+ http.setPort(http_port);
+ http.setMaxIdleTime(300000);
+ http.setRequestHeaderSize(2048);
+ http.setAcceptors(2);
+ http.setConfidentialPort(https_port);
+ http.setLowResourcesConnections(20000);
+
+ // HTTPS connector
+ SslSelectChannelConnector https = new SslSelectChannelConnector();
+ https.setPort(https_port);
+ https.setMaxIdleTime(30000);
+ https.setRequestHeaderSize(8192);
+ https.setAcceptors(2);
+
+ // SSL stuff
+ SslContextFactory cf = https.getSslContextFactory();
+
+ /**Skip SSLv3 Fixes*/
+ cf.addExcludeProtocols("SSLv3");
+ logger.info("Excluded protocols prov-"+cf.getExcludeProtocols());
+ /**End of SSLv3 Fixes*/
+
+ cf.setKeyStoreType(p.getProperty(KEYSTORE_TYPE_PROPERTY, "jks"));
+ cf.setKeyStorePath(p.getProperty(KEYSTORE_PATH_PROPERTY));
+ cf.setKeyStorePassword(p.getProperty(KEYSTORE_PASSWORD_PROPERTY));
+ cf.setKeyManagerPassword(p.getProperty("com.att.research.datarouter.provserver.keymanager.password"));
+ String ts = p.getProperty(TRUSTSTORE_PATH_PROPERTY);
+ if (ts != null && ts.length() > 0) {
+ System.out.println("@@ TS -> "+ts);
+ cf.setTrustStore(ts);
+ cf.setTrustStorePassword(p.getProperty(TRUSTSTORE_PASSWORD_PROPERTY));
+ } else {
+ cf.setTrustStore(DEFAULT_TRUSTSTORE);
+ cf.setTrustStorePassword("changeit");
+ }
+ cf.setTrustStore("/opt/app/datartr/self_signed/cacerts.jks");
+ cf.setTrustStorePassword("changeit");
+ cf.setWantClientAuth(true);
+
+ // Servlet and Filter configuration
+ ServletContextHandler ctxt = new ServletContextHandler(0);
+ ctxt.setContextPath("/");
+ ctxt.addServlet(new ServletHolder(new FeedServlet()), "/feed/*");
+ ctxt.addServlet(new ServletHolder(new FeedLogServlet()), "/feedlog/*");
+ ctxt.addServlet(new ServletHolder(new PublishServlet()), "/publish/*");
+ ctxt.addServlet(new ServletHolder(new SubscribeServlet()), "/subscribe/*");
+ ctxt.addServlet(new ServletHolder(new StatisticsServlet()), "/statistics/*");
+ ctxt.addServlet(new ServletHolder(new SubLogServlet()), "/sublog/*");
+ ctxt.addServlet(new ServletHolder(new GroupServlet()), "/group/*"); //Provision groups - Rally US708115 -1610
+ ctxt.addServlet(new ServletHolder(new SubscriptionServlet()), "/subs/*");
+ ctxt.addServlet(new ServletHolder(new InternalServlet()), "/internal/*");
+ ctxt.addServlet(new ServletHolder(new RouteServlet()), "/internal/route/*");
+ ctxt.addServlet(new ServletHolder(new DRFeedsServlet()), "/");
+ ctxt.addFilter (new FilterHolder (new ThrottleFilter()), "/publish/*", FilterMapping.REQUEST);
+
+ ContextHandlerCollection contexts = new ContextHandlerCollection();
+ contexts.addHandler(ctxt);
+
+ // Request log configuration
+ NCSARequestLog nrl = new NCSARequestLog();
+ nrl.setFilename(p.getProperty("com.att.research.datarouter.provserver.accesslog.dir") + "/request.log.yyyy_mm_dd");
+ nrl.setFilenameDateFormat("yyyyMMdd");
+ nrl.setRetainDays(90);
+ nrl.setAppend(true);
+ nrl.setExtended(false);
+ nrl.setLogCookies(false);
+ nrl.setLogTimeZone("GMT");
+
+ RequestLogHandler reqlog = new RequestLogHandler();
+ reqlog.setRequestLog(nrl);
+
+ // Server's Handler collection
+ HandlerCollection hc = new HandlerCollection();
+ hc.setHandlers(new Handler[] { contexts, new DefaultHandler() });
+ hc.addHandler(reqlog);
+
+ // Server's thread pool
+ QueuedThreadPool pool = new QueuedThreadPool();
+ pool.setMinThreads(10);
+ pool.setMaxThreads(200);
+ pool.setDetailedDump(false);
+
+ // Daemon to clean up the log directory on a daily basis
+ Timer rolex = new Timer();
+ rolex.scheduleAtFixedRate(new PurgeLogDirTask(), 0, 86400000L); // run once per day
+
+ // Start LogfileLoader
+ LogfileLoader.getLoader();
+
+ // The server itself
+ server = new Server();
+ server.setThreadPool(pool);
+ server.setConnectors(new Connector[] { http, https });
+ server.setHandler(hc);
+ server.setStopAtShutdown(true);
+ server.setSendServerVersion(true);
+ server.setSendDateHeader(true);
+ server.setGracefulShutdown(5000); // allow 5 seconds for servlets to wrap up
+ server.setDumpAfterStart(false);
+ server.setDumpBeforeStop(false);
+
+ server.start();
+ server.join();
+ logger.info("PROV0001 **** AT&T Data Router Provisioning Server halted.");
+ }
+
+ private static boolean checkDatabase(Logger logger) {
+ DB db = new DB();
+ return db.runRetroFits();
+ }
+
+ /**
+ * Stop the Jetty server.
+ */
+ public static void shutdown() {
+ new Thread() {
+ @Override
+ public void run() {
+ try {
+ server.stop();
+ Thread.sleep(5000L);
+ System.exit(0);
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+ }.start();
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/Poker.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/Poker.java
new file mode 100644
index 00000000..13350df0
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/Poker.java
@@ -0,0 +1,318 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package com.att.research.datarouter.provisioning;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.TreeSet;
+
+import javax.servlet.ServletException;
+
+import org.apache.log4j.Logger;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+
+import com.att.research.datarouter.provisioning.beans.EgressRoute;
+import com.att.research.datarouter.provisioning.beans.Feed;
+import com.att.research.datarouter.provisioning.beans.IngressRoute;
+import com.att.research.datarouter.provisioning.beans.NetworkRoute;
+import com.att.research.datarouter.provisioning.beans.Parameters;
+import com.att.research.datarouter.provisioning.beans.Subscription;
+import com.att.research.datarouter.provisioning.beans.Group; //Groups feature Rally:US708115 - 1610
+import com.att.research.datarouter.provisioning.utils.*;
+
+/**
+ * This class handles the two timers (described in R1 Design Notes), and takes care of issuing
+ * the GET to each node of the URL to "poke".
+ *
+ * @author Robert Eby
+ * @version $Id: Poker.java,v 1.11 2014/01/08 16:13:47 eby Exp $
+ */
+public class Poker extends TimerTask {
+ /** Template used to generate the URL to issue the GET against */
+ public static final String POKE_URL_TEMPLATE = "http://%s/internal/fetchProv";
+
+
+
+
+ /** This is a singleton -- there is only one Poker object in the server */
+ private static Poker p;
+
+ /**
+ * Get the singleton Poker object.
+ * @return the Poker
+ */
+ public static synchronized Poker getPoker() {
+ if (p == null)
+ p = new Poker();
+ return p;
+ }
+
+ private long timer1;
+ private long timer2;
+ private Timer rolex;
+ private String this_pod; // DNS name of this machine
+ private Logger logger;
+ private String provstring;
+
+ private Poker() {
+ timer1 = timer2 = 0;
+ rolex = new Timer();
+ logger = Logger.getLogger("com.att.research.datarouter.provisioning.internal");
+ try {
+ this_pod = InetAddress.getLocalHost().getHostName();
+ } catch (UnknownHostException e) {
+ this_pod = "*UNKNOWN*"; // not a major problem
+ }
+ provstring = buildProvisioningString();
+
+ rolex.scheduleAtFixedRate(this, 0L, 1000L); // Run once a second to check the timers
+ }
+
+ /**
+ * This method sets the two timers described in the design notes.
+ * @param t1 the first timer controls how long to wait after a provisioning request before poking each node
+ * This timer can be reset if it has not "gone off".
+ * @param t2 the second timer set the outer bound on how long to wait. It cannot be reset.
+ */
+ public void setTimers(long t1, long t2) {
+ synchronized (this_pod) {
+ if (timer1 == 0 || t1 > timer1)
+ timer1 = t1;
+ if (timer2 == 0)
+ timer2 = t2;
+ }
+ if (logger.isDebugEnabled())
+ logger.debug("Poker timers set to " + timer1 + " and " + timer2);
+
+
+ }
+
+ /**
+ * Return the last provisioning string built.
+ * @return the last provisioning string built.
+ */
+ public String getProvisioningString() {
+ return provstring;
+ }
+
+ /**
+ * The method to run at the predefined interval (once per second). This method checks
+ * to see if either of the two timers has expired, and if so, will rebuild the provisioning
+ * string, and poke all the nodes and other PODs. The timers are then reset to 0.
+ */
+ @Override
+ public void run() {
+ try {
+ if (timer1 > 0) {
+ long now = System.currentTimeMillis();
+ boolean fire = false;
+ synchronized (this_pod) {
+ if (now > timer1 || now > timer2) {
+ timer1 = timer2 = 0;
+ fire = true;
+ }
+ }
+ if (fire) {
+ // Rebuild the prov string
+ provstring = buildProvisioningString();
+
+ // Only the active POD should poke nodes, etc.
+ boolean active = SynchronizerTask.getSynchronizer().isActive();
+ if (active) {
+ // Poke all the DR nodes
+ for (String n : BaseServlet.getNodes()) {
+ pokeNode(n);
+ }
+ // Poke the pod that is not us
+ for (String n : BaseServlet.getPods()) {
+ if (n.length() > 0 && !n.equals(this_pod))
+ pokeNode(n);
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ logger.warn("PROV0020: Caught exception in Poker: "+e);
+ e.printStackTrace();
+ }
+ }
+ private void pokeNode(final String nodename) {
+ logger.debug("PROV0012 Poking node " + nodename + " ...");
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+
+ try {
+ String u = String.format(POKE_URL_TEMPLATE, nodename+":"+DB.HTTP_PORT);
+ URL url = new URL(u);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setConnectTimeout(60000); //Fixes for Itrack DATARTR-3, poke timeout
+ conn.connect();
+ conn.getContentLength(); // Force the GET through
+ conn.disconnect();
+ } catch (MalformedURLException e) {
+ logger.warn("PROV0013 MalformedURLException Error poking node "+nodename+": " + e.getMessage());
+ } catch (IOException e) {
+ logger.warn("PROV0013 IOException Error poking node "+nodename+": " + e.getMessage());
+ }
+ }
+ };
+// Thread t = new Thread(r);
+// t.start();
+ r.run();
+ }
+ @SuppressWarnings("unused")
+ private String buildProvisioningString() {
+ StringBuilder sb = new StringBuilder("{\n");
+
+ // Append Feeds to the string
+ String pfx = "\n";
+ sb.append("\"feeds\": [");
+ for (Feed f : Feed.getAllFeeds()) {
+ sb.append(pfx);
+ sb.append(f.asJSONObject().toString());
+ pfx = ",\n";
+ }
+ sb.append("\n],\n");
+
+ //Append groups to the string - Rally:US708115 - 1610
+ pfx = "\n";
+ sb.append("\"groups\": [");
+ for (Group s : Group.getAllgroups()) {
+ sb.append(pfx);
+ sb.append(s.asJSONObject().toString());
+ pfx = ",\n";
+ }
+ sb.append("\n],\n");
+
+
+ // Append Subscriptions to the string
+ pfx = "\n";
+ sb.append("\"subscriptions\": [");
+ for (Subscription s : Subscription.getAllSubscriptions()) {
+ sb.append(pfx);
+ if(s!=null)
+ sb.append(s.asJSONObject().toString());
+ pfx = ",\n";
+ }
+ sb.append("\n],\n");
+
+ // Append Parameters to the string
+ pfx = "\n";
+ sb.append("\"parameters\": {");
+ Map<String,String> props = Parameters.getParameters();
+ Set<String> ivals = new HashSet<String>();
+ String intv = props.get("_INT_VALUES");
+ if (intv != null)
+ ivals.addAll(Arrays.asList(intv.split("\\|")));
+ for (String key : new TreeSet<String>(props.keySet())) {
+ String v = props.get(key);
+ sb.append(pfx);
+ sb.append(" \"").append(key).append("\": ");
+ if (ivals.contains(key)) {
+ // integer value
+ sb.append(v);
+ } else if (key.endsWith("S")) {
+ // Split and append array of strings
+ String[] pp = v.split("\\|");
+ String p2 = "";
+ sb.append("[");
+ for (String t : pp) {
+ sb.append(p2).append("\"").append(quote(t)).append("\"");
+ p2 = ",";
+ }
+ sb.append("]");
+ } else {
+ sb.append("\"").append(quote(v)).append("\"");
+ }
+ pfx = ",\n";
+ }
+ sb.append("\n},\n");
+
+ // Append Routes to the string
+ pfx = "\n";
+ sb.append("\"ingress\": [");
+ for (IngressRoute in : IngressRoute.getAllIngressRoutes()) {
+ sb.append(pfx);
+ sb.append(in.asJSONObject().toString());
+ pfx = ",\n";
+ }
+ sb.append("\n],\n");
+
+ pfx = "\n";
+ sb.append("\"egress\": {");
+ for (EgressRoute eg : EgressRoute.getAllEgressRoutes()) {
+ sb.append(pfx);
+ String t = eg.asJSONObject().toString();
+ t = t.substring(1, t.length()-1);
+ sb.append(t);
+ pfx = ",\n";
+ }
+ sb.append("\n},\n");
+
+ pfx = "\n";
+ sb.append("\"routing\": [");
+ for (NetworkRoute ne : NetworkRoute.getAllNetworkRoutes()) {
+ sb.append(pfx);
+ sb.append(ne.asJSONObject().toString());
+ pfx = ",\n";
+ }
+ sb.append("\n]");
+ sb.append("\n}");
+
+ // Convert to string and verify it is valid JSON
+ String provstring = sb.toString();
+ try {
+ new JSONObject(new JSONTokener(provstring));
+ } catch (JSONException e) {
+ logger.warn("PROV0016: Possible invalid prov string: "+e);
+ }
+ return provstring;
+ }
+ private String quote(String s) {
+ StringBuilder sb = new StringBuilder();
+ for (char ch : s.toCharArray()) {
+ if (ch == '\\' || ch == '"') {
+ sb.append('\\');
+ }
+ sb.append(ch);
+ }
+ return sb.toString();
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/ProxyServlet.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/ProxyServlet.java
new file mode 100644
index 00000000..b22b018f
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/ProxyServlet.java
@@ -0,0 +1,304 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.entity.BasicHttpEntity;
+import org.apache.http.impl.client.AbstractHttpClient;
+import org.apache.http.impl.client.DefaultHttpClient;
+
+import com.att.research.datarouter.provisioning.utils.DB;
+import com.att.research.datarouter.provisioning.utils.URLUtilities;
+
+/**
+ * This class is the base class for those servlets that need to proxy their requests from the
+ * standby to active server. Its methods perform the proxy function to the active server. If the
+ * active server is not reachable, a 503 (SC_SERVICE_UNAVAILABLE) is returned. Only
+ * DELETE/GET/PUT/POST are supported.
+ *
+ * @author Robert Eby
+ * @version $Id: ProxyServlet.java,v 1.3 2014/03/24 18:47:10 eby Exp $
+ */
+@SuppressWarnings("serial")
+public class ProxyServlet extends BaseServlet {
+ private boolean inited = false;
+ private Scheme sch;
+
+ /**
+ * Initialize this servlet, by setting up SSL.
+ */
+ @SuppressWarnings("deprecation")
+ @Override
+ public void init(ServletConfig config) throws ServletException {
+ super.init(config);
+ try {
+ // Set up keystore
+ Properties props = (new DB()).getProperties();
+ String type = props.getProperty(Main.KEYSTORE_TYPE_PROPERTY, "jks");
+ String store = props.getProperty(Main.KEYSTORE_PATH_PROPERTY);
+ String pass = props.getProperty(Main.KEYSTORE_PASSWORD_PROPERTY);
+ KeyStore keyStore = readStore(store, pass, type);
+
+ store = props.getProperty(Main.TRUSTSTORE_PATH_PROPERTY);
+ pass = props.getProperty(Main.TRUSTSTORE_PASSWORD_PROPERTY);
+ if (store == null || store.length() == 0) {
+ store = Main.DEFAULT_TRUSTSTORE;
+ pass = "changeit";
+ }
+ KeyStore trustStore = readStore(store, pass, KeyStore.getDefaultType());
+
+ // We are connecting with the node name, but the certificate will have the CNAME
+ // So we need to accept a non-matching certificate name
+ SSLSocketFactory socketFactory = new SSLSocketFactory(keyStore, "changeit", trustStore);
+ socketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+ sch = new Scheme("https", 443, socketFactory);
+ inited = true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ intlogger.info("ProxyServlet: inited = "+inited);
+ }
+ private KeyStore readStore(String store, String pass, String type) throws KeyStoreException, FileNotFoundException {
+ KeyStore ks = KeyStore.getInstance(type);
+ FileInputStream instream = new FileInputStream(new File(store));
+ try {
+ ks.load(instream, pass.toCharArray());
+ } catch (Exception x) {
+ System.err.println("READING TRUSTSTORE: "+x);
+ } finally {
+ try { instream.close(); } catch (Exception ignore) {}
+ }
+ return ks;
+ }
+ /**
+ * Return <i>true</i> if the requester has NOT set the <i>noproxy</i> CGI variable.
+ * If they have, this indicates they want to forcibly turn the proxy off.
+ * @param req the HTTP request
+ * @return true or false
+ */
+ protected boolean isProxyOK(final HttpServletRequest req) {
+ String t = req.getQueryString();
+ if (t != null) {
+ t = t.replaceAll("&amp;", "&");
+ for (String s : t.split("&")) {
+ if (s.equals("noproxy") || s.startsWith("noproxy="))
+ return false;
+ }
+ }
+ return true;
+ }
+ /**
+ * Is this the standby server? If it is, the proxy functions can be used.
+ * If not, the proxy functions should not be called, and will send a response of 500
+ * (Internal Server Error).
+ * @return true if this server is the standby (and hence a proxy server).
+ */
+ public boolean isProxyServer() {
+ SynchronizerTask st = SynchronizerTask.getSynchronizer();
+ return st.getState() == SynchronizerTask.STANDBY;
+ }
+ /**
+ * Issue a proxy DELETE to the active provisioning server.
+ */
+ @Override
+ public void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ doProxy(req, resp, "DELETE");
+ }
+ /**
+ * Issue a proxy GET to the active provisioning server.
+ */
+ @Override
+ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ doProxy(req, resp, "GET");
+ }
+ /**
+ * Issue a proxy PUT to the active provisioning server.
+ */
+ @Override
+ public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ doProxy(req, resp, "PUT");
+ }
+ /**
+ * Issue a proxy POST to the active provisioning server.
+ */
+ @Override
+ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ doProxy(req, resp, "POST");
+ }
+ /**
+ * Issue a proxy GET to the active provisioning server. Unlike doGet() above,
+ * this method will allow the caller to fall back to other code if the remote server is unreachable.
+ * @return true if the proxy succeeded
+ */
+ public boolean doGetWithFallback(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ boolean rv = false;
+ if (inited) {
+ String url = buildUrl(req);
+ intlogger.info("ProxyServlet: proxying with fallback GET "+url);
+ AbstractHttpClient httpclient = new DefaultHttpClient();
+ HttpRequestBase proxy = new HttpGet(url);
+ try {
+ httpclient.getConnectionManager().getSchemeRegistry().register(sch);
+
+ // Copy request headers and request body
+ copyRequestHeaders(req, proxy);
+
+ // Execute the request
+ HttpResponse pxy_response = httpclient.execute(proxy);
+
+ // Get response headers and body
+ int code = pxy_response.getStatusLine().getStatusCode();
+ resp.setStatus(code);
+ copyResponseHeaders(pxy_response, resp);
+
+ HttpEntity entity = pxy_response.getEntity();
+ if (entity != null) {
+ InputStream in = entity.getContent();
+ IOUtils.copy(in, resp.getOutputStream());
+ in.close();
+ }
+ rv = true;
+ } catch (IOException e) {
+ System.err.println("ProxyServlet: "+e);
+ e.printStackTrace();
+ } finally {
+ proxy.releaseConnection();
+ httpclient.getConnectionManager().shutdown();
+ }
+ } else {
+ intlogger.warn("ProxyServlet: proxy disabled");
+ }
+ return rv;
+ }
+ private void doProxy(HttpServletRequest req, HttpServletResponse resp, final String method) throws IOException {
+ if (inited && isProxyServer()) {
+ String url = buildUrl(req);
+ intlogger.info("ProxyServlet: proxying "+method + " "+url);
+ AbstractHttpClient httpclient = new DefaultHttpClient();
+ ProxyHttpRequest proxy = new ProxyHttpRequest(method, url);
+ try {
+ httpclient.getConnectionManager().getSchemeRegistry().register(sch);
+
+ // Copy request headers and request body
+ copyRequestHeaders(req, proxy);
+ if (method.equals("POST") || method.equals("PUT")){
+ BasicHttpEntity body = new BasicHttpEntity();
+ body.setContent(req.getInputStream());
+ body.setContentLength(-1); // -1 = unknown
+ proxy.setEntity(body);
+ }
+
+ // Execute the request
+ HttpResponse pxy_response = httpclient.execute(proxy);
+
+ // Get response headers and body
+ int code = pxy_response.getStatusLine().getStatusCode();
+ resp.setStatus(code);
+ copyResponseHeaders(pxy_response, resp);
+
+ HttpEntity entity = pxy_response.getEntity();
+ if (entity != null) {
+ InputStream in = entity.getContent();
+ IOUtils.copy(in, resp.getOutputStream());
+ in.close();
+ }
+ } catch (IOException e) {
+ intlogger.warn("ProxyServlet: "+e);
+ resp.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
+ e.printStackTrace();
+ } finally {
+ proxy.releaseConnection();
+ httpclient.getConnectionManager().shutdown();
+ }
+ } else {
+ intlogger.warn("ProxyServlet: proxy disabled");
+ resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+ }
+ private String buildUrl(HttpServletRequest req) {
+ StringBuilder sb = new StringBuilder("https://");
+ sb.append(URLUtilities.getPeerPodName());
+ sb.append(req.getRequestURI());
+ String q = req.getQueryString();
+ if (q != null)
+ sb.append("?").append(q);
+ return sb.toString();
+ }
+ private void copyRequestHeaders(HttpServletRequest from, HttpRequestBase to) {
+ @SuppressWarnings("unchecked")
+ List<String> list = Collections.list(from.getHeaderNames());
+ for (String name : list) {
+ // Proxy code will add this one
+ if (!name.equalsIgnoreCase("Content-Length"))
+ to.addHeader(name, from.getHeader(name));
+ }
+ }
+ private void copyResponseHeaders(HttpResponse from, HttpServletResponse to) {
+ for (Header hdr : from.getAllHeaders()) {
+ // Don't copy Date: our Jetty will add another Date header
+ if (!hdr.getName().equals("Date"))
+ to.addHeader(hdr.getName(), hdr.getValue());
+ }
+ }
+
+ public class ProxyHttpRequest extends HttpEntityEnclosingRequestBase {
+ private final String method;
+
+ public ProxyHttpRequest(final String method, final String uri) {
+ super();
+ this.method = method;
+ setURI(URI.create(uri));
+ }
+ @Override
+ public String getMethod() {
+ return method;
+ }
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/PublishServlet.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/PublishServlet.java
new file mode 100644
index 00000000..2a8e2e3c
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/PublishServlet.java
@@ -0,0 +1,192 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Properties;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+import com.att.research.datarouter.provisioning.beans.EventLogRecord;
+import com.att.research.datarouter.provisioning.beans.Feed;
+import com.att.research.datarouter.provisioning.beans.IngressRoute;
+import com.att.research.datarouter.provisioning.eelf.EelfMsgs;
+import com.att.research.datarouter.provisioning.utils.*;
+
+/**
+ * This servlet handles redirects for the &lt;publishURL&gt; on the provisioning server,
+ * which is generated by the provisioning server to handle a particular subscriptions to a feed.
+ * See the <b>File Publishing and Delivery API</b> document for details on how these methods
+ * should be invoked.
+ *
+ * @author Robert Eby
+ * @version $Id: PublishServlet.java,v 1.8 2014/03/12 19:45:41 eby Exp $
+ */
+@SuppressWarnings("serial")
+public class PublishServlet extends BaseServlet {
+ private int next_node;
+ private String provstring;
+ private List<IngressRoute> irt;
+ //Adding EELF Logger Rally:US664892
+ private static EELFLogger eelflogger = EELFManager.getInstance().getLogger("com.att.research.datarouter.provisioning.PublishServlet");
+
+
+ @Override
+ public void init(ServletConfig config) throws ServletException {
+ super.init(config);
+ next_node = 0;
+ provstring = "";
+ irt = new ArrayList<IngressRoute>();
+
+ }
+ @Override
+ public void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doDelete");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+"");
+ redirect(req, resp);
+ }
+ @Override
+ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doGet");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+"");
+ redirect(req, resp);
+ }
+ @Override
+ public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doPut");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_FEEDID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+"");
+ redirect(req, resp);
+ }
+ @Override
+ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doPost");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF, req.getHeader(BEHALF_HEADER));
+ redirect(req, resp);
+ }
+ private void redirect(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ String[] nodes = getNodes();
+ if (nodes == null || nodes.length == 0) {
+ resp.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "There are no nodes defined in the DR network.");
+ } else {
+ EventLogRecord elr = new EventLogRecord(req);
+ int feedid = checkPath(req);
+ if (feedid < 0) {
+ String message = (feedid == -1)
+ ? "Invalid request - Missing or bad feed number."
+ : "Invalid request - Missing file ID.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_NOT_FOUND);
+ eventlogger.info(elr);
+
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, message);
+ } else {
+ // Generate new URL
+ String nextnode = getRedirectNode(feedid, req);
+ nextnode = nextnode+":"+DB.HTTPS_PORT;
+ String newurl = "https://" + nextnode + "/publish" + req.getPathInfo();
+ String qs = req.getQueryString();
+ if (qs != null)
+ newurl += "?" + qs;
+
+ // Log redirect in event log
+ String message = "Redirected to: "+newurl;
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_MOVED_PERMANENTLY);
+ eventlogger.info(elr);
+
+ resp.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
+ resp.setHeader("Location", newurl);
+ }
+ }
+ }
+ private String getRedirectNode(int feedid, HttpServletRequest req) {
+ // Check to see if the IRT needs to be updated
+ Poker p = Poker.getPoker();
+ String s = p.getProvisioningString();
+ synchronized (provstring) {
+ if (irt == null || (s.length() != provstring.length()) || !s.equals(provstring)) {
+ // Provisioning string has changed -- update the IRT
+ provstring = s;
+ JSONObject jo = new JSONObject(new JSONTokener(provstring));
+ JSONArray ja = jo.getJSONArray("ingress");
+ List<IngressRoute> newlist = new ArrayList<IngressRoute>();
+ for (int i = 0; i < ja.length(); i++) {
+ IngressRoute iroute = new IngressRoute(ja.getJSONObject(i));
+ newlist.add(iroute);
+ }
+ irt = newlist;
+ }
+ }
+
+ // Look in IRT for next node
+ for (IngressRoute route : irt) {
+ if (route.matches(feedid, req)) {
+ // pick a node at random from the list
+ Collection<String> nodes = route.getNodes();
+ String[] arr = nodes.toArray(new String[0]);
+ long id = System.currentTimeMillis() % arr.length;
+ String node = arr[(int) id];
+ intlogger.info("Redirecting to "+node+" because of route "+route);
+ return node;
+ }
+ }
+
+ // No IRT rule matches, do round robin of all active nodes
+ String[] nodes = getNodes();
+ if (next_node >= nodes.length) // The list of nodes may have grown/shrunk
+ next_node = 0;
+ return nodes[next_node++];
+ }
+ private int checkPath(HttpServletRequest req) {
+ String path = req.getPathInfo();
+ if (path == null || path.length() < 2)
+ return -1;
+ path = path.substring(1);
+ int ix = path.indexOf('/');
+ if (ix < 0 || ix == path.length()-1)
+ return -2;
+ try {
+ int feedid = Integer.parseInt(path.substring(0, ix));
+ if (!Feed.isFeedValid(feedid))
+ return -1;
+ return feedid;
+ } catch (NumberFormatException e) {
+ return -1;
+ }
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/RouteServlet.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/RouteServlet.java
new file mode 100644
index 00000000..68fd4c71
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/RouteServlet.java
@@ -0,0 +1,429 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning;
+
+import java.io.IOException;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.json.JSONObject;
+
+import com.att.research.datarouter.provisioning.beans.Deleteable;
+import com.att.research.datarouter.provisioning.beans.EgressRoute;
+import com.att.research.datarouter.provisioning.beans.EventLogRecord;
+import com.att.research.datarouter.provisioning.beans.IngressRoute;
+import com.att.research.datarouter.provisioning.beans.Insertable;
+import com.att.research.datarouter.provisioning.beans.NetworkRoute;
+import com.att.research.datarouter.provisioning.beans.NodeClass;
+
+/**
+ * <p>
+ * This servlet handles requests to URLs under /internal/route/ on the provisioning server.
+ * This part of the URL tree is used to manipulate the Data Router routing tables.
+ * These include:
+ * </p>
+ * <div class="contentContainer">
+ * <table class="packageSummary" border="0" cellpadding="3" cellspacing="0">
+ * <caption><span>URL Path Summary</span><span class="tabEnd">&nbsp;</span></caption>
+ * <tr>
+ * <th class="colFirst" width="35%">URL Path</th>
+ * <th class="colOne">Method</th>
+ * <th class="colLast">Purpose</th>
+ * </tr>
+ * <tr class="altColor">
+ * <td class="colFirst">/internal/route/</td>
+ * <td class="colOne">GET</td>
+ * <td class="colLast">used to GET a full JSON copy of all three routing tables.</td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td class="colFirst" rowspan="2">/internal/route/ingress/</td>
+ * <td class="colOne">GET</td>
+ * <td class="colLast">used to GET a full JSON copy of the ingress routing table (IRT).</td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td class="colOne">POST</td>
+ * <td class="colLast">used to create a new entry in the ingress routing table (IRT).</td></tr>
+ * <tr class="altColor">
+ * <td class="colFirst" rowspan="2">/internal/route/egress/</td>
+ * <td class="colOne">GET</td>
+ * <td class="colLast">used to GET a full JSON copy of the egress routing table (ERT).</td>
+ * </tr>
+ * <tr class="altColor">
+ * <td class="colOne">POST</td>
+ * <td class="colLast">used to create a new entry in the egress routing table (ERT).</td></tr>
+ * <tr class="rowColor">
+ * <td class="colFirst" rowspan="2">/internal/route/network/</td>
+ * <td class="colOne">GET</td>
+ * <td class="colLast">used to GET a full JSON copy of the network routing table (NRT).</td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td class="colOne">POST</td>
+ * <td class="colLast">used to create a new entry in the network routing table (NRT).</td>
+ * </tr>
+ * <tr class="altColor">
+ * <td class="colFirst">/internal/route/ingress/&lt;feed&gt;/&lt;user&gt;/&lt;subnet&gt;</td>
+ * <td class="colOne">DELETE</td>
+ * <td class="colLast">used to DELETE the ingress route corresponding to <i>feed</i>, <i>user</i> and <i>subnet</i>.
+ * The / in the subnet specified should be replaced with a !, since / cannot be used in a URL.</td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td class="colFirst">/internal/route/ingress/&lt;seq&gt;</td>
+ * <td class="colOne">DELETE</td>
+ * <td class="colLast">used to DELETE all ingress routes with the matching <i>seq</i> sequence number.</td>
+ * </tr>
+ * <tr class="altColor">
+ * <td class="colFirst">/internal/route/egress/&lt;sub&gt;</td>
+ * <td class="colOne">DELETE</td>
+ * <td class="colLast">used to DELETE the egress route the matching <i>sub</i> subscriber number.</td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td class="colFirst">/internal/route/network/&lt;fromnode&gt;/&lt;tonode&gt;</td>
+ * <td class="colOne">DELETE</td>
+ * <td class="colLast">used to DELETE the network route corresponding to <i>fromnode</i>
+ * and <i>tonode</i>.</td>
+ * </tr>
+ * </table>
+ * <p>
+ * Authorization to use these URLs is a little different than for other URLs on the provisioning server.
+ * For the most part, the IP address that the request comes from should be either:
+ * </p>
+ * <ol>
+ * <li>an IP address of a provisioning server, or</li>
+ * <li>the IP address of a node, or</li>
+ * <li>an IP address from the "<i>special subnet</i>" which is configured with
+ * the PROV_SPECIAL_SUBNET parameter.
+ * </ol>
+ * <p>
+ * All DELETE/GET/POST requests made to this servlet on the standby server are proxied to the
+ * active server (using the {@link ProxyServlet}) if it is up and reachable.
+ * </p>
+ *
+ * @author Robert Eby
+ * @version $Id$
+ */
+@SuppressWarnings("serial")
+public class RouteServlet extends ProxyServlet {
+ /**
+ * DELETE route table entries by deleting part of the route table tree.
+ */
+ @Override
+ public void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ EventLogRecord elr = new EventLogRecord(req);
+ if (!isAuthorizedForInternal(req)) {
+ elr.setMessage("Unauthorized.");
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Unauthorized.");
+ return;
+ }
+ if (isProxyOK(req) && isProxyServer()) {
+ super.doDelete(req, resp);
+ return;
+ }
+
+ String path = req.getPathInfo();
+ String[] parts = path.substring(1).split("/");
+ Deleteable[] d = null;
+ if (parts[0].equals("ingress")) {
+ if (parts.length == 4) {
+ // /internal/route/ingress/<feed>/<user>/<subnet>
+ try {
+ int feedid = Integer.parseInt(parts[1]);
+ IngressRoute er = IngressRoute.getIngressRoute(feedid, parts[2], parts[3].replaceAll("!", "/"));
+ if (er == null) {
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, "The specified ingress route does not exist.");
+ return;
+ }
+ d = new Deleteable[] { er };
+ } catch (NumberFormatException e) {
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Invalid feed ID in 'delete ingress' command.");
+ return;
+ }
+ } else if (parts.length == 2) {
+ // /internal/route/ingress/<seq>
+ try {
+ int seq = Integer.parseInt(parts[1]);
+ Set<IngressRoute> set = IngressRoute.getIngressRoutesForSeq(seq);
+ d = set.toArray(new Deleteable[0]);
+ } catch (NumberFormatException e) {
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Invalid sequence number in 'delete ingress' command.");
+ return;
+ }
+ } else {
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Invalid number of arguments in 'delete ingress' command.");
+ return;
+ }
+ } else if (parts[0].equals("egress")) {
+ if (parts.length == 2) {
+ // /internal/route/egress/<sub>
+ try {
+ int subid = Integer.parseInt(parts[1]);
+ EgressRoute er = EgressRoute.getEgressRoute(subid);
+ if (er == null) {
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, "The specified egress route does not exist.");
+ return;
+ }
+ d = new Deleteable[] { er };
+ } catch (NumberFormatException e) {
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Invalid sub ID in 'delete egress' command.");
+ return;
+ }
+ } else {
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Invalid number of arguments in 'delete egress' command.");
+ return;
+ }
+ } else if (parts[0].equals("network")) {
+ if (parts.length == 3) {
+ // /internal/route/network/<from>/<to>
+ try {//
+ NetworkRoute nr = new NetworkRoute(
+ NodeClass.normalizeNodename(parts[1]),
+ NodeClass.normalizeNodename(parts[2])
+ );
+ d = new Deleteable[] { nr };
+ } catch (IllegalArgumentException e) {
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, "The specified network route does not exist.");
+ return;
+ }
+ } else {
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Invalid number of arguments in 'delete network' command.");
+ return;
+ }
+ }
+ if (d == null) {
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Bad URL.");
+ return;
+ }
+ boolean rv = true;
+ for (Deleteable dd : d) {
+ rv &= doDelete(dd);
+ }
+ if (rv) {
+ elr.setResult(HttpServletResponse.SC_OK);
+ eventlogger.info(elr);
+ resp.setStatus(HttpServletResponse.SC_OK);
+ provisioningDataChanged();
+ provisioningParametersChanged();
+ } else {
+ // Something went wrong with the DELETE
+ elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG);
+ }
+ }
+ /**
+ * GET route table entries from the route table tree specified by the URL path.
+ */
+ @Override
+ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ EventLogRecord elr = new EventLogRecord(req);
+ if (!isAuthorizedForInternal(req)) {
+ elr.setMessage("Unauthorized.");
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Unauthorized.");
+ return;
+ }
+ if (isProxyOK(req) && isProxyServer()) {
+ super.doGet(req, resp);
+ return;
+ }
+
+ String path = req.getPathInfo();
+ if (!path.endsWith("/"))
+ path += "/";
+ if (!path.equals("/") && !path.equals("/ingress/") && !path.equals("/egress/") && !path.equals("/network/")) {
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Bad URL.");
+ return;
+ }
+
+ StringBuilder sb = new StringBuilder("{\n");
+ String px2 = "";
+ if (path.equals("/") || path.equals("/ingress/")) {
+ String pfx = "\n";
+ sb.append("\"ingress\": [");
+ for (IngressRoute in : IngressRoute.getAllIngressRoutes()) {
+ sb.append(pfx);
+ sb.append(in.asJSONObject().toString());
+ pfx = ",\n";
+ }
+ sb.append("\n]");
+ px2 = ",\n";
+ }
+
+ if (path.equals("/") || path.equals("/egress/")) {
+ String pfx = "\n";
+ sb.append(px2);
+ sb.append("\"egress\": {");
+ for (EgressRoute eg : EgressRoute.getAllEgressRoutes()) {
+ JSONObject jx = eg.asJSONObject();
+ for (String key : jx.keySet()) {
+ sb.append(pfx);
+ sb.append(" \"").append(key).append("\": ");
+ sb.append("\"").append(jx.getString(key)).append("\"");
+ pfx = ",\n";
+ }
+ }
+ sb.append("\n}");
+ px2 = ",\n";
+ }
+
+ if (path.equals("/") || path.equals("/network/")) {
+ String pfx = "\n";
+ sb.append(px2);
+ sb.append("\"routing\": [");
+ for (NetworkRoute ne : NetworkRoute.getAllNetworkRoutes()) {
+ sb.append(pfx);
+ sb.append(ne.asJSONObject().toString());
+ pfx = ",\n";
+ }
+ sb.append("\n]");
+ }
+ sb.append("}\n");
+ resp.setStatus(HttpServletResponse.SC_OK);
+ resp.setContentType("application/json");
+ resp.getOutputStream().print(sb.toString());
+ }
+ /**
+ * PUT on &lt;/internal/route/*&gt; -- not supported.
+ */
+ @Override
+ public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ EventLogRecord elr = new EventLogRecord(req);
+ if (!isAuthorizedForInternal(req)) {
+ elr.setMessage("Unauthorized.");
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Unauthorized.");
+ return;
+ }
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Bad URL.");
+ }
+ /**
+ * POST - modify existing route table entries in the route table tree specified by the URL path.
+ */
+ @Override
+ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ EventLogRecord elr = new EventLogRecord(req);
+ if (!isAuthorizedForInternal(req)) {
+ elr.setMessage("Unauthorized.");
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Unauthorized.");
+ return;
+ }
+ if (isProxyOK(req) && isProxyServer()) {
+ super.doPost(req, resp);
+ return;
+ }
+ String path = req.getPathInfo();
+ Insertable[] ins = null;
+ if (path.startsWith("/ingress/")) {
+ // /internal/route/ingress/?feed=%s&amp;user=%s&amp;subnet=%s&amp;nodepatt=%s
+ try {
+ // Although it probably doesn't make sense, you can install two identical routes in the IRT
+ int feedid = Integer.parseInt(req.getParameter("feed"));
+ String user = req.getParameter("user");
+ if (user == null)
+ user = "-";
+ String subnet = req.getParameter("subnet");
+ if (subnet == null)
+ subnet = "-";
+ String nodepatt = req.getParameter("nodepatt");
+ String t = req.getParameter("seq");
+ int seq = (t != null) ? Integer.parseInt(t) : (IngressRoute.getMaxSequence() + 100);
+ ins = new Insertable[] { new IngressRoute(seq, feedid, user, subnet, NodeClass.lookupNodeNames(nodepatt)) };
+ } catch (Exception e) {
+ intlogger.info(e);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid arguments in 'add ingress' command.");
+ return;
+ }
+ } else if (path.startsWith("/egress/")) {
+ // /internal/route/egress/?sub=%s&amp;node=%s
+ try {
+ int subid = Integer.parseInt(req.getParameter("sub"));
+ EgressRoute er = EgressRoute.getEgressRoute(subid);
+ if (er != null) {
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "An egress route already exists for that subscriber.");
+ return;
+ }
+ String node = NodeClass.normalizeNodename(req.getParameter("node"));
+ ins = new Insertable[] { new EgressRoute(subid, node) };
+ } catch (Exception e) {
+ intlogger.info(e);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid arguments in 'add egress' command.");
+ return;
+ }
+ } else if (path.startsWith("/network/")) {
+ // /internal/route/network/?from=%s&amp;to=%s&amp;via=%s
+ try {
+ String nfrom = req.getParameter("from");
+ String nto = req.getParameter("to");
+ String nvia = req.getParameter("via");
+ if (nfrom == null || nto == null || nvia == null) {
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Missing arguments in 'add network' command.");
+ return;
+ }
+ nfrom = NodeClass.normalizeNodename(nfrom);
+ nto = NodeClass.normalizeNodename(nto);
+ nvia = NodeClass.normalizeNodename(nvia);
+ NetworkRoute nr = new NetworkRoute(nfrom, nto, nvia);
+ for (NetworkRoute route : NetworkRoute.getAllNetworkRoutes()) {
+ if (route.getFromnode() == nr.getFromnode() && route.getTonode() == nr.getTonode()) {
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Network route table already contains a route for "+nfrom+" and "+nto);
+ return;
+ }
+ }
+ ins = new Insertable[] { nr };
+ } catch (IllegalArgumentException e) {
+ intlogger.info(e);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid arguments in 'add network' command.");
+ return;
+ }
+ }
+ if (ins == null) {
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Bad URL.");
+ return;
+ }
+ boolean rv = true;
+ for (Insertable dd : ins) {
+ rv &= doInsert(dd);
+ }
+ if (rv) {
+ elr.setResult(HttpServletResponse.SC_OK);
+ eventlogger.info(elr);
+ resp.setStatus(HttpServletResponse.SC_OK);
+ provisioningDataChanged();
+ provisioningParametersChanged();
+ } else {
+ // Something went wrong with the INSERT
+ elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG);
+ }
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/StatisticsServlet.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/StatisticsServlet.java
new file mode 100644
index 00000000..1c508b77
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/StatisticsServlet.java
@@ -0,0 +1,588 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package com.att.research.datarouter.provisioning;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TimeZone;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.json.JSONException;
+import org.json.LOGJSONObject;
+import com.att.research.datarouter.provisioning.beans.EventLogRecord;
+import com.att.research.datarouter.provisioning.utils.DB;
+
+/**
+ * This Servlet handles requests to the &lt;Statistics API&gt; and &lt;Statistics consilidated resultset&gt;,
+ * @author Manish Singh
+ * @version $Id: StatisticsServlet.java,v 1.11 2016/08/10 17:27:02 Manish Exp $
+ */
+@SuppressWarnings("serial")
+
+public class StatisticsServlet extends BaseServlet {
+
+ private static final long TWENTYFOUR_HOURS = (24 * 60 * 60 * 1000L);
+ private static final String fmt1 = "yyyy-MM-dd'T'HH:mm:ss'Z'";
+ private static final String fmt2 = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
+
+
+ /**
+ * DELETE a logging URL -- not supported.
+ */
+ @Override
+ public void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ String message = "DELETE not allowed for the logURL.";
+ EventLogRecord elr = new EventLogRecord(req);
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, message);
+ }
+ /**
+ * GET a Statistics URL -- retrieve Statistics data for a feed or subscription.
+ * See the <b>Statistics API</b> document for details on how this method should be invoked.
+ */
+ @Override
+ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+
+ Map<String, String> map = buildMapFromRequest(req);
+ if (map.get("err") != null) {
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid arguments: "+map.get("err"));
+ return;
+ }
+ // check Accept: header??
+
+ resp.setStatus(HttpServletResponse.SC_OK);
+ resp.setContentType(LOGLIST_CONTENT_TYPE);
+ ServletOutputStream out = resp.getOutputStream();
+
+
+ String outputType = "json";
+ String feedids = null;
+
+ if(req.getParameter("feedid") ==null && req.getParameter("groupid") ==null)
+ {
+ out.print("Invalid request, Feedid or Group ID is required.");
+ }
+
+ if(req.getParameter("feedid")!=null && req.getParameter("groupid") == null) {
+ map.put("feedids", req.getParameter("feedid").replace("|", ",").toString());
+ }
+
+ if(req.getParameter("groupid") != null && req.getParameter("feedid") ==null) {
+ // String groupid1 = null;
+ StringBuffer groupid1 = new StringBuffer();
+
+ try {
+ System.out.println("feeedidsssssssss");
+ groupid1 = this.getFeedIdsByGroupId(Integer.parseInt(req.getParameter("groupid")));
+ System.out.println("feeedids"+req.getParameter("groupid"));
+
+ map.put("feedids", groupid1.toString());
+ System.out.println("groupid1" +groupid1.toString());
+
+
+ } catch (NumberFormatException e) {
+ e.printStackTrace();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ if(req.getParameter("groupid") != null && req.getParameter("feedid") !=null) {
+ StringBuffer groupid1 = new StringBuffer();
+
+
+ try {
+ System.out.println("both r not null");
+ groupid1 = this.getFeedIdsByGroupId(Integer.parseInt(req.getParameter("groupid")));
+ System.out.println("feeedids"+req.getParameter("groupid"));
+ groupid1.append(",");
+ groupid1.append(req.getParameter("feedid").replace("|", ",").toString());
+
+ map.put("feedids", groupid1.toString());
+
+
+ System.out.println("groupid1" +groupid1.toString());
+
+
+ } catch (NumberFormatException e) {
+ e.printStackTrace();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+
+
+
+ if(req.getParameter("subid")!=null && req.getParameter("feedid") !=null) {
+ StringBuffer subidstr = new StringBuffer();
+// subidstr.append(" and e.DELIVERY_SUBID in(subid)");
+// subidstr.append(req.getParameter("subid").replace("|", ",").toString());
+ subidstr.append("and e.DELIVERY_SUBID in(");
+
+ subidstr.append(req.getParameter("subid").replace("|", ",").toString());
+ subidstr.append(")");
+ map.put("subid", subidstr.toString());
+ }
+ if(req.getParameter("subid")!=null && req.getParameter("groupid") !=null) {
+ StringBuffer subidstr = new StringBuffer();
+// subidstr.append(" and e.DELIVERY_SUBID in(subid)");
+// subidstr.append(req.getParameter("subid").replace("|", ",").toString());
+ subidstr.append("and e.DELIVERY_SUBID in(");
+
+ subidstr.append(req.getParameter("subid").replace("|", ",").toString());
+ subidstr.append(")");
+ map.put("subid", subidstr.toString());
+ }
+ if(req.getParameter("type")!=null) {
+ map.put("eventType", req.getParameter("type").replace("|", ",").toString());
+ }
+ if(req.getParameter("output_type")!=null) {
+ map.put("output_type", req.getParameter("output_type").toString());
+ }
+ if(req.getParameter("start_time")!=null) {
+ map.put("start_time", req.getParameter("start_time").toString());
+ }
+ if(req.getParameter("end_time")!=null) {
+ map.put("end_time", req.getParameter("end_time").toString());
+ }
+
+ if(req.getParameter("time")!=null) {
+ map.put("start_time", req.getParameter("time").toString());
+ map.put("end_time", null);
+ }
+
+
+
+ if(req.getParameter("output_type") !=null)
+ {
+ outputType = req.getParameter("output_type");
+ }
+
+
+ try {
+
+ String filterQuery = this.queryGeneretor(map);
+ eventlogger.debug("SQL Query for Statistics resultset. "+filterQuery);
+
+ ResultSet rs=this.getRecordsForSQL(filterQuery);
+
+ if(outputType.equals("csv")) {
+ resp.setContentType("application/octet-stream");
+ Date date = new Date() ;
+ SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-YYYY HH:mm:ss") ;
+ resp.setHeader("Content-Disposition", "attachment; filename=\"result:"+dateFormat.format(date)+".csv\"");
+ eventlogger.info("Generating CSV file from Statistics resultset");
+
+ rsToCSV(rs, out);
+ }
+ else {
+ eventlogger.info("Generating JSON for Statistics resultset");
+ this.rsToJson(rs, out);
+ }
+ }
+ catch (IOException e) {
+ eventlogger.error("IOException - Generating JSON/CSV:"+e);
+ e.printStackTrace();
+ }
+ catch (JSONException e) {
+ eventlogger.error("JSONException - executing SQL query:"+e);
+ e.printStackTrace();
+ } catch (SQLException e) {
+ eventlogger.error("SQLException - executing SQL query:"+e);
+ e.printStackTrace();
+ } catch (ParseException e) {
+ eventlogger.error("ParseException - executing SQL query:"+e);
+ e.printStackTrace();
+ }
+ }
+
+
+ /**
+ * rsToJson - Converting RS to JSON object
+ * @exception IOException, SQLException
+ * @param out ServletOutputStream, rs as ResultSet
+ */
+ public void rsToCSV(ResultSet rs, ServletOutputStream out) throws IOException, SQLException {
+ String header = "FEEDNAME,FEEDID,FILES_PUBLISHED,PUBLISH_LENGTH, FILES_DELIVERED, DELIVERED_LENGTH, SUBSCRIBER_URL, SUBID, PUBLISH_TIME,DELIVERY_TIME, AverageDelay\n";
+
+ // String header = "FEEDNAME,FEEDID,TYPE,REMOTE_ADDR,DELIVERY_SUBID,REQURI,TOTAL CONTENT LENGTH,NO OF FILE,AVERAGE DELAY\n";
+
+ out.write(header.getBytes());
+
+ while(rs.next()) {
+ StringBuffer line = new StringBuffer();
+ line.append(rs.getString("FEEDNAME"));
+ line.append(",");
+ line.append(rs.getString("FEEDID"));
+ line.append(",");
+ line.append(rs.getString("FILES_PUBLISHED"));
+ line.append(",");
+ line.append(rs.getString("PUBLISH_LENGTH"));
+ line.append(",");
+ line.append(rs.getString("FILES_DELIVERED"));
+ line.append(",");
+ line.append(rs.getString("DELIVERED_LENGTH"));
+ line.append(",");
+ line.append(rs.getString("SUBSCRIBER_URL"));
+ line.append(",");
+ line.append(rs.getString("SUBID"));
+ line.append(",");
+ line.append(rs.getString("PUBLISH_TIME"));
+ line.append(",");
+ line.append(rs.getString("DELIVERY_TIME"));
+ line.append(",");
+ line.append(rs.getString("AverageDelay"));
+ line.append(",");
+
+ line.append("\n");
+ out.write(line.toString().getBytes());
+ out.flush();
+ }
+ }
+
+ /**
+ * rsToJson - Converting RS to JSON object
+ * @exception IOException, SQLException
+ * @param out ServletOutputStream, rs as ResultSet
+ */
+ public void rsToJson(ResultSet rs, ServletOutputStream out) throws IOException, SQLException {
+
+ String fields[] = {"FEEDNAME","FEEDID","FILES_PUBLISHED","PUBLISH_LENGTH", "FILES_DELIVERED", "DELIVERED_LENGTH", "SUBSCRIBER_URL", "SUBID", "PUBLISH_TIME","DELIVERY_TIME", "AverageDelay"};
+ StringBuffer line = new StringBuffer();
+
+ line.append("[\n");
+
+ while(rs.next()) {
+ LOGJSONObject j2 = new LOGJSONObject();
+ for (String key : fields) {
+ Object v = rs.getString(key);
+ if (v != null)
+ j2.put(key.toLowerCase(), v);
+ else
+ j2.put(key.toLowerCase(), "");
+ }
+ line = line.append(j2.toString());;
+ line.append(",\n");
+ }
+ line.append("]");
+ out.print(line.toString());
+ }
+
+ /**
+ * getFeedIdsByGroupId - Getting FEEDID's by GROUP ID.
+ * @exception SQL Query SQLException.
+ * @param groupIds
+ */
+ public StringBuffer getFeedIdsByGroupId(int groupIds) throws SQLException{
+
+ DB db = null;
+ Connection conn = null;
+ PreparedStatement prepareStatement = null;
+ ResultSet resultSet=null;
+ String sqlGoupid = null;
+ StringBuffer feedIds = new StringBuffer();
+
+ try {
+ db = new DB();
+ conn = db.getConnection();
+ sqlGoupid= " SELECT FEEDID from FEEDS WHERE GROUPID = ?";
+ prepareStatement =conn.prepareStatement(sqlGoupid);
+ prepareStatement.setInt(1, groupIds);
+ resultSet=prepareStatement.executeQuery();
+ while(resultSet.next()){
+ feedIds.append(resultSet.getInt("FEEDID"));
+ feedIds.append(",");
+ }
+ feedIds.deleteCharAt(feedIds.length()-1);
+ System.out.println("feedIds"+feedIds.toString());
+
+ } catch (SQLException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ if(resultSet != null) {
+ resultSet.close();
+ resultSet = null;
+ }
+
+ if(prepareStatement != null) {
+ prepareStatement.close();
+ prepareStatement = null;
+ }
+
+ if(conn != null){
+ db.release(conn);
+ }
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return feedIds;
+ }
+
+
+ /**
+ * queryGeneretor - Generating sql query
+ * @exception SQL Query parse exception.
+ * @param Map as key value pare of all user input fields
+ */
+ public String queryGeneretor(Map<String, String> map) throws ParseException{
+
+ String sql = null;
+ String eventType = null;
+ String feedids = null;
+ String start_time = null;
+ String end_time = null;
+ String subid=" ";
+ if(map.get("eventType") != null){
+ eventType=(String) map.get("eventType");
+ }
+ if(map.get("feedids") != null){
+ feedids=(String) map.get("feedids");
+ }
+ if(map.get("start_time") != null){
+ start_time=(String) map.get("start_time");
+ }
+ if(map.get("end_time") != null){
+ end_time=(String) map.get("end_time");
+ }
+ if("all".equalsIgnoreCase(eventType)){
+ eventType="PUB','DEL, EXP, PBF";
+ }
+ if(map.get("subid") != null){
+ subid=(String) map.get("subid");
+ }
+
+ eventlogger.info("Generating sql query to get Statistics resultset. ");
+
+ if(end_time==null && start_time==null ){
+
+
+ sql="SELECT (SELECT NAME FROM FEEDS AS f WHERE f.FEEDID in("+feedids+") and f.FEEDID=e.FEEDID) AS FEEDNAME, e.FEEDID as FEEDID, (SELECT COUNT(*) FROM LOG_RECORDS AS c WHERE c.FEEDID in("+feedids+") and c.FEEDID=e.FEEDID AND c.TYPE='PUB') AS FILES_PUBLISHED,(SELECT SUM(content_length) FROM LOG_RECORDS AS c WHERE c.FEEDID in("+feedids+") and c.FEEDID=e.FEEDID AND c.TYPE='PUB') AS PUBLISH_LENGTH, COUNT(e.EVENT_TIME) as FILES_DELIVERED, sum(m.content_length) as DELIVERED_LENGTH,SUBSTRING_INDEX(e.REQURI,'/',+3) as SUBSCRIBER_URL, e.DELIVERY_SUBID as SUBID, e.EVENT_TIME AS PUBLISH_TIME, m.EVENT_TIME AS DELIVERY_TIME, AVG(e.EVENT_TIME - m.EVENT_TIME)/1000 as AverageDelay FROM LOG_RECORDS e JOIN LOG_RECORDS m ON m.PUBLISH_ID = e.PUBLISH_ID AND e.FEEDID IN ("+feedids+") "+subid+" AND m.STATUS=204 AND e.RESULT=204 group by SUBID";
+
+ return sql;
+ }else if(start_time!=null && end_time==null ){
+
+ long inputTimeInMilli=60000*Long.parseLong(start_time);
+ Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+ long currentTimeInMilli=cal.getTimeInMillis();
+ long compareTime=currentTimeInMilli-inputTimeInMilli;
+
+ sql="SELECT (SELECT NAME FROM FEEDS AS f WHERE f.FEEDID in("+feedids+") and f.FEEDID=e.FEEDID) AS FEEDNAME, e.FEEDID as FEEDID, (SELECT COUNT(*) FROM LOG_RECORDS AS c WHERE c.FEEDID in("+feedids+") and c.FEEDID=e.FEEDID AND c.TYPE='PUB') AS FILES_PUBLISHED,(SELECT SUM(content_length) FROM LOG_RECORDS AS c WHERE c.FEEDID in("+feedids+") and c.FEEDID=e.FEEDID AND c.TYPE='PUB') AS PUBLISH_LENGTH, COUNT(e.EVENT_TIME) as FILES_DELIVERED, sum(m.content_length) as DELIVERED_LENGTH,SUBSTRING_INDEX(e.REQURI,'/',+3) as SUBSCRIBER_URL, e.DELIVERY_SUBID as SUBID, e.EVENT_TIME AS PUBLISH_TIME, m.EVENT_TIME AS DELIVERY_TIME, AVG(e.EVENT_TIME - m.EVENT_TIME)/1000 as AverageDelay FROM LOG_RECORDS e JOIN LOG_RECORDS m ON m.PUBLISH_ID = e.PUBLISH_ID AND e.FEEDID IN ("+feedids+") "+subid+" AND m.STATUS=204 AND e.RESULT=204 and e.event_time>="+compareTime+" group by SUBID";
+
+ return sql;
+
+ }else{
+ SimpleDateFormat inFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
+ Date startDate=inFormat.parse(start_time);
+ Date endDate=inFormat.parse(end_time);
+
+ long startInMillis=startDate.getTime();
+ long endInMillis=endDate.getTime();
+
+ {
+
+ sql="SELECT (SELECT NAME FROM FEEDS AS f WHERE f.FEEDID in("+feedids+") and f.FEEDID=e.FEEDID) AS FEEDNAME, e.FEEDID as FEEDID, (SELECT COUNT(*) FROM LOG_RECORDS AS c WHERE c.FEEDID in("+feedids+") and c.FEEDID=e.FEEDID AND c.TYPE='PUB') AS FILES_PUBLISHED,(SELECT SUM(content_length) FROM LOG_RECORDS AS c WHERE c.FEEDID in("+feedids+") and c.FEEDID=e.FEEDID AND c.TYPE='PUB') AS PUBLISH_LENGTH, COUNT(e.EVENT_TIME) as FILES_DELIVERED, sum(m.content_length) as DELIVERED_LENGTH,SUBSTRING_INDEX(e.REQURI,'/',+3) as SUBSCRIBER_URL, e.DELIVERY_SUBID as SUBID, e.EVENT_TIME AS PUBLISH_TIME, m.EVENT_TIME AS DELIVERY_TIME, AVG(e.EVENT_TIME - m.EVENT_TIME)/1000 as AverageDelay FROM LOG_RECORDS e JOIN LOG_RECORDS m ON m.PUBLISH_ID = e.PUBLISH_ID AND e.FEEDID IN ("+feedids+") "+subid+" AND m.STATUS=204 AND e.RESULT=204 and e.event_time between "+startInMillis+" and "+endInMillis+" group by SUBID";
+
+ }
+ return sql;
+ }
+ }
+
+
+ /**
+ * PUT a Statistics URL -- not supported.
+ */
+ @Override
+ public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ String message = "PUT not allowed for the StatisticsURL.";
+ EventLogRecord elr = new EventLogRecord(req);
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, message);
+ }
+ /**
+ * POST a Statistics URL -- not supported.
+ */
+ @Override
+ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ String message = "POST not allowed for the StatisticsURL.";
+ EventLogRecord elr = new EventLogRecord(req);
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, message);
+ }
+
+ private Map<String, String> buildMapFromRequest(HttpServletRequest req) {
+ Map<String, String> map = new HashMap<String, String>();
+ String s = req.getParameter("type");
+ if (s != null) {
+ if (s.equals("pub") || s.equals("del") || s.equals("exp")) {
+ map.put("type", s);
+ } else {
+ map.put("err", "bad type");
+ return map;
+ }
+ } else
+ map.put("type", "all");
+ map.put("publishSQL", "");
+ map.put("statusSQL", "");
+ map.put("resultSQL", "");
+ map.put("reasonSQL", "");
+
+ s = req.getParameter("publishId");
+ if (s != null) {
+ if (s.indexOf("'") >= 0) {
+ map.put("err", "bad publishId");
+ return map;
+ }
+ map.put("publishSQL", " AND PUBLISH_ID = '"+s+"'");
+ }
+
+ s = req.getParameter("statusCode");
+ if (s != null) {
+ String sql = null;
+ if (s.equals("success")) {
+ sql = " AND STATUS >= 200 AND STATUS < 300";
+ } else if (s.equals("redirect")) {
+ sql = " AND STATUS >= 300 AND STATUS < 400";
+ } else if (s.equals("failure")) {
+ sql = " AND STATUS >= 400";
+ } else {
+ try {
+ Integer n = Integer.parseInt(s);
+ if ((n >= 100 && n < 600) || (n == -1))
+ sql = " AND STATUS = " + n;
+ } catch (NumberFormatException e) {
+ }
+ }
+ if (sql == null) {
+ map.put("err", "bad statusCode");
+ return map;
+ }
+ map.put("statusSQL", sql);
+ map.put("resultSQL", sql.replaceAll("STATUS", "RESULT"));
+ }
+
+ s = req.getParameter("expiryReason");
+ if (s != null) {
+ map.put("type", "exp");
+ if (s.equals("notRetryable")) {
+ map.put("reasonSQL", " AND REASON = 'notRetryable'");
+ } else if (s.equals("retriesExhausted")) {
+ map.put("reasonSQL", " AND REASON = 'retriesExhausted'");
+ } else if (s.equals("diskFull")) {
+ map.put("reasonSQL", " AND REASON = 'diskFull'");
+ } else if (s.equals("other")) {
+ map.put("reasonSQL", " AND REASON = 'other'");
+ } else {
+ map.put("err", "bad expiryReason");
+ return map;
+ }
+ }
+
+ long stime = getTimeFromParam(req.getParameter("start"));
+ if (stime < 0) {
+ map.put("err", "bad start");
+ return map;
+ }
+ long etime = getTimeFromParam(req.getParameter("end"));
+ if (etime < 0) {
+ map.put("err", "bad end");
+ return map;
+ }
+ if (stime == 0 && etime == 0) {
+ etime = System.currentTimeMillis();
+ stime = etime - TWENTYFOUR_HOURS;
+ } else if (stime == 0) {
+ stime = etime - TWENTYFOUR_HOURS;
+ } else if (etime == 0) {
+ etime = stime + TWENTYFOUR_HOURS;
+ }
+ map.put("timeSQL", String.format(" AND EVENT_TIME >= %d AND EVENT_TIME <= %d", stime, etime));
+ return map;
+ }
+ private long getTimeFromParam(final String s) {
+ if (s == null)
+ return 0;
+ try {
+ // First, look for an RFC 3339 date
+ String fmt = (s.indexOf('.') > 0) ? fmt2 : fmt1;
+ SimpleDateFormat sdf = new SimpleDateFormat(fmt);
+ Date d = sdf.parse(s);
+ return d.getTime();
+ } catch (ParseException e) {
+ }
+ try {
+ // Also allow a long (in ms); useful for testing
+ long n = Long.parseLong(s);
+ return n;
+ } catch (NumberFormatException e) {
+ }
+ intlogger.info("Error parsing time="+s);
+ return -1;
+ }
+
+
+ private ResultSet getRecordsForSQL(String sql) {
+ intlogger.debug(sql);
+ long start = System.currentTimeMillis();
+ DB db = new DB();
+ Connection conn = null;
+ ResultSet rs=null;
+
+ try {
+ conn = db.getConnection();
+ Statement stmt = conn.createStatement();
+ PreparedStatement pst=conn.prepareStatement(sql);
+ rs=pst.executeQuery();
+ //this.rsToJson(rs)
+ //rs.close();
+ stmt.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ } finally {
+ if (conn != null)
+ db.release(conn);
+ }
+
+ intlogger.debug("Time: " + (System.currentTimeMillis()-start) + " ms");
+
+ return rs;
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/SubLogServlet.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/SubLogServlet.java
new file mode 100644
index 00000000..0f196b28
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/SubLogServlet.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning;
+
+/**
+ * This servlet handles requests to the &lt;subLogURL&gt;,
+ * which are generated by the provisioning server to handle the log query API.
+ *
+ * @author Robert Eby
+ * @version $Id: SubLogServlet.java,v 1.1 2013/04/26 21:00:25 eby Exp $
+ */
+@SuppressWarnings("serial")
+public class SubLogServlet extends LogServlet {
+ public SubLogServlet() {
+ super(false);
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/SubscribeServlet.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/SubscribeServlet.java
new file mode 100644
index 00000000..ea79e9ff
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/SubscribeServlet.java
@@ -0,0 +1,288 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.util.Collection;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.json.JSONObject;
+
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+import com.att.research.datarouter.authz.AuthorizationResponse;
+import com.att.research.datarouter.provisioning.beans.EventLogRecord;
+import com.att.research.datarouter.provisioning.beans.Feed;
+import com.att.research.datarouter.provisioning.beans.Subscription;
+import com.att.research.datarouter.provisioning.eelf.EelfMsgs;
+import com.att.research.datarouter.provisioning.utils.JSONUtilities;
+
+/**
+ * This servlet handles provisioning for the &lt;subscribeURL&gt; which is generated by the provisioning
+ * server to handle the creation and inspection of subscriptions to a specific feed.
+ *
+ * @author Robert Eby
+ * @version $Id$
+ */
+@SuppressWarnings("serial")
+public class SubscribeServlet extends ProxyServlet {
+
+ //Adding EELF Logger Rally:US664892
+ private static EELFLogger eelflogger = EELFManager.getInstance().getLogger("com.att.research.datarouter.provisioning.SubscribeServlet");
+
+ /**
+ * DELETE on the &lt;subscribeUrl&gt; -- not supported.
+ */
+ @Override
+ public void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doDelete");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_SUBID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+"");
+ String message = "DELETE not allowed for the subscribeURL.";
+ EventLogRecord elr = new EventLogRecord(req);
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, message);
+ }
+ /**
+ * GET on the &lt;subscribeUrl&gt; -- get the list of subscriptions to a feed.
+ * See the <i>Subscription Collection Query</i> section in the <b>Provisioning API</b>
+ * document for details on how this method should be invoked.
+ */
+ @Override
+ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doGet");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_SUBID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+"");
+ EventLogRecord elr = new EventLogRecord(req);
+ String message = isAuthorizedForProvisioning(req);
+ if (message != null) {
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }
+ if (isProxyServer()) {
+ super.doGet(req, resp);
+ return;
+ }
+ String bhdr = req.getHeader(BEHALF_HEADER);
+ if (bhdr == null) {
+ message = "Missing "+BEHALF_HEADER+" header.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ int feedid = getIdFromPath(req);
+ if (feedid < 0) {
+ message = "Missing or bad feed number.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ Feed feed = Feed.getFeedById(feedid);
+ if (feed == null || feed.isDeleted()) {
+ message = "Missing or bad feed number.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_NOT_FOUND);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, message);
+ return;
+ }
+ // Check with the Authorizer
+ AuthorizationResponse aresp = authz.decide(req);
+ if (! aresp.isAuthorized()) {
+ message = "Policy Engine disallows access.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }
+
+ // Display a list of URLs
+ Collection<String> list = Subscription.getSubscriptionUrlList(feedid);
+ String t = JSONUtilities.createJSONArray(list);
+
+ // send response
+ elr.setResult(HttpServletResponse.SC_OK);
+ eventlogger.info(elr);
+ resp.setStatus(HttpServletResponse.SC_OK);
+ resp.setContentType(SUBLIST_CONTENT_TYPE);
+ resp.getOutputStream().print(t);
+ }
+ /**
+ * PUT on the &lt;subscribeUrl&gt; -- not supported.
+ */
+ @Override
+ public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doPut");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_SUBID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+"");
+ String message = "PUT not allowed for the subscribeURL.";
+ EventLogRecord elr = new EventLogRecord(req);
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, message);
+ }
+ /**
+ * POST on the &lt;subscribeUrl&gt; -- create a new subscription to a feed.
+ * See the <i>Creating a Subscription</i> section in the <b>Provisioning API</b>
+ * document for details on how this method should be invoked.
+ */
+ @Override
+ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doPost");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF, req.getHeader(BEHALF_HEADER));
+ EventLogRecord elr = new EventLogRecord(req);
+ String message = isAuthorizedForProvisioning(req);
+ if (message != null) {
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }
+ if (isProxyServer()) {
+ super.doPost(req, resp);
+ return;
+ }
+ String bhdr = req.getHeader(BEHALF_HEADER);
+ if (bhdr == null) {
+ message = "Missing "+BEHALF_HEADER+" header.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ int feedid = getIdFromPath(req);
+ if (feedid < 0) {
+ message = "Missing or bad feed number.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ Feed feed = Feed.getFeedById(feedid);
+ if (feed == null || feed.isDeleted()) {
+ message = "Missing or bad feed number.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_NOT_FOUND);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, message);
+ return;
+ }
+ // Check with the Authorizer
+ AuthorizationResponse aresp = authz.decide(req);
+ if (! aresp.isAuthorized()) {
+ message = "Policy Engine disallows access.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }
+
+ // check content type is SUB_CONTENT_TYPE, version 1.0
+ ContentHeader ch = getContentHeader(req);
+ String ver = ch.getAttribute("version");
+ if (!ch.getType().equals(SUB_BASECONTENT_TYPE) || !(ver.equals("1.0") || ver.equals("2.0"))) {
+ intlogger.debug("Content-type is: "+req.getHeader("Content-Type"));
+ message = "Incorrect content-type";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE, message);
+ return;
+ }
+ JSONObject jo = getJSONfromInput(req);
+ if (jo == null) {
+ message = "Badly formed JSON";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ if (intlogger.isDebugEnabled())
+ intlogger.debug(jo.toString());
+ if (++active_subs > max_subs) {
+ active_subs--;
+ message = "Cannot create subscription; the maximum number of subscriptions has been configured.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_CONFLICT);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_CONFLICT, message);
+ return;
+ }
+ Subscription sub = null;
+ try {
+ sub = new Subscription(jo);
+ } catch (InvalidObjectException e) {
+ active_subs--;
+ message = e.getMessage();
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ sub.setFeedid(feedid);
+ sub.setSubscriber(bhdr); // set from X-ATT-DR-ON-BEHALF-OF header
+
+ // Check if this subscription already exists; not an error (yet), just warn
+ Subscription sub2 = Subscription.getSubscriptionMatching(sub);
+ if (sub2 != null)
+ intlogger.warn("PROV0011 Creating a duplicate subscription: new subid="+sub.getSubid()+", old subid="+sub2.getSubid());
+
+ // Create SUBSCRIPTIONS table entries
+ if (doInsert(sub)) {
+ // send response
+ elr.setResult(HttpServletResponse.SC_CREATED);
+ eventlogger.info(elr);
+ resp.setStatus(HttpServletResponse.SC_CREATED);
+ resp.setContentType(SUBFULL_CONTENT_TYPE);
+ resp.setHeader("Location", sub.getLinks().getSelf());
+ resp.getOutputStream().print(sub.asLimitedJSONObject().toString());
+
+ provisioningDataChanged();
+ } else {
+ // Something went wrong with the INSERT
+ active_subs--;
+ elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG);
+ }
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/SubscriptionServlet.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/SubscriptionServlet.java
new file mode 100644
index 00000000..0bb47175
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/SubscriptionServlet.java
@@ -0,0 +1,476 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.List;
+import java.util.Vector;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+import com.att.research.datarouter.authz.AuthorizationResponse;
+import com.att.research.datarouter.provisioning.beans.EventLogRecord;
+import com.att.research.datarouter.provisioning.beans.Subscription;
+import com.att.research.datarouter.provisioning.eelf.EelfMsgs;
+
+/**
+ * This servlet handles provisioning for the &lt;subscriptionURL&gt; which is generated by the provisioning
+ * server to handle the inspection, modification, and deletion of a particular subscription to a feed.
+ * It supports DELETE to delete a subscription, GET to retrieve information about the subscription,
+ * and PUT to modify the subscription. In DR 3.0, POST is also supported in order to reset the subscription
+ * timers for individual subscriptions.
+ *
+ * @author Robert Eby
+ * @version $Id$
+ */
+@SuppressWarnings("serial")
+public class SubscriptionServlet extends ProxyServlet {
+ public static final String SUBCNTRL_CONTENT_TYPE = "application/vnd.att-dr.subscription-control";
+ //Adding EELF Logger Rally:US664892
+ private static EELFLogger eelflogger = EELFManager.getInstance().getLogger("com.att.research.datarouter.provisioning.SubscriptionServlet");
+
+ /**
+ * DELETE on the &lt;subscriptionUrl&gt; -- delete a subscription.
+ * See the <i>Deleting a Subscription</i> section in the <b>Provisioning API</b>
+ * document for details on how this method should be invoked.
+ */
+ @Override
+ public void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doDelete");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_SUBID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+"");
+ EventLogRecord elr = new EventLogRecord(req);
+ String message = isAuthorizedForProvisioning(req);
+ if (message != null) {
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }
+ if (isProxyServer()) {
+ super.doDelete(req, resp);
+ return;
+ }
+ String bhdr = req.getHeader(BEHALF_HEADER);
+ if (bhdr == null) {
+ message = "Missing "+BEHALF_HEADER+" header.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ int subid = getIdFromPath(req);
+ if (subid < 0) {
+ message = "Missing or bad subscription number.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ Subscription sub = Subscription.getSubscriptionById(subid);
+ if (sub == null) {
+ message = "Missing or bad subscription number.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_NOT_FOUND);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, message);
+ return;
+ }
+ // Check with the Authorizer
+ AuthorizationResponse aresp = authz.decide(req);
+ if (! aresp.isAuthorized()) {
+ message = "Policy Engine disallows access.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }
+
+ // Delete Subscription
+ if (doDelete(sub)) {
+ active_subs--;
+ // send response
+ elr.setResult(HttpServletResponse.SC_NO_CONTENT);
+ eventlogger.info(elr);
+ resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
+ provisioningDataChanged();
+ } else {
+ // Something went wrong with the DELETE
+ elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG);
+ }
+ }
+ /**
+ * GET on the &lt;subscriptionUrl&gt; -- get information about a subscription.
+ * See the <i>Retreiving Information about a Subscription</i> section in the <b>Provisioning API</b>
+ * document for details on how this method should be invoked.
+ */
+ @Override
+ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doGet");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_SUBID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+"");
+ EventLogRecord elr = new EventLogRecord(req);
+ String message = isAuthorizedForProvisioning(req);
+ if (message != null) {
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }
+ if (isProxyServer()) {
+ super.doGet(req, resp);
+ return;
+ }
+ String bhdr = req.getHeader(BEHALF_HEADER);
+ if (bhdr == null) {
+ message = "Missing "+BEHALF_HEADER+" header.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ int subid = getIdFromPath(req);
+ if (subid < 0) {
+ message = "Missing or bad subscription number.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ Subscription sub = Subscription.getSubscriptionById(subid);
+ if (sub == null) {
+ message = "Missing or bad subscription number.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_NOT_FOUND);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, message);
+ return;
+ }
+ // Check with the Authorizer
+ AuthorizationResponse aresp = authz.decide(req);
+ if (! aresp.isAuthorized()) {
+ message = "Policy Engine disallows access.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }
+
+ // send response
+ elr.setResult(HttpServletResponse.SC_OK);
+ eventlogger.info(elr);
+ resp.setStatus(HttpServletResponse.SC_OK);
+ resp.setContentType(SUBFULL_CONTENT_TYPE);
+ resp.getOutputStream().print(sub.asJSONObject(true).toString());
+ }
+ /**
+ * PUT on the &lt;subscriptionUrl&gt; -- modify a subscription.
+ * See the <i>Modifying a Subscription</i> section in the <b>Provisioning API</b>
+ * document for details on how this method should be invoked.
+ */
+ @Override
+ public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ setIpAndFqdnForEelf("doPut");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_SUBID, req.getHeader(BEHALF_HEADER),getIdFromPath(req)+"");
+ EventLogRecord elr = new EventLogRecord(req);
+ String message = isAuthorizedForProvisioning(req);
+ if (message != null) {
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }
+ if (isProxyServer()) {
+ super.doPut(req, resp);
+ return;
+ }
+ String bhdr = req.getHeader(BEHALF_HEADER);
+ if (bhdr == null) {
+ message = "Missing "+BEHALF_HEADER+" header.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ int subid = getIdFromPath(req);
+ if (subid < 0) {
+ message = "Missing or bad subscription number.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ Subscription oldsub = Subscription.getSubscriptionById(subid);
+ if (oldsub == null) {
+ message = "Missing or bad subscription number.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_NOT_FOUND);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, message);
+ return;
+ }
+ // Check with the Authorizer
+ AuthorizationResponse aresp = authz.decide(req);
+ if (! aresp.isAuthorized()) {
+ message = "Policy Engine disallows access.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }
+ // check content type is SUB_CONTENT_TYPE, version 1.0
+ ContentHeader ch = getContentHeader(req);
+ String ver = ch.getAttribute("version");
+ if (!ch.getType().equals(SUB_BASECONTENT_TYPE) || !(ver.equals("1.0") || ver.equals("2.0"))) {
+ message = "Incorrect content-type";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE, message);
+ return;
+ }
+ JSONObject jo = getJSONfromInput(req);
+ if (jo == null) {
+ message = "Badly formed JSON";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ if (intlogger.isDebugEnabled())
+ intlogger.debug(jo.toString());
+ Subscription sub = null;
+ try {
+ sub = new Subscription(jo);
+ } catch (InvalidObjectException e) {
+ message = e.getMessage();
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ sub.setSubid(oldsub.getSubid());
+ sub.setFeedid(oldsub.getFeedid());
+ sub.setSubscriber(bhdr); // set from X-ATT-DR-ON-BEHALF-OF header
+
+ String subjectgroup = (req.getHeader("X-ATT-DR-ON-BEHALF-OF-GROUP")); //Adding for group feature:Rally US708115
+ if (!oldsub.getSubscriber().equals(sub.getSubscriber()) && subjectgroup == null) {
+ message = "This subscriber must be modified by the same subscriber that created it.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+
+ // Update SUBSCRIPTIONS table entries
+ if (doUpdate(sub)) {
+ // send response
+ elr.setResult(HttpServletResponse.SC_OK);
+ eventlogger.info(elr);
+ resp.setStatus(HttpServletResponse.SC_OK);
+ resp.setContentType(SUBFULL_CONTENT_TYPE);
+ resp.getOutputStream().print(sub.asLimitedJSONObject().toString());
+
+ /**Change Owner ship of Subscriber Adding for group feature:Rally US708115*/
+ if (jo.has("changeowner") && subjectgroup != null) {
+ Boolean changeowner = (Boolean) jo.get("changeowner");
+ if (changeowner != null && changeowner.equals(true)) {
+ sub.setSubscriber(req.getHeader(BEHALF_HEADER));
+ sub.changeOwnerShip();
+ }
+ }
+ /***End of change ownership*/
+
+ provisioningDataChanged();
+ } else {
+ // Something went wrong with the UPDATE
+ elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG);
+ }
+ }
+ /**
+ * POST on the &lt;subscriptionUrl&gt; -- control a subscription.
+ * See the <i>Resetting a Subscription's Retry Schedule</i> section in the <b>Provisioning API</b>
+ * document for details on how this method should be invoked.
+ */
+ @Override
+ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+// OLD pre-3.0 code
+// String message = "POST not allowed for the subscriptionURL.";
+// EventLogRecord elr = new EventLogRecord(req);
+// elr.setMessage(message);
+// elr.setResult(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
+// eventlogger.info(elr);
+// resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, message);
+
+ setIpAndFqdnForEelf("doPost");
+ eelflogger.info(EelfMsgs.MESSAGE_WITH_BEHALF, req.getHeader(BEHALF_HEADER));
+ EventLogRecord elr = new EventLogRecord(req);
+ String message = isAuthorizedForProvisioning(req);
+ if (message != null) {
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }
+ if (isProxyServer()) {
+ super.doPost(req, resp);
+ return;
+ }
+ String bhdr = req.getHeader(BEHALF_HEADER);
+ if (bhdr == null) {
+ message = "Missing "+BEHALF_HEADER+" header.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ final int subid = getIdFromPath(req);
+ if (subid < 0 || Subscription.getSubscriptionById(subid) == null) {
+ message = "Missing or bad subscription number.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ // check content type is SUBCNTRL_CONTENT_TYPE, version 1.0
+ ContentHeader ch = getContentHeader(req);
+ String ver = ch.getAttribute("version");
+ if (!ch.getType().equals(SUBCNTRL_CONTENT_TYPE) || !ver.equals("1.0")) {
+ message = "Incorrect content-type";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE, message);
+ return;
+ }
+ // Check with the Authorizer
+ AuthorizationResponse aresp = authz.decide(req);
+ if (! aresp.isAuthorized()) {
+ message = "Policy Engine disallows access.";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_FORBIDDEN);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
+ return;
+ }
+ JSONObject jo = getJSONfromInput(req);
+ if (jo == null) {
+ message = "Badly formed JSON";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ return;
+ }
+ try {
+ // Only the active POD sends notifications
+ boolean active = SynchronizerTask.getSynchronizer().isActive();
+ boolean b = jo.getBoolean("failed");
+ if (active && !b) {
+ // Notify all nodes to reset the subscription
+ SubscriberNotifyThread t = new SubscriberNotifyThread();
+ t.resetSubscription(subid);
+ t.start();
+ }
+ // send response
+ elr.setResult(HttpServletResponse.SC_ACCEPTED);
+ eventlogger.info(elr);
+ resp.setStatus(HttpServletResponse.SC_ACCEPTED);
+ } catch (JSONException e) {
+ message = "Badly formed JSON";
+ elr.setMessage(message);
+ elr.setResult(HttpServletResponse.SC_BAD_REQUEST);
+ eventlogger.info(elr);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
+ }
+ }
+
+ /**
+ * A Thread class used to serially send reset notifications to all nodes in the DR network,
+ * when a POST is received for a subscription.
+ */
+ public class SubscriberNotifyThread extends Thread {
+ public static final String URL_TEMPLATE = "http://%s/internal/resetSubscription/%d";
+ private List<String> urls = new Vector<String>();
+
+ public SubscriberNotifyThread() {
+ setName("SubscriberNotifyThread");
+ }
+ public void resetSubscription(int subid) {
+ for (String nodename : BaseServlet.getNodes()) {
+ String u = String.format(URL_TEMPLATE, nodename, subid);
+ urls.add(u);
+ }
+ }
+ public void run() {
+ try {
+ while (!urls.isEmpty()) {
+ String u = urls.remove(0);
+ try {
+ URL url = new URL(u);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.connect();
+ conn.getContentLength(); // Force the GET through
+ conn.disconnect();
+ } catch (IOException e) {
+ intlogger.info("IOException Error accessing URL: "+u+": " + e.getMessage());
+ }
+ }
+ } catch (Exception e) {
+ intlogger.warn("Caught exception in SubscriberNotifyThread: "+e);
+ e.printStackTrace();
+ }
+ }
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/SynchronizerTask.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/SynchronizerTask.java
new file mode 100644
index 00000000..9cb9b7c0
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/SynchronizerTask.java
@@ -0,0 +1,614 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.security.KeyStore;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.TreeSet;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.entity.ContentType;
+import org.apache.http.impl.client.AbstractHttpClient;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.log4j.Logger;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+
+import com.att.research.datarouter.provisioning.beans.EgressRoute;
+import com.att.research.datarouter.provisioning.beans.Feed;
+import com.att.research.datarouter.provisioning.beans.IngressRoute;
+import com.att.research.datarouter.provisioning.beans.NetworkRoute;
+import com.att.research.datarouter.provisioning.beans.Parameters;
+import com.att.research.datarouter.provisioning.beans.Subscription;
+import com.att.research.datarouter.provisioning.beans.Syncable;
+import com.att.research.datarouter.provisioning.utils.DB;
+import com.att.research.datarouter.provisioning.utils.RLEBitSet;
+import com.att.research.datarouter.provisioning.utils.LogfileLoader;
+import com.att.research.datarouter.provisioning.utils.URLUtilities;
+import com.att.research.datarouter.provisioning.beans.Group; //Groups feature Rally:US708115 - 1610
+
+/**
+ * This class handles synchronization between provisioning servers (PODs). It has three primary functions:
+ * <ol>
+ * <li>Checking DNS once per minute to see which POD the DNS CNAME points to. The CNAME will point to
+ * the active (master) POD.</li>
+ * <li>On non-master (standby) PODs, fetches provisioning data and logs in order to keep MySQL in sync.</li>
+ * <li>Providing information to other parts of the system as to the current role (ACTIVE, STANDBY, UNKNOWN)
+ * of this POD.</li>
+ * </ol>
+ * <p>For this to work correctly, the following code needs to be placed at the beginning of main().</p>
+ * <code>
+ * Security.setProperty("networkaddress.cache.ttl", "10");
+ * </code>
+ *
+ * @author Robert Eby
+ * @version $Id: SynchronizerTask.java,v 1.10 2014/03/21 13:50:10 eby Exp $
+ */
+public class SynchronizerTask extends TimerTask {
+ /** This is a singleton -- there is only one SynchronizerTask object in the server */
+ private static SynchronizerTask synctask;
+
+ /** This POD is unknown -- not on the list of PODs */
+ public static final int UNKNOWN = 0;
+ /** This POD is active -- on the list of PODs, and the DNS CNAME points to us */
+ public static final int ACTIVE = 1;
+ /** This POD is standby -- on the list of PODs, and the DNS CNAME does not point to us */
+ public static final int STANDBY = 2;
+ private static final String[] stnames = { "UNKNOWN", "ACTIVE", "STANDBY" };
+ private static final long ONE_HOUR = 60 * 60 * 1000L;
+
+ private final Logger logger;
+ private final Timer rolex;
+ private final String spooldir;
+ private int state;
+ private boolean doFetch;
+ private long nextsynctime;
+ private AbstractHttpClient httpclient = null;
+
+ /**
+ * Get the singleton SynchronizerTask object.
+ * @return the SynchronizerTask
+ */
+ public static synchronized SynchronizerTask getSynchronizer() {
+ if (synctask == null)
+ synctask = new SynchronizerTask();
+ return synctask;
+ }
+
+ @SuppressWarnings("deprecation")
+ private SynchronizerTask() {
+ logger = Logger.getLogger("com.att.research.datarouter.provisioning.internal");
+ rolex = new Timer();
+ spooldir = (new DB()).getProperties().getProperty("com.att.research.datarouter.provserver.spooldir");
+ state = UNKNOWN;
+ doFetch = true; // start off with a fetch
+ nextsynctime = 0;
+
+ logger.info("PROV5000: Sync task starting, server state is UNKNOWN");
+ try {
+ Properties props = (new DB()).getProperties();
+ String type = props.getProperty(Main.KEYSTORE_TYPE_PROPERTY, "jks");
+ String store = props.getProperty(Main.KEYSTORE_PATH_PROPERTY);
+ String pass = props.getProperty(Main.KEYSTORE_PASSWORD_PROPERTY);
+ KeyStore keyStore = KeyStore.getInstance(type);
+ FileInputStream instream = new FileInputStream(new File(store));
+ keyStore.load(instream, pass.toCharArray());
+ instream.close();
+
+ store = props.getProperty(Main.TRUSTSTORE_PATH_PROPERTY);
+ pass = props.getProperty(Main.TRUSTSTORE_PASSWORD_PROPERTY);
+ KeyStore trustStore = null;
+ if (store != null && store.length() > 0) {
+ trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ instream = new FileInputStream(new File(store));
+ trustStore.load(instream, pass.toCharArray());
+ instream.close();
+ }
+
+ // We are connecting with the node name, but the certificate will have the CNAME
+ // So we need to accept a non-matching certificate name
+ String keystorepass = props.getProperty(Main.KEYSTORE_PASSWORD_PROPERTY); //itrack.web.att.com/browse/DATARTR-6 for changing hard coded passphase ref
+ AbstractHttpClient hc = new DefaultHttpClient();
+ SSLSocketFactory socketFactory =
+ (trustStore == null)
+ ? new SSLSocketFactory(keyStore, keystorepass)
+ : new SSLSocketFactory(keyStore, keystorepass, trustStore);
+ socketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+ Scheme sch = new Scheme("https", 443, socketFactory);
+ hc.getConnectionManager().getSchemeRegistry().register(sch);
+ httpclient = hc;
+
+ // Run once every 5 seconds to check DNS, etc.
+ long interval = 0;
+ try {
+ String s = props.getProperty("com.att.research.datarouter.provserver.sync_interval", "5000");
+ interval = Long.parseLong(s);
+ } catch (NumberFormatException e) {
+ interval = 5000L;
+ }
+ rolex.scheduleAtFixedRate(this, 0L, interval);
+ } catch (Exception e) {
+ logger.warn("PROV5005: Problem starting the synchronizer: "+e);
+ }
+ }
+
+ /**
+ * What is the state of this POD?
+ * @return one of ACTIVE, STANDBY, UNKNOWN
+ */
+ public int getState() {
+ return state;
+ }
+
+ /**
+ * Is this the active POD?
+ * @return true if we are active (the master), false otherwise
+ */
+ public boolean isActive() {
+ return state == ACTIVE;
+ }
+
+ /**
+ * This method is used to signal that another POD (the active POD) has sent us a /fetchProv request,
+ * and that we should re-synchronize with the master.
+ */
+ public void doFetch() {
+ doFetch = true;
+ }
+
+ /**
+ * Runs once a minute in order to <ol>
+ * <li>lookup DNS names,</li>
+ * <li>determine the state of this POD,</li>
+ * <li>if this is a standby POD, and the fetch flag is set, perform a fetch of state from the active POD.</li>
+ * <li>if this is a standby POD, check if there are any new log records to be replicated.</li>
+ * </ol>
+ */
+ @Override
+ public void run() {
+ try {
+ state = lookupState();
+ if (state == STANDBY) {
+ // Only copy provisioning data FROM the active server TO the standby
+ if (doFetch || (System.currentTimeMillis() >= nextsynctime)) {
+ logger.debug("Initiating a sync...");
+ JSONObject jo = readProvisioningJSON();
+ if (jo != null) {
+ doFetch = false;
+ syncFeeds( jo.getJSONArray("feeds"));
+ syncSubs( jo.getJSONArray("subscriptions"));
+ syncGroups( jo.getJSONArray("groups")); //Rally:US708115 - 1610
+ syncParams(jo.getJSONObject("parameters"));
+ // The following will not be present in a version=1.0 provfeed
+ JSONArray ja = jo.optJSONArray("ingress");
+ if (ja != null)
+ syncIngressRoutes(ja);
+ JSONObject j2 = jo.optJSONObject("egress");
+ if (j2 != null)
+ syncEgressRoutes( j2);
+ ja = jo.optJSONArray("routing");
+ if (ja != null)
+ syncNetworkRoutes(ja);
+ }
+ logger.info("PROV5013: Sync completed.");
+ nextsynctime = System.currentTimeMillis() + ONE_HOUR;
+ }
+ } else {
+ // Don't do fetches on non-standby PODs
+ doFetch = false;
+ }
+
+ // Fetch DR logs as needed - server to server
+ LogfileLoader lfl = LogfileLoader.getLoader();
+ if (lfl.isIdle()) {
+ // Only fetch new logs if the loader is waiting for them.
+ logger.trace("Checking for logs to replicate...");
+ RLEBitSet local = lfl.getBitSet();
+ RLEBitSet remote = readRemoteLoglist();
+ remote.andNot(local);
+ if (!remote.isEmpty()) {
+ logger.debug(" Replicating logs: "+remote);
+ replicateDRLogs(remote);
+ }
+ }
+ } catch (Exception e) {
+ logger.warn("PROV0020: Caught exception in SynchronizerTask: "+e);
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * This method is used to lookup the CNAME that points to the active server.
+ * It returns 0 (UNKNOWN), 1(ACTIVE), or 2 (STANDBY) to indicate the state of this server.
+ * @return the current state
+ */
+ private int lookupState() {
+ int newstate = UNKNOWN;
+ try {
+ InetAddress myaddr = InetAddress.getLocalHost();
+ if (logger.isTraceEnabled())
+ logger.trace("My address: "+myaddr);
+ String this_pod = myaddr.getHostName();
+ Set<String> pods = new TreeSet<String>(Arrays.asList(BaseServlet.getPods()));
+ if (pods.contains(this_pod)) {
+ InetAddress pserver = InetAddress.getByName(BaseServlet.active_prov_name);
+ newstate = myaddr.equals(pserver) ? ACTIVE : STANDBY;
+ if (logger.isDebugEnabled() && System.currentTimeMillis() >= next_msg) {
+ logger.debug("Active POD = "+pserver+", Current state is "+stnames[newstate]);
+ next_msg = System.currentTimeMillis() + (5 * 60 * 1000L);
+ }
+ } else {
+ logger.warn("PROV5003: My name ("+this_pod+") is missing from the list of provisioning servers.");
+ }
+ } catch (UnknownHostException e) {
+ logger.warn("PROV5002: Cannot determine the name of this provisioning server.");
+ }
+
+ if (newstate != state)
+ logger.info(String.format("PROV5001: Server state changed from %s to %s", stnames[state], stnames[newstate]));
+ return newstate;
+ }
+ private static long next_msg = 0; // only display the "Current state" msg every 5 mins.
+ /** Synchronize the Feeds in the JSONArray, with the Feeds in the DB. */
+ private void syncFeeds(JSONArray ja) {
+ Collection<Syncable> coll = new ArrayList<Syncable>();
+ for (int n = 0; n < ja.length(); n++) {
+ try {
+ Feed f = new Feed(ja.getJSONObject(n));
+ coll.add(f);
+ } catch (Exception e) {
+ logger.warn("PROV5004: Invalid object in feed: "+ja.optJSONObject(n));
+ }
+ }
+ if (sync(coll, Feed.getAllFeeds()))
+ BaseServlet.provisioningDataChanged();
+ }
+ /** Synchronize the Subscriptions in the JSONArray, with the Subscriptions in the DB. */
+ private void syncSubs(JSONArray ja) {
+ Collection<Syncable> coll = new ArrayList<Syncable>();
+ for (int n = 0; n < ja.length(); n++) {
+ try {
+ //Data Router Subscriber HTTPS Relaxation feature USERSTORYID:US674047.
+ JSONObject j = ja.getJSONObject(n);
+ j.put("sync", "true");
+ Subscription s = new Subscription(j);
+ coll.add(s);
+ } catch (Exception e) {
+ logger.warn("PROV5004: Invalid object in subscription: "+ja.optJSONObject(n));
+ }
+ }
+ if (sync(coll, Subscription.getAllSubscriptions()))
+ BaseServlet.provisioningDataChanged();
+ }
+
+ /** Rally:US708115 - Synchronize the Groups in the JSONArray, with the Groups in the DB. */
+ private void syncGroups(JSONArray ja) {
+ Collection<Syncable> coll = new ArrayList<Syncable>();
+ for (int n = 0; n < ja.length(); n++) {
+ try {
+ Group g = new Group(ja.getJSONObject(n));
+ coll.add(g);
+ } catch (Exception e) {
+ logger.warn("PROV5004: Invalid object in subscription: "+ja.optJSONObject(n));
+ }
+ }
+ if (sync(coll, Group.getAllgroups()))
+ BaseServlet.provisioningDataChanged();
+ }
+
+
+ /** Synchronize the Parameters in the JSONObject, with the Parameters in the DB. */
+ private void syncParams(JSONObject jo) {
+ Collection<Syncable> coll = new ArrayList<Syncable>();
+ for (String k : jo.keySet()) {
+ String v = "";
+ try {
+ v = jo.getString(k);
+ } catch (JSONException e) {
+ try {
+ v = ""+jo.getInt(k);
+ } catch (JSONException e1) {
+ JSONArray ja = jo.getJSONArray(k);
+ for (int i = 0; i < ja.length(); i++) {
+ if (i > 0)
+ v += "|";
+ v += ja.getString(i);
+ }
+ }
+ }
+ coll.add(new Parameters(k, v));
+ }
+ if (sync(coll, Parameters.getParameterCollection())) {
+ BaseServlet.provisioningDataChanged();
+ BaseServlet.provisioningParametersChanged();
+ }
+ }
+ private void syncIngressRoutes(JSONArray ja) {
+ Collection<Syncable> coll = new ArrayList<Syncable>();
+ for (int n = 0; n < ja.length(); n++) {
+ try {
+ IngressRoute in = new IngressRoute(ja.getJSONObject(n));
+ coll.add(in);
+ } catch (NumberFormatException e) {
+ logger.warn("PROV5004: Invalid object in ingress routes: "+ja.optJSONObject(n));
+ }
+ }
+ if (sync(coll, IngressRoute.getAllIngressRoutes()))
+ BaseServlet.provisioningDataChanged();
+ }
+ private void syncEgressRoutes(JSONObject jo) {
+ Collection<Syncable> coll = new ArrayList<Syncable>();
+ for (String key : jo.keySet()) {
+ try {
+ int sub = Integer.parseInt(key);
+ String node = jo.getString(key);
+ EgressRoute er = new EgressRoute(sub, node);
+ coll.add(er);
+ } catch (NumberFormatException e) {
+ logger.warn("PROV5004: Invalid subid in egress routes: "+key);
+ } catch (IllegalArgumentException e) {
+ logger.warn("PROV5004: Invalid node name in egress routes: "+key);
+ }
+ }
+ if (sync(coll, EgressRoute.getAllEgressRoutes()))
+ BaseServlet.provisioningDataChanged();
+ }
+ private void syncNetworkRoutes(JSONArray ja) {
+ Collection<Syncable> coll = new ArrayList<Syncable>();
+ for (int n = 0; n < ja.length(); n++) {
+ try {
+ NetworkRoute nr = new NetworkRoute(ja.getJSONObject(n));
+ coll.add(nr);
+ } catch (JSONException e) {
+ logger.warn("PROV5004: Invalid object in network routes: "+ja.optJSONObject(n));
+ }
+ }
+ if (sync(coll, NetworkRoute.getAllNetworkRoutes()))
+ BaseServlet.provisioningDataChanged();
+ }
+ private boolean sync(Collection<? extends Syncable> newc, Collection<? extends Syncable> oldc) {
+ boolean changes = false;
+ try {
+ Map<String, Syncable> newmap = getMap(newc);
+ Map<String, Syncable> oldmap = getMap(oldc);
+ Set<String> union = new TreeSet<String>(newmap.keySet());
+ union.addAll(oldmap.keySet());
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ for (String n : union) {
+ Syncable newobj = newmap.get(n);
+ Syncable oldobj = oldmap.get(n);
+ if (oldobj == null) {
+ if (logger.isDebugEnabled())
+ logger.debug(" Inserting record: "+newobj);
+ newobj.doInsert(conn);
+ changes = true;
+ } else if (newobj == null) {
+ if (logger.isDebugEnabled())
+ logger.debug(" Deleting record: "+oldobj);
+ oldobj.doDelete(conn);
+ changes = true;
+ } else if (!newobj.equals(oldobj)) {
+ if (logger.isDebugEnabled())
+ logger.debug(" Updating record: "+newobj);
+ newobj.doUpdate(conn);
+
+ /**Rally US708115
+ * Change Ownership of FEED - 1610, Syncronised with secondary DB.
+ * */
+ checkChnageOwner(newobj, oldobj);
+
+ changes = true;
+ }
+ }
+ db.release(conn);
+ } catch (SQLException e) {
+ logger.warn("PROV5009: problem during sync, exception: "+e);
+ e.printStackTrace();
+ }
+ return changes;
+ }
+ private Map<String, Syncable> getMap(Collection<? extends Syncable> c) {
+ Map<String, Syncable> map = new HashMap<String, Syncable>();
+ for (Syncable v : c) {
+ map.put(v.getKey(), v);
+ }
+ return map;
+ }
+
+
+ /**Change owner of FEED/SUBSCRIPTION*/
+ /**Rally US708115
+ * Change Ownership of FEED - 1610
+ *
+ * */
+ private void checkChnageOwner(Syncable newobj, Syncable oldobj) {
+ if(newobj instanceof Feed) {
+ Feed oldfeed = (Feed) oldobj;
+ Feed newfeed = (Feed) newobj;
+
+ if(!oldfeed.getPublisher().equals(newfeed.getPublisher())){
+ logger.info("PROV5013 - Previous publisher: "+oldfeed.getPublisher() +": New publisher-"+newfeed.getPublisher());
+ oldfeed.setPublisher(newfeed.getPublisher());
+ oldfeed.changeOwnerShip();
+ }
+ }
+ else if(newobj instanceof Subscription) {
+ Subscription oldsub = (Subscription) oldobj;
+ Subscription newsub = (Subscription) newobj;
+
+ if(!oldsub.getSubscriber().equals(newsub.getSubscriber())){
+ logger.info("PROV5013 - Previous subscriber: "+oldsub.getSubscriber() +": New subscriber-"+newsub.getSubscriber());
+ oldsub.setSubscriber(newsub.getSubscriber());
+ oldsub.changeOwnerShip();
+ }
+ }
+
+ }
+
+ /**
+ * Issue a GET on the peer POD's /internal/prov/ URL to get a copy of its provisioning data.
+ * @return the provisioning data (as a JONObject)
+ */
+ private synchronized JSONObject readProvisioningJSON() {
+ String url = URLUtilities.generatePeerProvURL();
+ HttpGet get = new HttpGet(url);
+ try {
+ HttpResponse response = httpclient.execute(get);
+ int code = response.getStatusLine().getStatusCode();
+ if (code != HttpServletResponse.SC_OK) {
+ logger.warn("PROV5010: readProvisioningJSON failed, bad error code: "+code);
+ return null;
+ }
+ HttpEntity entity = response.getEntity();
+ String ctype = entity.getContentType().getValue().trim();
+ if (!ctype.equals(BaseServlet.PROVFULL_CONTENT_TYPE1) && !ctype.equals(BaseServlet.PROVFULL_CONTENT_TYPE2)) {
+ logger.warn("PROV5011: readProvisioningJSON failed, bad content type: "+ctype);
+ return null;
+ }
+ return new JSONObject(new JSONTokener(entity.getContent()));
+ } catch (Exception e) {
+ logger.warn("PROV5012: readProvisioningJSON failed, exception: "+e);
+ return null;
+ } finally {
+ get.releaseConnection();
+ }
+ }
+ /**
+ * Issue a GET on the peer POD's /internal/drlogs/ URL to get an RELBitSet representing the
+ * log records available in the remote database.
+ * @return the bitset
+ */
+ private RLEBitSet readRemoteLoglist() {
+ RLEBitSet bs = new RLEBitSet();
+ String url = URLUtilities.generatePeerLogsURL();
+
+ //Fixing if only one Prov is configured, not to give exception to fill logs, return empty bitset.
+ if(url.equals("")) {
+ return bs;
+ }
+ //End of fix.
+
+ HttpGet get = new HttpGet(url);
+ try {
+ HttpResponse response = httpclient.execute(get);
+ int code = response.getStatusLine().getStatusCode();
+ if (code != HttpServletResponse.SC_OK) {
+ logger.warn("PROV5010: readRemoteLoglist failed, bad error code: "+code);
+ return bs;
+ }
+ HttpEntity entity = response.getEntity();
+ String ctype = entity.getContentType().getValue().trim();
+ if (!ctype.equals("text/plain")) {
+ logger.warn("PROV5011: readRemoteLoglist failed, bad content type: "+ctype);
+ return bs;
+ }
+ InputStream is = entity.getContent();
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ int ch = 0;
+ while ((ch = is.read()) >= 0)
+ bos.write(ch);
+ bs.set(bos.toString());
+ is.close();
+ } catch (Exception e) {
+ logger.warn("PROV5012: readRemoteLoglist failed, exception: "+e);
+ return bs;
+ } finally {
+ get.releaseConnection();
+ }
+ return bs;
+ }
+ /**
+ * Issue a POST on the peer POD's /internal/drlogs/ URL to fetch log records available
+ * in the remote database that we wish to copy to the local database.
+ * @param bs the bitset (an RELBitSet) of log records to fetch
+ */
+ private void replicateDRLogs(RLEBitSet bs) {
+ String url = URLUtilities.generatePeerLogsURL();
+ HttpPost post = new HttpPost(url);
+ try {
+ String t = bs.toString();
+ HttpEntity body = new ByteArrayEntity(t.getBytes(), ContentType.create("text/plain"));
+ post.setEntity(body);
+ if (logger.isDebugEnabled())
+ logger.debug("Requesting records: "+t);
+
+ HttpResponse response = httpclient.execute(post);
+ int code = response.getStatusLine().getStatusCode();
+ if (code != HttpServletResponse.SC_OK) {
+ logger.warn("PROV5010: replicateDRLogs failed, bad error code: "+code);
+ return;
+ }
+ HttpEntity entity = response.getEntity();
+ String ctype = entity.getContentType().getValue().trim();
+ if (!ctype.equals("text/plain")) {
+ logger.warn("PROV5011: replicateDRLogs failed, bad content type: "+ctype);
+ return;
+ }
+
+ String spoolname = "" + System.currentTimeMillis();
+ Path tmppath = Paths.get(spooldir, spoolname);
+ Path donepath = Paths.get(spooldir, "IN."+spoolname);
+ Files.copy(entity.getContent(), Paths.get(spooldir, spoolname), StandardCopyOption.REPLACE_EXISTING);
+ Files.move(tmppath, donepath, StandardCopyOption.REPLACE_EXISTING);
+ logger.info("Approximately "+bs.cardinality()+" records replicated.");
+ } catch (Exception e) {
+ logger.warn("PROV5012: replicateDRLogs failed, exception: "+e);
+ } finally {
+ post.releaseConnection();
+ }
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/BaseLogRecord.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/BaseLogRecord.java
new file mode 100644
index 00000000..327f95f2
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/BaseLogRecord.java
@@ -0,0 +1,184 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.beans;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import org.json.LOGJSONObject;
+
+/**
+ * Define the common fields used by the three types of records generated by DR nodes.
+ *
+ * @author Robert Eby
+ * @version $Id: BaseLogRecord.java,v 1.10 2013/10/29 16:57:57 eby Exp $
+ */
+public class BaseLogRecord implements LOGJSONable, Loadable {
+ protected static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
+
+ private long eventTime;
+ private String publishId;
+ private int feedid;
+ private String requestUri;
+ private String method;
+ private String contentType;
+ private long contentLength;
+
+ protected BaseLogRecord(String[] pp) throws ParseException {
+// This throws exceptions occasionally - don't know why.
+// Date d = null;
+// synchronized (sdf) {
+// d = sdf.parse(pp[0]);
+// }
+ Date d = parseDate(pp[0]);
+ this.eventTime = d.getTime();
+ this.publishId = pp[2];
+ this.feedid = Integer.parseInt(pp[3]);
+ if (pp[1].equals("DLX")) {
+ this.requestUri = "";
+ this.method = "GET"; // Note: we need a valid value in this field, even though unused
+ this.contentType = "";
+ this.contentLength = Long.parseLong(pp[5]);
+ } else if (pp[1].equals("PUB") || pp[1].equals("LOG") || pp[1].equals("PBF")) {
+ this.requestUri = pp[4];
+ this.method = pp[5];
+ this.contentType = pp[6];
+ this.contentLength = Long.parseLong(pp[7]);
+ } else {
+ this.requestUri = pp[5];
+ this.method = pp[6];
+ this.contentType = pp[7];
+ this.contentLength = Long.parseLong(pp[8]);
+ }
+ }
+ protected BaseLogRecord(ResultSet rs) throws SQLException {
+ this.eventTime = rs.getLong("EVENT_TIME");
+ this.publishId = rs.getString("PUBLISH_ID");
+ this.feedid = rs.getInt("FEEDID");
+ this.requestUri = rs.getString("REQURI");
+ this.method = rs.getString("METHOD");
+ this.contentType = rs.getString("CONTENT_TYPE");
+ this.contentLength = rs.getLong("CONTENT_LENGTH");
+ }
+ protected Date parseDate(final String s) throws ParseException {
+ int[] n = new int[7];
+ int p = 0;
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if (c < '0' || c > '9') {
+ p++;
+ } else {
+ if (p > n.length)
+ throw new ParseException("parseDate()", 0);
+ n[p] = (n[p] * 10) + (c - '0');
+ }
+ }
+ if (p != 7)
+ throw new ParseException("parseDate()", 1);
+ Calendar cal = new GregorianCalendar();
+ cal.set(Calendar.YEAR, n[0]);
+ cal.set(Calendar.MONTH, n[1]-1);
+ cal.set(Calendar.DAY_OF_MONTH, n[2]);
+ cal.set(Calendar.HOUR_OF_DAY, n[3]);
+ cal.set(Calendar.MINUTE, n[4]);
+ cal.set(Calendar.SECOND, n[5]);
+ cal.set(Calendar.MILLISECOND, n[6]);
+ return cal.getTime();
+ }
+ public long getEventTime() {
+ return eventTime;
+ }
+ public void setEventTime(long eventTime) {
+ this.eventTime = eventTime;
+ }
+ public String getPublishId() {
+ return publishId;
+ }
+ public void setPublishId(String publishId) {
+ this.publishId = publishId;
+ }
+ public int getFeedid() {
+ return feedid;
+ }
+ public void setFeedid(int feedid) {
+ this.feedid = feedid;
+ }
+ public String getRequestUri() {
+ return requestUri;
+ }
+ public void setRequestUri(String requestUri) {
+ this.requestUri = requestUri;
+ }
+ public String getMethod() {
+ return method;
+ }
+ public void setMethod(String method) {
+ this.method = method;
+ }
+ public String getContentType() {
+ return contentType;
+ }
+ public void setContentType(String contentType) {
+ this.contentType = contentType;
+ }
+ public long getContentLength() {
+ return contentLength;
+ }
+ public void setContentLength(long contentLength) {
+ this.contentLength = contentLength;
+ }
+ @Override
+ public LOGJSONObject asJSONObject() {
+ LOGJSONObject jo = new LOGJSONObject();
+ String t = "";
+ synchronized (sdf) {
+ t = sdf.format(eventTime);
+ }
+ jo.put("date", t);
+ jo.put("publishId", publishId);
+ jo.put("requestURI", requestUri);
+ jo.put("method", method);
+ if (method.equals("PUT")) {
+ jo.put("contentType", contentType);
+ jo.put("contentLength", contentLength);
+ }
+ return jo;
+ }
+ @Override
+ public void load(PreparedStatement ps) throws SQLException {
+ ps.setLong (2, getEventTime());
+ ps.setString(3, getPublishId());
+ ps.setInt (4, getFeedid());
+ ps.setString(5, getRequestUri());
+ ps.setString(6, getMethod());
+ ps.setString(7, getContentType());
+ ps.setLong (8, getContentLength());
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Deleteable.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Deleteable.java
new file mode 100644
index 00000000..c16bdbc1
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Deleteable.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.beans;
+
+import java.sql.Connection;
+
+/**
+ * An object that can be DELETE-ed from the database.
+ * @author Robert Eby
+ * @version $Id: Deleteable.java,v 1.2 2013/05/29 14:44:36 eby Exp $
+ */
+public interface Deleteable {
+ /**
+ * Delete this object in the DB.
+ * @param c the JDBC Connection to use
+ * @return true if the DELETE succeeded, false otherwise
+ */
+ public boolean doDelete(Connection c);
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/DeliveryExtraRecord.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/DeliveryExtraRecord.java
new file mode 100644
index 00000000..1a1cb569
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/DeliveryExtraRecord.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.beans;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.text.ParseException;
+
+/**
+ * The representation of a Delivery Extra (DLX) Record, as retrieved from the DB.
+ * @author Robert Eby
+ * @version $Id: DeliveryExtraRecord.java,v 1.1 2013/10/28 18:06:52 eby Exp $
+ */
+public class DeliveryExtraRecord extends BaseLogRecord {
+ private int subid;
+ private long contentLength2;
+
+ public DeliveryExtraRecord(String[] pp) throws ParseException {
+ super(pp);
+ this.subid = Integer.parseInt(pp[4]);
+ this.contentLength2 = Long.parseLong(pp[6]);
+ }
+ public DeliveryExtraRecord(ResultSet rs) throws SQLException {
+ super(rs);
+ // Note: because this record should be "rare" these fields are mapped to unconventional fields in the DB
+ this.subid = rs.getInt("DELIVERY_SUBID");
+ this.contentLength2 = rs.getInt("CONTENT_LENGTH_2");
+ }
+ @Override
+ public void load(PreparedStatement ps) throws SQLException {
+ ps.setString(1, "dlx"); // field 1: type
+ super.load(ps); // loads fields 2-8
+ ps.setNull( 9, Types.VARCHAR);
+ ps.setNull(10, Types.VARCHAR);
+ ps.setNull(11, Types.VARCHAR);
+ ps.setNull(12, Types.INTEGER);
+ ps.setInt (13, subid);
+ ps.setNull(14, Types.VARCHAR);
+ ps.setNull(15, Types.INTEGER);
+ ps.setNull(16, Types.INTEGER);
+ ps.setNull(17, Types.VARCHAR);
+ ps.setLong(19, contentLength2);
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/DeliveryRecord.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/DeliveryRecord.java
new file mode 100644
index 00000000..b4791d48
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/DeliveryRecord.java
@@ -0,0 +1,137 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.beans;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.text.ParseException;
+import java.util.LinkedHashMap;
+
+import org.json.LOGJSONObject;
+
+/**
+ * The representation of a Delivery Record, as retrieved from the DB.
+ * @author Robert Eby
+ * @version $Id: DeliveryRecord.java,v 1.9 2014/03/12 19:45:41 eby Exp $
+ */
+public class DeliveryRecord extends BaseLogRecord {
+ private int subid;
+ private String fileid;
+ private int result;
+ private String user;
+
+ public DeliveryRecord(String[] pp) throws ParseException {
+ super(pp);
+ String fileid = pp[5];
+ if (fileid.lastIndexOf('/') >= 0)
+ fileid = fileid.substring(fileid.lastIndexOf('/')+1);
+ this.subid = Integer.parseInt(pp[4]);
+ this.fileid = fileid;
+ this.result = Integer.parseInt(pp[10]);
+ this.user = pp[9];
+ if (this.user != null && this.user.length() > 50)
+ this.user = this.user.substring(0, 50);
+ }
+ public DeliveryRecord(ResultSet rs) throws SQLException {
+ super(rs);
+ this.subid = rs.getInt("DELIVERY_SUBID");
+ this.fileid = rs.getString("DELIVERY_FILEID");
+ this.result = rs.getInt("RESULT");
+ this.user = rs.getString("USER");
+ }
+ public int getSubid() {
+ return subid;
+ }
+ public void setSubid(int subid) {
+ this.subid = subid;
+ }
+ public String getFileid() {
+ return fileid;
+ }
+ public void setFileid(String fileid) {
+ this.fileid = fileid;
+ }
+ public int getResult() {
+ return result;
+ }
+ public void setResult(int result) {
+ this.result = result;
+ }
+ public String getUser() {
+ return user;
+ }
+ public void setUser(String user) {
+ this.user = user;
+ }
+
+
+ public LOGJSONObject reOrderObject(LOGJSONObject jo) {
+ LinkedHashMap<String,Object> logrecordObj = new LinkedHashMap<String,Object>();
+
+ logrecordObj.put("statusCode", jo.get("statusCode"));
+ logrecordObj.put("deliveryId", jo.get("deliveryId"));
+ logrecordObj.put("publishId", jo.get("publishId"));
+ logrecordObj.put("requestURI", jo.get("requestURI"));
+ //logrecordObj.put("sourceIP", jo.get("sourceIP"));
+ logrecordObj.put("method", jo.get("method"));
+ logrecordObj.put("contentType", jo.get("contentType"));
+ //logrecordObj.put("endpointId", jo.get("endpointId"));
+ logrecordObj.put("type", jo.get("type"));
+ logrecordObj.put("date", jo.get("date"));
+ logrecordObj.put("contentLength", jo.get("contentLength"));
+
+
+ LOGJSONObject newjo = new LOGJSONObject(logrecordObj);
+ return newjo;
+ }
+
+ @Override
+ public LOGJSONObject asJSONObject() {
+ LOGJSONObject jo = super.asJSONObject();
+ jo.put("type", "del");
+ jo.put("deliveryId", user);
+ jo.put("statusCode", result);
+
+ LOGJSONObject newjo = this.reOrderObject(jo);
+ return newjo;
+ }
+ @Override
+ public void load(PreparedStatement ps) throws SQLException {
+ ps.setString(1, "del"); // field 1: type
+ super.load(ps); // loads fields 2-8
+ ps.setNull (9, Types.VARCHAR);
+ ps.setNull (10, Types.VARCHAR);
+ ps.setString(11, getUser());
+ ps.setNull (12, Types.INTEGER);
+ ps.setInt (13, getSubid());
+ ps.setString(14, getFileid());
+ ps.setInt (15, getResult());
+ ps.setNull (16, Types.INTEGER);
+ ps.setNull (17, Types.VARCHAR);
+ ps.setNull (19, Types.BIGINT);
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/EgressRoute.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/EgressRoute.java
new file mode 100644
index 00000000..94b59ce4
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/EgressRoute.java
@@ -0,0 +1,227 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.beans;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.apache.log4j.Logger;
+import org.json.JSONObject;
+
+import com.att.research.datarouter.provisioning.utils.DB;
+
+/**
+ * The representation of one route in the Egress Route Table.
+ *
+ * @author Robert P. Eby
+ * @version $Id: EgressRoute.java,v 1.3 2013/12/16 20:30:23 eby Exp $
+ */
+public class EgressRoute extends NodeClass implements Comparable<EgressRoute> {
+ private static Logger intlogger = Logger.getLogger("com.att.research.datarouter.provisioning.internal");
+ private final int subid;
+ private final int nodeid;
+
+ /**
+ * Get a set of all Egress Routes in the DB. The set is sorted according to the natural sorting order
+ * of the routes (based on the subscription ID in each route).
+ * @return the sorted set
+ */
+ public static SortedSet<EgressRoute> getAllEgressRoutes() {
+ SortedSet<EgressRoute> set = new TreeSet<EgressRoute>();
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery("select SUBID, NODEID from EGRESS_ROUTES");
+ while (rs.next()) {
+ int subid = rs.getInt("SUBID");
+ int nodeid = rs.getInt("NODEID");
+ set.add(new EgressRoute(subid, nodeid));
+ }
+ rs.close();
+ stmt.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return set;
+ }
+ /**
+ * Get a single Egress Route for the subscription <i>sub</i>.
+ * @param sub the subscription to lookup
+ * @return an EgressRoute, or null if there is no route for this subscription
+ */
+ public static EgressRoute getEgressRoute(int sub) {
+ EgressRoute v = null;
+ PreparedStatement ps = null;
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ String sql = "select NODEID from EGRESS_ROUTES where SUBID = ?";
+ ps = conn.prepareStatement(sql);
+ ps.setInt(1, sub);
+ ResultSet rs = ps.executeQuery();
+ if (rs.next()) {
+ int node = rs.getInt("NODEID");
+ v = new EgressRoute(sub, node);
+ }
+ rs.close();
+ ps.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ return v;
+ }
+
+ public EgressRoute(int subid, int nodeid) throws IllegalArgumentException {
+ this.subid = subid;
+ this.nodeid = nodeid;
+// Note: unlike for Feeds, it subscriptions can be removed from the tables, so it is
+// possible that an orphan ERT entry can exist if a sub is removed.
+// if (Subscription.getSubscriptionById(subid) == null)
+// throw new IllegalArgumentException("No such subscription: "+subid);
+ }
+
+ public EgressRoute(int subid, String node) throws IllegalArgumentException {
+ this(subid, lookupNodeName(node));
+ }
+
+ @Override
+ public boolean doDelete(Connection c) {
+ boolean rv = true;
+ PreparedStatement ps = null;
+ try {
+ String sql = "delete from EGRESS_ROUTES where SUBID = ?";
+ ps = c.prepareStatement(sql);
+ ps.setInt(1, subid);
+ ps.execute();
+ } catch (SQLException e) {
+ rv = false;
+ intlogger.warn("PROV0007 doDelete: "+e.getMessage());
+ e.printStackTrace();
+ } finally {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ return rv;
+ }
+
+ @Override
+ public boolean doInsert(Connection c) {
+ boolean rv = false;
+ PreparedStatement ps = null;
+ try {
+ // Create the NETWORK_ROUTES row
+ String sql = "insert into EGRESS_ROUTES (SUBID, NODEID) values (?, ?)";
+ ps = c.prepareStatement(sql);
+ ps.setInt(1, this.subid);
+ ps.setInt(2, this.nodeid);
+ ps.execute();
+ ps.close();
+ rv = true;
+ } catch (SQLException e) {
+ intlogger.warn("PROV0005 doInsert: "+e.getMessage());
+ e.printStackTrace();
+ } finally {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ return rv;
+ }
+
+ @Override
+ public boolean doUpdate(Connection c) {
+ boolean rv = true;
+ PreparedStatement ps = null;
+ try {
+ String sql = "update EGRESS_ROUTES set NODEID = ? where SUBID = ?";
+ ps = c.prepareStatement(sql);
+ ps.setInt(1, nodeid);
+ ps.setInt(2, subid);
+ ps.executeUpdate();
+ } catch (SQLException e) {
+ rv = false;
+ intlogger.warn("PROV0006 doUpdate: "+e.getMessage());
+ e.printStackTrace();
+ } finally {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ return rv;
+ }
+
+ @Override
+ public JSONObject asJSONObject() {
+ JSONObject jo = new JSONObject();
+ jo.put(""+subid, lookupNodeID(nodeid));
+ return jo;
+ }
+
+ @Override
+ public String getKey() {
+ return ""+subid;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof EgressRoute))
+ return false;
+ EgressRoute on = (EgressRoute)obj;
+ return (subid == on.subid) && (nodeid == on.nodeid);
+ }
+
+ @Override
+ public int compareTo(EgressRoute o) {
+ return this.subid - o.subid;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("EGRESS: sub=%d, node=%d", subid, nodeid);
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/EventLogRecord.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/EventLogRecord.java
new file mode 100644
index 00000000..adf45d49
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/EventLogRecord.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.beans;
+
+import java.security.cert.X509Certificate;
+
+import javax.servlet.http.HttpServletRequest;
+
+import com.att.research.datarouter.provisioning.BaseServlet;
+
+/**
+ * This class is used to log provisioning server events. Each event consists of a who
+ * (who made the provisioning request including the IP address, the X-ATT-DR-ON-BEHALF-OF
+ * header value, and the client certificate), a what (what request was made; the method
+ * and servlet involved), and a how (how the request was handled; the result code and
+ * message returned to the client). EventLogRecords are logged using log4j at the INFO level.
+ *
+ * @author Robert Eby
+ * @version $Id: EventLogRecord.java,v 1.1 2013/04/26 21:00:25 eby Exp $
+ */
+public class EventLogRecord {
+ private final String ipaddr; // Who
+ private final String behalfof;
+ private final String clientSubject;
+ private final String method; // What
+ private final String servlet;
+ private int result; // How
+ private String message;
+
+ public EventLogRecord(HttpServletRequest request) {
+ // Who is making the request
+ this.ipaddr = request.getRemoteAddr();
+ String s = request.getHeader(BaseServlet.BEHALF_HEADER);
+ this.behalfof = (s != null) ? s : "";
+ X509Certificate certs[] = (X509Certificate[]) request.getAttribute(BaseServlet.CERT_ATTRIBUTE);
+ this.clientSubject = (certs != null && certs.length > 0)
+ ? certs[0].getSubjectX500Principal().getName() : "";
+
+ // What is the request
+ this.method = request.getMethod();
+ this.servlet = request.getServletPath();
+
+ // How was it dealt with
+ this.result = -1;
+ this.message = "";
+ }
+ public void setResult(int result) {
+ this.result = result;
+ }
+ public void setMessage(String message) {
+ this.message = message;
+ }
+ @Override
+ public String toString() {
+ return String.format(
+ "%s %s \"%s\" %s %s %d \"%s\"",
+ ipaddr, behalfof, clientSubject,
+ method, servlet,
+ result, message
+ );
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/ExpiryRecord.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/ExpiryRecord.java
new file mode 100644
index 00000000..1db5417b
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/ExpiryRecord.java
@@ -0,0 +1,141 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.beans;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.text.ParseException;
+import java.util.LinkedHashMap;
+
+import org.json.LOGJSONObject;
+
+/**
+ * The representation of a Expiry Record, as retrieved from the DB.
+ * @author Robert Eby
+ * @version $Id: ExpiryRecord.java,v 1.4 2013/10/28 18:06:52 eby Exp $
+ */
+public class ExpiryRecord extends BaseLogRecord {
+ private int subid;
+ private String fileid;
+ private int attempts;
+ private String reason;
+
+ public ExpiryRecord(String[] pp) throws ParseException {
+ super(pp);
+ String fileid = pp[5];
+ if (fileid.lastIndexOf('/') >= 0)
+ fileid = fileid.substring(fileid.lastIndexOf('/')+1);
+ this.subid = Integer.parseInt(pp[4]);
+ this.fileid = fileid;
+ this.attempts = Integer.parseInt(pp[10]);
+ this.reason = pp[9];
+ if (!reason.equals("notRetryable") && !reason.equals("retriesExhausted") && !reason.equals("diskFull"))
+ this.reason = "other";
+ }
+ public ExpiryRecord(ResultSet rs) throws SQLException {
+ super(rs);
+ this.subid = rs.getInt("DELIVERY_SUBID");
+ this.fileid = rs.getString("DELIVERY_FILEID");
+ this.attempts = rs.getInt("ATTEMPTS");
+ this.reason = rs.getString("REASON");
+ }
+
+ public int getSubid() {
+ return subid;
+ }
+
+ public void setSubid(int subid) {
+ this.subid = subid;
+ }
+
+ public String getFileid() {
+ return fileid;
+ }
+
+ public void setFileid(String fileid) {
+ this.fileid = fileid;
+ }
+
+ public int getAttempts() {
+ return attempts;
+ }
+
+ public void setAttempts(int attempts) {
+ this.attempts = attempts;
+ }
+
+ public String getReason() {
+ return reason;
+ }
+
+ public void setReason(String reason) {
+ this.reason = reason;
+ }
+
+ public LOGJSONObject reOrderObject(LOGJSONObject jo) {
+ LinkedHashMap<String,Object> logrecordObj = new LinkedHashMap<String,Object>();
+
+ logrecordObj.put("expiryReason", jo.get("expiryReason"));
+ logrecordObj.put("publishId", jo.get("publishId"));
+ logrecordObj.put("attempts", jo.get("attempts"));
+ logrecordObj.put("requestURI", jo.get("requestURI"));
+ logrecordObj.put("method", jo.get("method"));
+ logrecordObj.put("contentType", jo.get("contentType"));
+ logrecordObj.put("type", jo.get("type"));
+ logrecordObj.put("date", jo.get("date"));
+ logrecordObj.put("contentLength", jo.get("contentLength"));
+
+ LOGJSONObject newjo = new LOGJSONObject(logrecordObj);
+ return newjo;
+ }
+
+ @Override
+ public LOGJSONObject asJSONObject() {
+ LOGJSONObject jo = super.asJSONObject();
+ jo.put("type", "exp");
+ jo.put("expiryReason", reason);
+ jo.put("attempts", attempts);
+
+ LOGJSONObject newjo = this.reOrderObject(jo);
+ return newjo;
+ }
+ @Override
+ public void load(PreparedStatement ps) throws SQLException {
+ ps.setString(1, "exp"); // field 1: type
+ super.load(ps); // loads fields 2-8
+ ps.setNull (9, Types.VARCHAR);
+ ps.setNull (10, Types.VARCHAR);
+ ps.setNull (11, Types.VARCHAR);
+ ps.setNull (12, Types.INTEGER);
+ ps.setInt (13, getSubid());
+ ps.setString(14, getFileid());
+ ps.setNull (15, Types.INTEGER);
+ ps.setInt (16, getAttempts());
+ ps.setString(17, getReason());
+ ps.setNull (19, Types.BIGINT);
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Feed.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Feed.java
new file mode 100644
index 00000000..4ee5ab96
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Feed.java
@@ -0,0 +1,760 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.beans;
+
+import java.io.InvalidObjectException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.log4j.Logger;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import com.att.research.datarouter.provisioning.utils.DB;
+import com.att.research.datarouter.provisioning.utils.JSONUtilities;
+import com.att.research.datarouter.provisioning.utils.URLUtilities;
+
+/**
+ * The representation of a Feed. Feeds can be retrieved from the DB, or stored/updated in the DB.
+ * @author Robert Eby
+ * @version $Id: Feed.java,v 1.13 2013/10/28 18:06:52 eby Exp $
+ */
+public class Feed extends Syncable {
+ private static Logger intlogger = Logger.getLogger("com.att.research.datarouter.provisioning.internal");
+ private static int next_feedid = getMaxFeedID() + 1;
+
+ private int feedid;
+ private int groupid; //New field is added - Groups feature Rally:US708115 - 1610
+ private String name;
+ private String version;
+ private String description;
+ private String business_description; // New field is added - Groups feature Rally:US708102 - 1610
+ private FeedAuthorization authorization;
+ private String publisher;
+ private FeedLinks links;
+ private boolean deleted;
+ private boolean suspended;
+ private Date last_mod;
+ private Date created_date;
+
+ /**
+ * Check if a feed ID is valid.
+ * @param id the Feed ID
+ * @return true if it is valid
+ */
+ @SuppressWarnings("resource")
+ public static boolean isFeedValid(int id) {
+ int count = 0;
+ try {
+ DB db = new DB();
+ Connection conn = db.getConnection();
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery("select COUNT(*) from FEEDS where FEEDID = " + id);
+ if (rs.next()) {
+ count = rs.getInt(1);
+ }
+ rs.close();
+ stmt.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return count != 0;
+ }
+ /**
+ * Get a specific feed from the DB, based upon its ID.
+ * @param id the Feed ID
+ * @return the Feed object, or null if it does not exist
+ */
+ public static Feed getFeedById(int id) {
+ String sql = "select * from FEEDS where FEEDID = " + id;
+ return getFeedBySQL(sql);
+ }
+ /**
+ * Get a specific feed from the DB, based upon its name and version.
+ * @param name the name of the Feed
+ * @param version the version of the Feed
+ * @return the Feed object, or null if it does not exist
+ */
+ public static Feed getFeedByNameVersion(String name, String version) {
+ name = name.replaceAll("'", "''");
+ version = version.replaceAll("'", "''");
+ String sql = "select * from FEEDS where NAME = '" + name + "' and VERSION ='" + version + "'";
+ return getFeedBySQL(sql);
+ }
+ /**
+ * Return a count of the number of active feeds in the DB.
+ * @return the count
+ */
+ public static int countActiveFeeds() {
+ int count = 0;
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery("select count(*) from FEEDS where DELETED = 0");
+ if (rs.next()) {
+ count = rs.getInt(1);
+ }
+ rs.close();
+ stmt.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ intlogger.info("countActiveFeeds: "+e.getMessage());
+ e.printStackTrace();
+ }
+ return count;
+ }
+ public static int getMaxFeedID() {
+ int max = 0;
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery("select MAX(feedid) from FEEDS");
+ if (rs.next()) {
+ max = rs.getInt(1);
+ }
+ rs.close();
+ stmt.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ intlogger.info("getMaxFeedID: "+e.getMessage());
+ e.printStackTrace();
+ }
+ return max;
+ }
+ public static Collection<Feed> getAllFeeds() {
+ Map<Integer, Feed> map = new HashMap<Integer, Feed>();
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery("select * from FEEDS");
+ while (rs.next()) {
+ Feed feed = new Feed(rs);
+ map.put(feed.getFeedid(), feed);
+ }
+ rs.close();
+
+ String sql = "select * from FEED_ENDPOINT_IDS";
+ rs = stmt.executeQuery(sql);
+ while (rs.next()) {
+ int id = rs.getInt("FEEDID");
+ Feed feed = map.get(id);
+ if (feed != null) {
+ FeedEndpointID epi = new FeedEndpointID(rs);
+ Collection<FeedEndpointID> ecoll = feed.getAuthorization().getEndpoint_ids();
+ ecoll.add(epi);
+ }
+ }
+ rs.close();
+
+ sql = "select * from FEED_ENDPOINT_ADDRS";
+ rs = stmt.executeQuery(sql);
+ while (rs.next()) {
+ int id = rs.getInt("FEEDID");
+ Feed feed = map.get(id);
+ if (feed != null) {
+ Collection<String> acoll = feed.getAuthorization().getEndpoint_addrs();
+ acoll.add(rs.getString("ADDR"));
+ }
+ }
+ rs.close();
+
+ stmt.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return map.values();
+ }
+ public static List<String> getFilteredFeedUrlList(final String name, final String val) {
+ List<String> list = new ArrayList<String>();
+ String sql = "select SELF_LINK from FEEDS where DELETED = 0";
+ if (name.equals("name")) {
+ sql += " and NAME = ?";
+ } else if (name.equals("publ")) {
+ sql += " and PUBLISHER = ?";
+ } else if (name.equals("subs")) {
+ sql = "select distinct FEEDS.SELF_LINK from FEEDS, SUBSCRIPTIONS " +
+ "where DELETED = 0 " +
+ "and FEEDS.FEEDID = SUBSCRIPTIONS.FEEDID " +
+ "and SUBSCRIPTIONS.SUBSCRIBER = ?";
+ }
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ PreparedStatement ps = conn.prepareStatement(sql);
+ if (sql.indexOf('?') >= 0)
+ ps.setString(1, val);
+ ResultSet rs = ps.executeQuery();
+ while (rs.next()) {
+ String t = rs.getString(1);
+ list.add(t.trim());
+ }
+ rs.close();
+ ps.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return list;
+ }
+ @SuppressWarnings("resource")
+ private static Feed getFeedBySQL(String sql) {
+ Feed feed = null;
+ try {
+ DB db = new DB();
+ Connection conn = db.getConnection();
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery(sql);
+ if (rs.next()) {
+ feed = new Feed(rs);
+ rs.close();
+
+ sql = "select * from FEED_ENDPOINT_IDS where FEEDID = " + feed.feedid;
+ rs = stmt.executeQuery(sql);
+ Collection<FeedEndpointID> ecoll = feed.getAuthorization().getEndpoint_ids();
+ while (rs.next()) {
+ FeedEndpointID epi = new FeedEndpointID(rs);
+ ecoll.add(epi);
+ }
+ rs.close();
+
+ sql = "select * from FEED_ENDPOINT_ADDRS where FEEDID = " + feed.feedid;
+ rs = stmt.executeQuery(sql);
+ Collection<String> acoll = feed.getAuthorization().getEndpoint_addrs();
+ while (rs.next()) {
+ acoll.add(rs.getString("ADDR"));
+ }
+ }
+ rs.close();
+ stmt.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return feed;
+ }
+
+ public Feed() {
+ this("", "", "","");
+ }
+
+ public Feed(String name, String version, String desc,String business_description) {
+ this.feedid = -1;
+ this.groupid = -1; //New field is added - Groups feature Rally:US708115 - 1610
+ this.name = name;
+ this.version = version;
+ this.description = desc;
+ this.business_description=business_description; // New field is added - Groups feature Rally:US708102 - 1610
+ this.authorization = new FeedAuthorization();
+ this.publisher = "";
+ this.links = new FeedLinks();
+ this.deleted = false;
+ this.suspended = false;
+ this.last_mod = new Date();
+ this.created_date = new Date();
+ }
+ public Feed(ResultSet rs) throws SQLException {
+ this.feedid = rs.getInt("FEEDID");
+ this.groupid = rs.getInt("GROUPID"); //New field is added - Groups feature Rally:US708115 - 1610
+ this.name = rs.getString("NAME");
+ this.version = rs.getString("VERSION");
+ this.description = rs.getString("DESCRIPTION");
+ this.business_description=rs.getString("BUSINESS_DESCRIPTION"); // New field is added - Groups feature Rally:US708102 - 1610
+ this.authorization = new FeedAuthorization();
+ this.authorization.setClassification(rs.getString("AUTH_CLASS"));
+ this.publisher = rs.getString("PUBLISHER");
+ this.links = new FeedLinks();
+ this.links.setSelf(rs.getString("SELF_LINK"));
+ this.links.setPublish(rs.getString("PUBLISH_LINK"));
+ this.links.setSubscribe(rs.getString("SUBSCRIBE_LINK"));
+ this.links.setLog(rs.getString("LOG_LINK"));
+ this.deleted = rs.getBoolean("DELETED");
+ this.suspended = rs.getBoolean("SUSPENDED");
+ this.last_mod = rs.getDate("LAST_MOD");
+ this.created_date = rs.getTimestamp("CREATED_DATE");
+ }
+ public Feed(JSONObject jo) throws InvalidObjectException {
+ this("", "", "","");
+ try {
+ // The JSONObject is assumed to contain a vnd.att-dr.feed representation
+ this.feedid = jo.optInt("feedid", -1);
+ this.groupid = jo.optInt("groupid"); //New field is added - Groups feature Rally:US708115 - 1610
+ this.name = jo.getString("name");
+ if (name.length() > 255)
+ throw new InvalidObjectException("name field is too long");
+ this.version = jo.getString("version");
+ if (version.length() > 20)
+ throw new InvalidObjectException("version field is too long");
+ this.description = jo.optString("description");
+ this.business_description = jo.optString("business_description"); // New field is added - Groups feature Rally:US708102 - 1610
+ if (description.length() > 1000)
+ throw new InvalidObjectException("technical description field is too long");
+
+ if (business_description.length() > 1000) // New field is added - Groups feature Rally:US708102 - 1610
+ throw new InvalidObjectException("business description field is too long");
+
+ this.authorization = new FeedAuthorization();
+ JSONObject jauth = jo.getJSONObject("authorization");
+ this.authorization.setClassification(jauth.getString("classification"));
+ if (this.authorization.getClassification().length() > 32)
+ throw new InvalidObjectException("classification field is too long");
+ JSONArray ja = jauth.getJSONArray("endpoint_ids");
+ for (int i = 0; i < ja.length(); i++) {
+ JSONObject id = ja.getJSONObject(i);
+ FeedEndpointID fid = new FeedEndpointID(id.getString("id"), id.getString("password"));
+ if (fid.getId().length() > 20)
+ throw new InvalidObjectException("id field is too long ("+fid.getId()+")");
+ if (fid.getPassword().length() > 32)
+ throw new InvalidObjectException("password field is too long ("+fid.getPassword()+")");
+ this.authorization.getEndpoint_ids().add(fid);
+ }
+ if (this.authorization.getEndpoint_ids().size() < 1)
+ throw new InvalidObjectException("need to specify at least one endpoint_id");
+ ja = jauth.getJSONArray("endpoint_addrs");
+ for (int i = 0; i < ja.length(); i++) {
+ String addr = ja.getString(i);
+ if (!JSONUtilities.validIPAddrOrSubnet(addr))
+ throw new InvalidObjectException("bad IP addr or subnet mask: "+addr);
+ this.authorization.getEndpoint_addrs().add(addr);
+ }
+
+ this.publisher = jo.optString("publisher", "");
+ this.deleted = jo.optBoolean("deleted", false);
+ this.suspended = jo.optBoolean("suspend", false);
+ JSONObject jol = jo.optJSONObject("links");
+ this.links = (jol == null) ? (new FeedLinks()) : (new FeedLinks(jol));
+ } catch (InvalidObjectException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new InvalidObjectException("invalid JSON: "+e.getMessage());
+ }
+ }
+ public int getFeedid() {
+ return feedid;
+ }
+ public void setFeedid(int feedid) {
+ this.feedid = feedid;
+
+ // Create link URLs
+ FeedLinks fl = getLinks();
+ fl.setSelf(URLUtilities.generateFeedURL(feedid));
+ fl.setPublish(URLUtilities.generatePublishURL(feedid));
+ fl.setSubscribe(URLUtilities.generateSubscribeURL(feedid));
+ fl.setLog(URLUtilities.generateFeedLogURL(feedid));
+ }
+
+ //new getter setters for groups- Rally:US708115 - 1610
+ public int getGroupid() {
+ return groupid;
+ }
+
+ public void setGroupid(int groupid) {
+ this.groupid = groupid;
+ }
+
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public String getVersion() {
+ return version;
+ }
+ public void setVersion(String version) {
+ this.version = version;
+ }
+ public String getDescription() {
+ return description;
+ }
+ public void setDescription(String description) {
+ this.description = description;
+ }
+ // New field is added - Groups feature Rally:US708102 - 1610
+ public String getBusiness_description() {
+ return business_description;
+ }
+
+ public void setBusiness_description(String business_description) {
+ this.business_description = business_description;
+ }
+
+ public FeedAuthorization getAuthorization() {
+ return authorization;
+ }
+ public void setAuthorization(FeedAuthorization authorization) {
+ this.authorization = authorization;
+ }
+ public String getPublisher() {
+ return publisher;
+ }
+ public void setPublisher(String publisher) {
+ if (publisher != null) {
+ if (publisher.length() > 8)
+ publisher = publisher.substring(0, 8);
+ this.publisher = publisher;
+ }
+ }
+ public FeedLinks getLinks() {
+ return links;
+ }
+ public void setLinks(FeedLinks links) {
+ this.links = links;
+ }
+
+ public boolean isDeleted() {
+ return deleted;
+ }
+
+ public void setDeleted(boolean deleted) {
+ this.deleted = deleted;
+ }
+
+ public boolean isSuspended() {
+ return suspended;
+ }
+
+ public void setSuspended(boolean suspended) {
+ this.suspended = suspended;
+ }
+
+ public Date getLast_mod() {
+ return last_mod;
+ }
+
+ public Date getCreated_date() {
+ return created_date;
+ }
+
+ @Override
+ public JSONObject asJSONObject() {
+ JSONObject jo = new JSONObject();
+ jo.put("feedid", feedid);
+ jo.put("groupid", groupid); //New field is added - Groups feature Rally:US708115 - 1610
+ jo.put("name", name);
+ jo.put("version", version);
+ jo.put("description", description);
+ jo.put("business_description", business_description); // New field is added - Groups feature Rally:US708102 - 1610
+ jo.put("authorization", authorization.asJSONObject());
+ jo.put("publisher", publisher);
+ jo.put("links", links.asJSONObject());
+ jo.put("deleted", deleted);
+ jo.put("suspend", suspended);
+ jo.put("last_mod", last_mod.getTime());
+ jo.put("created_date", created_date.getTime());
+ return jo;
+ }
+ public JSONObject asLimitedJSONObject() {
+ JSONObject jo = asJSONObject();
+ jo.remove("deleted");
+ jo.remove("feedid");
+ jo.remove("last_mod");
+ jo.remove("created_date");
+ return jo;
+ }
+ public JSONObject asJSONObject(boolean hidepasswords) {
+ JSONObject jo = asJSONObject();
+ if (hidepasswords) {
+ jo.remove("feedid"); // we no longer hide passwords, however we do hide these
+ jo.remove("deleted");
+ jo.remove("last_mod");
+ jo.remove("created_date");
+ }
+ return jo;
+ }
+ @Override
+ public boolean doDelete(Connection c) {
+ boolean rv = true;
+ PreparedStatement ps = null;
+ try {
+ String sql = "delete from FEEDS where FEEDID = ?";
+ ps = c.prepareStatement(sql);
+ ps.setInt(1, feedid);
+ ps.execute();
+ } catch (SQLException e) {
+ rv = false;
+ intlogger.warn("PROV0007 doDelete: "+e.getMessage());
+ e.printStackTrace();
+ } finally {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ return rv;
+ }
+ @Override
+ public synchronized boolean doInsert(Connection c) {
+ boolean rv = true;
+// PreparedStatement ps = null;
+ try {
+ if (feedid == -1) {
+// // Get the next feedid
+// String sql = "insert into FEEDS_UNIQUEID (FEEDID) values (0)";
+// ps = c.prepareStatement(sql, new String[] { "FEEDID" });
+// ps.execute();
+// ResultSet rs = ps.getGeneratedKeys();
+// rs.first();
+// setFeedid(rs.getInt(1));
+ // No feed ID assigned yet, so assign the next available one
+ setFeedid(next_feedid++);
+ }
+ // In case we insert a feed from synchronization
+ if (feedid > next_feedid)
+ next_feedid = feedid+1;
+
+ // Create FEED_ENDPOINT_IDS rows
+ FeedAuthorization auth = getAuthorization();
+ String sql = "insert into FEED_ENDPOINT_IDS values (?, ?, ?)";
+ PreparedStatement ps2 = c.prepareStatement(sql);
+ for (FeedEndpointID fid : auth.getEndpoint_ids()) {
+ ps2.setInt(1, feedid);
+ ps2.setString(2, fid.getId());
+ ps2.setString(3, fid.getPassword());
+ ps2.executeUpdate();
+ }
+ ps2.close();
+
+ // Create FEED_ENDPOINT_ADDRS rows
+ sql = "insert into FEED_ENDPOINT_ADDRS values (?, ?)";
+ ps2 = c.prepareStatement(sql);
+ for (String t : auth.getEndpoint_addrs()) {
+ ps2.setInt(1, feedid);
+ ps2.setString(2, t);
+ ps2.executeUpdate();
+ }
+ ps2.close();
+
+ // Finally, create the FEEDS row
+ sql = "insert into FEEDS (FEEDID, NAME, VERSION, DESCRIPTION, AUTH_CLASS, PUBLISHER, SELF_LINK, PUBLISH_LINK, SUBSCRIBE_LINK, LOG_LINK, DELETED, SUSPENDED,BUSINESS_DESCRIPTION, GROUPID) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?, ?)";
+ ps2 = c.prepareStatement(sql);
+ ps2.setInt(1, feedid);
+ ps2.setString(2, getName());
+ ps2.setString(3, getVersion());
+ ps2.setString(4, getDescription());
+ ps2.setString(5, getAuthorization().getClassification());
+ ps2.setString(6, getPublisher());
+ ps2.setString(7, getLinks().getSelf());
+ ps2.setString(8, getLinks().getPublish());
+ ps2.setString(9, getLinks().getSubscribe());
+ ps2.setString(10, getLinks().getLog());
+ ps2.setBoolean(11, isDeleted());
+ ps2.setBoolean(12, isSuspended());
+ ps2.setString(13,getBusiness_description()); // New field is added - Groups feature Rally:US708102 - 1610
+ ps2.setInt(14,groupid); //New field is added - Groups feature Rally:US708115 - 1610
+ ps2.executeUpdate();
+ ps2.close();
+ } catch (SQLException e) {
+ rv = false;
+ intlogger.warn("PROV0005 doInsert: "+e.getMessage());
+ e.printStackTrace();
+// } finally {
+// try {
+// ps.close();
+// } catch (SQLException e) {
+// e.printStackTrace();
+// }
+ }
+ return rv;
+ }
+ @Override
+ public boolean doUpdate(Connection c) {
+ boolean rv = true;
+ Feed oldobj = getFeedById(feedid);
+ PreparedStatement ps = null;
+ try {
+ Set<FeedEndpointID> newset = getAuthorization().getEndpoint_ids();
+ Set<FeedEndpointID> oldset = oldobj.getAuthorization().getEndpoint_ids();
+
+ // Insert new FEED_ENDPOINT_IDS rows
+ String sql = "insert into FEED_ENDPOINT_IDS values (?, ?, ?)";
+ ps = c.prepareStatement(sql);
+ for (FeedEndpointID fid : newset) {
+ if (!oldset.contains(fid)) {
+ ps.setInt(1, feedid);
+ ps.setString(2, fid.getId());
+ ps.setString(3, fid.getPassword());
+ ps.executeUpdate();
+ }
+ }
+ ps.close();
+
+ // Delete old FEED_ENDPOINT_IDS rows
+ sql = "delete from FEED_ENDPOINT_IDS where FEEDID = ? AND USERID = ? AND PASSWORD = ?";
+ ps = c.prepareStatement(sql);
+ for (FeedEndpointID fid : oldset) {
+ if (!newset.contains(fid)) {
+ ps.setInt(1, feedid);
+ ps.setString(2, fid.getId());
+ ps.setString(3, fid.getPassword());
+ ps.executeUpdate();
+ }
+ }
+ ps.close();
+
+ // Insert new FEED_ENDPOINT_ADDRS rows
+ Set<String> newset2 = getAuthorization().getEndpoint_addrs();
+ Set<String> oldset2 = oldobj.getAuthorization().getEndpoint_addrs();
+ sql = "insert into FEED_ENDPOINT_ADDRS values (?, ?)";
+ ps = c.prepareStatement(sql);
+ for (String t : newset2) {
+ if (!oldset2.contains(t)) {
+ ps.setInt(1, feedid);
+ ps.setString(2, t);
+ ps.executeUpdate();
+ }
+ }
+ ps.close();
+
+ // Delete old FEED_ENDPOINT_ADDRS rows
+ sql = "delete from FEED_ENDPOINT_ADDRS where FEEDID = ? AND ADDR = ?";
+ ps = c.prepareStatement(sql);
+ for (String t : oldset2) {
+ if (!newset2.contains(t)) {
+ ps.setInt(1, feedid);
+ ps.setString(2, t);
+ ps.executeUpdate();
+ }
+ }
+ ps.close();
+
+ // Finally, update the FEEDS row
+ sql = "update FEEDS set DESCRIPTION = ?, AUTH_CLASS = ?, DELETED = ?, SUSPENDED = ?, BUSINESS_DESCRIPTION=?, GROUPID=? where FEEDID = ?";
+ ps = c.prepareStatement(sql);
+ ps.setString(1, getDescription());
+ ps.setString(2, getAuthorization().getClassification());
+ ps.setInt(3, deleted ? 1 : 0);
+ ps.setInt(4, suspended ? 1 : 0);
+ ps.setString(5, getBusiness_description()); // New field is added - Groups feature Rally:US708102 - 1610
+ ps.setInt(6, groupid); //New field is added - Groups feature Rally:US708115 - 1610
+ ps.setInt(7, feedid);
+ ps.executeUpdate();
+ ps.close();
+ } catch (SQLException e) {
+ rv = false;
+ intlogger.warn("PROV0006 doUpdate: "+e.getMessage());
+ e.printStackTrace();
+ } finally {
+ try {
+ if (ps != null)
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ return rv;
+ }
+
+ /**Rally US708115
+ * Change Ownership of FEED - 1610
+ * */
+ public boolean changeOwnerShip() {
+ boolean rv = true;
+ PreparedStatement ps = null;
+ try {
+
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection c = db.getConnection();
+ String sql = "update FEEDS set PUBLISHER = ? where FEEDID = ?";
+ ps = c.prepareStatement(sql);
+ ps.setString(1, this.publisher);
+ ps.setInt(2, feedid);
+ ps.execute();
+ ps.close();
+ } catch (SQLException e) {
+ rv = false;
+ intlogger.warn("PROV0006 doUpdate: "+e.getMessage());
+ e.printStackTrace();
+ } finally {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ return rv;
+ }
+
+
+ @Override
+ public String getKey() {
+ return ""+getFeedid();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Feed))
+ return false;
+ Feed of = (Feed) obj;
+ if (feedid != of.feedid)
+ return false;
+ if (groupid != of.groupid) //New field is added - Groups feature Rally:US708115 - 1610
+ return false;
+ if (!name.equals(of.name))
+ return false;
+ if (!version.equals(of.version))
+ return false;
+ if (!description.equals(of.description))
+ return false;
+ if (!business_description.equals(of.business_description)) // New field is added - Groups feature Rally:US708102 - 1610
+ return false;
+ if (!publisher.equals(of.publisher))
+ return false;
+ if (!authorization.equals(of.authorization))
+ return false;
+ if (!links.equals(of.links))
+ return false;
+ if (deleted != of.deleted)
+ return false;
+ if (suspended != of.suspended)
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "FEED: feedid=" + feedid + ", name=" + name + ", version=" + version;
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/FeedAuthorization.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/FeedAuthorization.java
new file mode 100644
index 00000000..5701ce9f
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/FeedAuthorization.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.beans;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+/**
+ * The representation of a Feed authorization. This encapsulates the authorization information about a feed.
+ * @author Robert Eby
+ * @version $Id: FeedAuthorization.java,v 1.2 2013/06/20 14:11:05 eby Exp $
+ */
+public class FeedAuthorization implements JSONable {
+ private String classification;
+ private Set<FeedEndpointID> endpoint_ids;
+ private Set<String> endpoint_addrs;
+
+ public FeedAuthorization() {
+ this.classification = "";
+ this.endpoint_ids = new HashSet<FeedEndpointID>();
+ this.endpoint_addrs = new HashSet<String>();
+ }
+ public String getClassification() {
+ return classification;
+ }
+ public void setClassification(String classification) {
+ this.classification = classification;
+ }
+ public Set<FeedEndpointID> getEndpoint_ids() {
+ return endpoint_ids;
+ }
+ public void setEndpoint_ids(Set<FeedEndpointID> endpoint_ids) {
+ this.endpoint_ids = endpoint_ids;
+ }
+ public Set<String> getEndpoint_addrs() {
+ return endpoint_addrs;
+ }
+ public void setEndpoint_addrs(Set<String> endpoint_addrs) {
+ this.endpoint_addrs = endpoint_addrs;
+ }
+
+ @Override
+ public JSONObject asJSONObject() {
+ JSONObject jo = new JSONObject();
+ jo.put("classification", classification);
+ JSONArray ja = new JSONArray();
+ for (FeedEndpointID eid : endpoint_ids) {
+ ja.put(eid.asJSONObject());
+ }
+ jo.put("endpoint_ids", ja);
+ ja = new JSONArray();
+ for (String t : endpoint_addrs) {
+ ja.put(t);
+ }
+ jo.put("endpoint_addrs", ja);
+ return jo;
+ }
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof FeedAuthorization))
+ return false;
+ FeedAuthorization of = (FeedAuthorization) obj;
+ if (!classification.equals(of.classification))
+ return false;
+ if (!endpoint_ids.equals(of.endpoint_ids))
+ return false;
+ if (!endpoint_addrs.equals(of.endpoint_addrs))
+ return false;
+ return true;
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/FeedEndpointID.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/FeedEndpointID.java
new file mode 100644
index 00000000..f009c64c
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/FeedEndpointID.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.beans;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.json.JSONObject;
+
+/**
+ * The representation of a Feed endpoint. This contains a login/password pair.
+ * @author Robert Eby
+ * @version $Id: FeedEndpointID.java,v 1.1 2013/04/26 21:00:26 eby Exp $
+ */
+public class FeedEndpointID implements JSONable {
+ private String id;
+ private String password;
+
+ public FeedEndpointID() {
+ this("", "");
+ }
+ public FeedEndpointID(String id, String password) {
+ this.id = id;
+ this.password = password;
+ }
+ public FeedEndpointID(ResultSet rs) throws SQLException {
+ this.id = rs.getString("USERID");
+ this.password = rs.getString("PASSWORD");
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ @Override
+ public JSONObject asJSONObject() {
+ JSONObject jo = new JSONObject();
+ jo.put("id", id);
+ jo.put("password", password);
+ return jo;
+ }
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof FeedEndpointID))
+ return false;
+ FeedEndpointID f2 = (FeedEndpointID) obj;
+ return id.equals(f2.id) && password.equals(f2.password);
+ }
+ @Override
+ public int hashCode() {
+ return (id + ":" + password).hashCode();
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/FeedLinks.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/FeedLinks.java
new file mode 100644
index 00000000..ccce9c4b
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/FeedLinks.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.beans;
+
+import java.io.InvalidObjectException;
+
+import org.json.JSONObject;
+
+/**
+ * The URLs associated with a Feed.
+ * @author Robert Eby
+ * @version $Id: FeedLinks.java,v 1.3 2013/07/05 13:48:05 eby Exp $
+ */
+public class FeedLinks implements JSONable {
+ private String self;
+ private String publish;
+ private String subscribe;
+ private String log;
+
+ public FeedLinks() {
+ self = publish = subscribe = log = null;
+ }
+
+ public FeedLinks(JSONObject jo) throws InvalidObjectException {
+ this();
+ self = jo.getString("self");
+ publish = jo.getString("publish");
+ subscribe = jo.getString("subscribe");
+ log = jo.getString("log");
+ }
+
+ public String getSelf() {
+ return self;
+ }
+ public void setSelf(String self) {
+ this.self = self;
+ }
+ public String getPublish() {
+ return publish;
+ }
+ public void setPublish(String publish) {
+ this.publish = publish;
+ }
+ public String getSubscribe() {
+ return subscribe;
+ }
+ public void setSubscribe(String subscribe) {
+ this.subscribe = subscribe;
+ }
+ public String getLog() {
+ return log;
+ }
+ public void setLog(String log) {
+ this.log = log;
+ }
+
+ @Override
+ public JSONObject asJSONObject() {
+ JSONObject jo = new JSONObject();
+ jo.put("self", self);
+ jo.put("publish", publish);
+ jo.put("subscribe", subscribe);
+ jo.put("log", log);
+ return jo;
+ }
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof FeedLinks))
+ return false;
+ FeedLinks of = (FeedLinks) obj;
+ if (!self.equals(of.self))
+ return false;
+ if (!publish.equals(of.publish))
+ return false;
+ if (!subscribe.equals(of.subscribe))
+ return false;
+ if (!log.equals(of.log))
+ return false;
+ return true;
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Group.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Group.java
new file mode 100644
index 00000000..3f55b00a
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Group.java
@@ -0,0 +1,417 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package com.att.research.datarouter.provisioning.beans;
+
+import java.io.InvalidObjectException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.json.JSONObject;
+
+import com.att.research.datarouter.provisioning.utils.DB;
+import com.att.research.datarouter.provisioning.utils.URLUtilities;
+
+/**
+ * The representation of a Subscription. Subscriptions can be retrieved from the DB, or stored/updated in the DB.
+ * @author vikram
+ * @version $Id: Group.java,v 1.0 2016/07/19
+ */
+public class Group extends Syncable {
+ private static Logger intlogger = Logger.getLogger("com.att.research.datarouter.provisioning.internal");
+ private static int next_groupid = getMaxGroupID() + 1;
+
+ private int groupid;
+ private String authid;
+ private String name;
+ private String description;
+ private String classification;
+ private String members;
+ private Date last_mod;
+
+
+ public static Group getGroupMatching(Group gup) {
+ String sql = String.format(
+ "select * from GROUPS where NAME = \"%s\"",
+ gup.getName()
+ );
+ List<Group> list = getGroupsForSQL(sql);
+ return list.size() > 0 ? list.get(0) : null;
+ }
+
+ public static Group getGroupMatching(Group gup, int groupid) {
+ String sql = String.format(
+ "select * from GROUPS where NAME = \"%s\" and GROUPID != %d ",
+ gup.getName(),
+ gup.getGroupid()
+ );
+ List<Group> list = getGroupsForSQL(sql);
+ return list.size() > 0 ? list.get(0) : null;
+ }
+
+ public static Group getGroupById(int id) {
+ String sql = "select * from GROUPS where GROUPID = " + id;
+ List<Group> list = getGroupsForSQL(sql);
+ return list.size() > 0 ? list.get(0) : null;
+ }
+
+ public static Group getGroupByAuthId(String id) {
+ String sql = "select * from GROUPS where AUTHID = '" + id +"'";
+ List<Group> list = getGroupsForSQL(sql);
+ return list.size() > 0 ? list.get(0) : null;
+ }
+
+ public static Collection<Group> getAllgroups() {
+ return getGroupsForSQL("select * from GROUPS");
+ }
+ private static List<Group> getGroupsForSQL(String sql) {
+ List<Group> list = new ArrayList<Group>();
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery(sql);
+ while (rs.next()) {
+ Group group = new Group(rs);
+ list.add(group);
+ }
+ rs.close();
+ stmt.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return list;
+ }
+ public static int getMaxGroupID() {
+ int max = 0;
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery("select MAX(groupid) from GROUPS");
+ if (rs.next()) {
+ max = rs.getInt(1);
+ }
+ rs.close();
+ stmt.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ intlogger.info("getMaxSubID: "+e.getMessage());
+ e.printStackTrace();
+ }
+ return max;
+ }
+ public static Collection<String> getGroupsByClassfication(String classfication) {
+ List<String> list = new ArrayList<String>();
+ String sql = "select * from GROUPS where classification = '"+classfication+"'";
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery(sql);
+ while (rs.next()) {
+ int groupid = rs.getInt("groupid");
+ //list.add(URLUtilities.generateSubscriptionURL(groupid));
+ }
+ rs.close();
+ stmt.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return list;
+ }
+ /**
+ * Return a count of the number of active subscriptions in the DB.
+ * @return the count
+ */
+ public static int countActiveSubscriptions() {
+ int count = 0;
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery("select count(*) from SUBSCRIPTIONS");
+ if (rs.next()) {
+ count = rs.getInt(1);
+ }
+ rs.close();
+ stmt.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ intlogger.warn("PROV0008 countActiveSubscriptions: "+e.getMessage());
+ e.printStackTrace();
+ }
+ return count;
+ }
+
+ public Group() {
+ this("", "", "");
+ }
+ public Group(String name, String desc, String members) {
+ this.groupid = -1;
+ this.authid = "";
+ this.name = name;
+ this.description = desc;
+ this.members = members;
+ this.classification = "";
+ this.last_mod = new Date();
+ }
+
+
+ public Group(ResultSet rs) throws SQLException {
+ this.groupid = rs.getInt("GROUPID");
+ this.authid = rs.getString("AUTHID");
+ this.name = rs.getString("NAME");
+ this.description = rs.getString("DESCRIPTION");
+ this.classification = rs.getString("CLASSIFICATION");
+ this.members = rs.getString("MEMBERS");
+ this.last_mod = rs.getDate("LAST_MOD");
+ }
+
+
+
+ public Group(JSONObject jo) throws InvalidObjectException {
+ this("", "", "");
+ try {
+ // The JSONObject is assumed to contain a vnd.att-dr.group representation
+ this.groupid = jo.optInt("groupid", -1);
+ String gname = jo.getString("name");
+ String gdescription = jo.getString("description");
+
+ this.authid = jo.getString("authid");
+ this.name = gname;
+ this.description = gdescription;
+ this.classification = jo.getString("classification");
+ this.members = jo.getString("members");
+
+ if (gname.length() > 50)
+ throw new InvalidObjectException("Group name is too long");
+ if (gdescription.length() > 256)
+ throw new InvalidObjectException("Group Description is too long");
+ } catch (InvalidObjectException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new InvalidObjectException("invalid JSON: "+e.getMessage());
+ }
+ }
+ public int getGroupid() {
+ return groupid;
+ }
+
+ public static Logger getIntlogger() {
+ return intlogger;
+ }
+ public void setGroupid(int groupid) {
+ this.groupid = groupid;
+ }
+
+ public static void setIntlogger(Logger intlogger) {
+ Group.intlogger = intlogger;
+ }
+ public static int getNext_groupid() {
+ return next_groupid;
+ }
+ public static void setNext_groupid(int next_groupid) {
+ Group.next_groupid = next_groupid;
+ }
+ public String getAuthid() {
+ return authid;
+ }
+ public void setAuthid(String authid) {
+ this.authid = authid;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public String getDescription() {
+ return description;
+ }
+ public void setDescription(String description) {
+ this.description = description;
+ }
+ public String getClassification() {
+ return classification;
+ }
+ public void setClassification(String classification) {
+ this.classification = classification;
+ }
+ public String getMembers() {
+ return members;
+ }
+ public void setMembers(String members) {
+ this.members = members;
+ }
+ public Date getLast_mod() {
+ return last_mod;
+ }
+ public void setLast_mod(Date last_mod) {
+ this.last_mod = last_mod;
+ }
+
+
+ @Override
+ public JSONObject asJSONObject() {
+ JSONObject jo = new JSONObject();
+ jo.put("groupid", groupid);
+ jo.put("authid", authid);
+ jo.put("name", name);
+ jo.put("description", description);
+ jo.put("classification", classification);
+ jo.put("members", members);
+ jo.put("last_mod", last_mod.getTime());
+ return jo;
+ }
+ @Override
+ public boolean doInsert(Connection c) {
+ boolean rv = true;
+ PreparedStatement ps = null;
+ try {
+ if (groupid == -1) {
+ // No feed ID assigned yet, so assign the next available one
+ setGroupid(next_groupid++);
+ }
+ // In case we insert a gropup from synchronization
+ if (groupid > next_groupid)
+ next_groupid = groupid+1;
+
+
+ // Create the GROUPS row
+ String sql = "insert into GROUPS (GROUPID, AUTHID, NAME, DESCRIPTION, CLASSIFICATION, MEMBERS) values (?, ?, ?, ?, ?, ?)";
+ ps = c.prepareStatement(sql, new String[] { "GROUPID" });
+ ps.setInt(1, groupid);
+ ps.setString(2, authid);
+ ps.setString(3, name);
+ ps.setString(4, description);
+ ps.setString(5, classification);
+ ps.setString(6, members);
+ ps.execute();
+ ps.close();
+ } catch (SQLException e) {
+ rv = false;
+ intlogger.warn("PROV0005 doInsert: "+e.getMessage());
+ e.printStackTrace();
+ } finally {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ return rv;
+ }
+ @Override
+ public boolean doUpdate(Connection c) {
+ boolean rv = true;
+ PreparedStatement ps = null;
+ try {
+ String sql = "update GROUPS set AUTHID = ?, NAME = ?, DESCRIPTION = ?, CLASSIFICATION = ? , MEMBERS = ? where GROUPID = ?";
+ ps = c.prepareStatement(sql);
+ ps.setString(1, authid);
+ ps.setString(2, name);
+ ps.setString(3, description);
+ ps.setString(4, classification);
+ ps.setString(5, members);
+ ps.setInt(6, groupid);
+ ps.executeUpdate();
+ } catch (SQLException e) {
+ rv = false;
+ intlogger.warn("PROV0006 doUpdate: "+e.getMessage());
+ e.printStackTrace();
+ } finally {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ return rv;
+ }
+ @Override
+ public boolean doDelete(Connection c) {
+ boolean rv = true;
+ PreparedStatement ps = null;
+ try {
+ String sql = "delete from GROUPS where GROUPID = ?";
+ ps = c.prepareStatement(sql);
+ ps.setInt(1, groupid);
+ ps.execute();
+ } catch (SQLException e) {
+ rv = false;
+ intlogger.warn("PROV0007 doDelete: "+e.getMessage());
+ e.printStackTrace();
+ } finally {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ return rv;
+ }
+ @Override
+ public String getKey() {
+ return ""+getGroupid();
+ }
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Group))
+ return false;
+ Group os = (Group) obj;
+ if (groupid != os.groupid)
+ return false;
+ if (authid != os.authid)
+ return false;
+ if (!name.equals(os.name))
+ return false;
+ if (description != os.description)
+ return false;
+ if (!classification.equals(os.classification))
+ return false;
+ if (!members.equals(os.members))
+ return false;
+
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "GROUP: groupid=" + groupid;
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/IngressRoute.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/IngressRoute.java
new file mode 100644
index 00000000..a9ea9bc7
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/IngressRoute.java
@@ -0,0 +1,542 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.beans;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.log4j.Logger;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import com.att.research.datarouter.provisioning.utils.DB;
+
+/**
+ * The representation of one route in the Ingress Route Table.
+ *
+ * @author Robert P. Eby
+ * @version $Id: IngressRoute.java,v 1.3 2013/12/16 20:30:23 eby Exp $
+ */
+public class IngressRoute extends NodeClass implements Comparable<IngressRoute> {
+ private static Logger intlogger = Logger.getLogger("com.att.research.datarouter.provisioning.internal");
+ private final int seq;
+ private final int feedid;
+ private final String userid;
+ private final String subnet;
+ private int nodelist;
+ private SortedSet<String> nodes;
+
+ /**
+ * Get all IngressRoutes in the database, sorted in order according to their sequence field.
+ * @return a sorted set of IngressRoutes
+ */
+ public static SortedSet<IngressRoute> getAllIngressRoutes() {
+ return getAllIngressRoutesForSQL("select SEQUENCE, FEEDID, USERID, SUBNET, NODESET from INGRESS_ROUTES");
+ }
+ /**
+ * Get all IngressRoutes in the database with a particular sequence number.
+ * @param seq the sequence number
+ * @return a set of IngressRoutes
+ */
+ public static Set<IngressRoute> getIngressRoutesForSeq(int seq) {
+ return getAllIngressRoutesForSQL("select SEQUENCE, FEEDID, USERID, SUBNET, NODESET from INGRESS_ROUTES where SEQUENCE = "+seq);
+ }
+ private static SortedSet<IngressRoute> getAllIngressRoutesForSQL(String sql) {
+ SortedSet<IngressRoute> set = new TreeSet<IngressRoute>();
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery(sql);
+ while (rs.next()) {
+ int seq = rs.getInt("SEQUENCE");
+ int feedid = rs.getInt("FEEDID");
+ String user = rs.getString("USERID");
+ String subnet = rs.getString("SUBNET");
+ int nodeset = rs.getInt("NODESET");
+ set.add(new IngressRoute(seq, feedid, user, subnet, nodeset));
+ }
+ rs.close();
+ stmt.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return set;
+ }
+
+ /**
+ * Get the maximum node set ID in use in the DB.
+ * @return the integer value of the maximum
+ */
+ public static int getMaxNodeSetID() {
+ return getMax("select max(SETID) as MAX from NODESETS");
+ }
+ /**
+ * Get the maximum node sequence number in use in the DB.
+ * @return the integer value of the maximum
+ */
+ public static int getMaxSequence() {
+ return getMax("select max(SEQUENCE) as MAX from INGRESS_ROUTES");
+ }
+ private static int getMax(String sql) {
+ int rv = 0;
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery(sql);
+ if (rs.next()) {
+ rv = rs.getInt("MAX");
+ }
+ rs.close();
+ stmt.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return rv;
+ }
+
+ /**
+ * Get an Ingress Route for a particular feed ID, user, and subnet
+ * @param feedid the Feed ID to look for
+ * @param user the user name to look for
+ * @param subnet the subnet to look for
+ * @return the Ingress Route, or null of there is none
+ */
+ public static IngressRoute getIngressRoute(int feedid, String user, String subnet) {
+ IngressRoute v = null;
+ PreparedStatement ps = null;
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ String sql = "select SEQUENCE, NODESET from INGRESS_ROUTES where FEEDID = ? AND USERID = ? and SUBNET = ?";
+ ps = conn.prepareStatement(sql);
+ ps.setInt(1, feedid);
+ ps.setString(2, user);
+ ps.setString(3, subnet);
+ ResultSet rs = ps.executeQuery();
+ if (rs.next()) {
+ int seq = rs.getInt("SEQUENCE");
+ int nodeset = rs.getInt("NODESET");
+ v = new IngressRoute(seq, feedid, user, subnet, nodeset);
+ }
+ rs.close();
+ ps.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ return v;
+ }
+
+ /**
+ * Get a collection of all Ingress Routes with a particular sequence number.
+ * @param seq the sequence number to look for
+ * @return the collection (may be empty).
+ */
+ public static Collection<IngressRoute> getIngressRoute(int seq) {
+ Collection<IngressRoute> rv = new ArrayList<IngressRoute>();
+ PreparedStatement ps = null;
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ String sql = "select FEEDID, USERID, SUBNET, NODESET from INGRESS_ROUTES where SEQUENCE = ?";
+ ps = conn.prepareStatement(sql);
+ ps.setInt(1, seq);
+ ResultSet rs = ps.executeQuery();
+ while (rs.next()) {
+ int feedid = rs.getInt("FEEDID");
+ String user = rs.getString("USERID");
+ String subnet = rs.getString("SUBNET");
+ int nodeset = rs.getInt("NODESET");
+ rv.add(new IngressRoute(seq, feedid, user, subnet, nodeset));
+ }
+ rs.close();
+ ps.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ return rv;
+ }
+
+ public IngressRoute(int seq, int feedid, String user, String subnet, Collection<String> nodes)
+ throws IllegalArgumentException
+ {
+ this(seq, feedid, user, subnet);
+ this.nodelist = -1;
+ this.nodes = new TreeSet<String>(nodes);
+ }
+
+ public IngressRoute(int seq, int feedid, String user, String subnet, int nodeset)
+ throws IllegalArgumentException
+ {
+ this(seq, feedid, user, subnet);
+ this.nodelist = nodeset;
+ this.nodes = new TreeSet<String>(readNodes());
+ }
+
+ private IngressRoute(int seq, int feedid, String user, String subnet)
+ throws IllegalArgumentException
+ {
+ this.seq = seq;
+ this.feedid = feedid;
+ this.userid = (user == null) ? "-" : user;
+ this.subnet = (subnet == null) ? "-" : subnet;
+ this.nodelist = -1;
+ this.nodes = null;
+ if (Feed.getFeedById(feedid) == null)
+ throw new IllegalArgumentException("No such feed: "+feedid);
+ if (!this.subnet.equals("-")) {
+ SubnetMatcher sm = new SubnetMatcher(subnet);
+ if (!sm.isValid())
+ throw new IllegalArgumentException("Invalid subnet: "+subnet);
+ }
+ }
+
+ public IngressRoute(JSONObject jo) {
+ this.seq = jo.optInt("seq");
+ this.feedid = jo.optInt("feedid");
+ String t = jo.optString("user");
+ this.userid = t.equals("") ? "-" : t;
+ t = jo.optString("subnet");
+ this.subnet = t.equals("") ? "-" : t;
+ this.nodelist = -1;
+ this.nodes = new TreeSet<String>();
+ JSONArray ja = jo.getJSONArray("node");
+ for (int i = 0; i < ja.length(); i++)
+ this.nodes.add(ja.getString(i));
+ }
+ /**
+ * Does this particular IngressRoute match a request, represented by feedid and req?
+ * To match, <i>feedid</i> must match the feed ID in the route, the user in the route
+ * (if specified) must match the user in the request, and the subnet in the route (if specified)
+ * must match the subnet from the request.
+ * @param feedid the feedid for this request
+ * @param req the remainder of the request
+ * @return true if a match, false otherwise
+ */
+ public boolean matches(int feedid, HttpServletRequest req) {
+ // Check feedid
+ if (this.feedid != feedid)
+ return false;
+
+ // Get user from request and compare
+ // Note: we don't check the password; the node will do that
+ if (userid.length() > 0 && !userid.equals("-")) {
+ String credentials = req.getHeader("Authorization");
+ if (credentials == null || !credentials.startsWith("Basic "))
+ return false;
+ String t = new String(Base64.decodeBase64(credentials.substring(6)));
+ int ix = t.indexOf(':');
+ if (ix >= 0)
+ t = t.substring(0, ix);
+ if (!t.equals(this.userid))
+ return false;
+ }
+
+ // If this route has a subnet, match it against the requester's IP addr
+ if (subnet.length() > 0 && !subnet.equals("-")) {
+ try {
+ InetAddress inet = InetAddress.getByName(req.getRemoteAddr());
+ SubnetMatcher sm = new SubnetMatcher(subnet);
+ return sm.matches(inet.getAddress());
+ } catch (UnknownHostException e) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Compare IP addresses as byte arrays to a subnet specified as a CIDR.
+ * Taken from com.att.research.datarouter.node.SubnetMatcher and modified somewhat.
+ */
+ public class SubnetMatcher {
+ private byte[] sn;
+ private int len;
+ private int mask;
+ private boolean valid;
+
+ /**
+ * Construct a subnet matcher given a CIDR
+ * @param subnet The CIDR to match
+ */
+ public SubnetMatcher(String subnet) {
+ int i = subnet.lastIndexOf('/');
+ if (i == -1) {
+ try {
+ sn = InetAddress.getByName(subnet).getAddress();
+ len = sn.length;
+ valid = true;
+ } catch (UnknownHostException e) {
+ len = 0;
+ valid = false;
+ }
+ mask = 0;
+ } else {
+ int n = Integer.parseInt(subnet.substring(i + 1));
+ try {
+ sn = InetAddress.getByName(subnet.substring(0, i)).getAddress();
+ valid = true;
+ } catch (UnknownHostException e) {
+ valid = false;
+ }
+ len = n / 8;
+ mask = ((0xff00) >> (n % 8)) & 0xff;
+ }
+ }
+ public boolean isValid() {
+ return valid;
+ }
+ /**
+ * Is the IP address in the CIDR?
+ * @param addr the IP address as bytes in network byte order
+ * @return true if the IP address matches.
+ */
+ public boolean matches(byte[] addr) {
+ if (!valid || addr.length != sn.length) {
+ return false;
+ }
+ for (int i = 0; i < len; i++) {
+ if (addr[i] != sn[i]) {
+ return false;
+ }
+ }
+ if (mask != 0 && ((addr[len] ^ sn[len]) & mask) != 0) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Get the list of node names for this route.
+ * @return the list
+ */
+ public SortedSet<String> getNodes() {
+ return this.nodes;
+ }
+
+ private Collection<String> readNodes() {
+ Collection<String> set = new TreeSet<String>();
+ PreparedStatement ps = null;
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ Statement stmt = conn.createStatement();
+ String sql = "select NODEID from NODESETS where SETID = ?";
+ ps = conn.prepareStatement(sql);
+ ps.setInt(1, nodelist);
+ ResultSet rs = ps.executeQuery();
+ while (rs.next()) {
+ int id = rs.getInt("NODEID");
+ set.add(lookupNodeID(id));
+ }
+ rs.close();
+ stmt.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ return set;
+ }
+
+ /**
+ * Delete the IRT route having this IngressRoutes feed ID, user ID, and subnet from the database.
+ * @return true if the delete succeeded
+ */
+ @Override
+ public boolean doDelete(Connection c) {
+ boolean rv = true;
+ PreparedStatement ps = null;
+ try {
+ ps = c.prepareStatement("delete from INGRESS_ROUTES where FEEDID = ? and USERID = ? and SUBNET = ?");
+ ps.setInt(1, feedid);
+ ps.setString(2, userid);
+ ps.setString(3, subnet);
+ ps.execute();
+ ps.close();
+
+ ps = c.prepareStatement("delete from NODESETS where SETID = ?");
+ ps.setInt(1, nodelist);
+ ps.execute();
+ } catch (SQLException e) {
+ rv = false;
+ intlogger.warn("PROV0007 doDelete: "+e.getMessage());
+ e.printStackTrace();
+ } finally {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ return rv;
+ }
+
+ @SuppressWarnings("resource")
+ @Override
+ public boolean doInsert(Connection c) {
+ boolean rv = false;
+ PreparedStatement ps = null;
+ try {
+ // Create the NODESETS rows & set nodelist
+ int set = getMaxNodeSetID() + 1;
+ this.nodelist = set;
+ for (String node : nodes) {
+ int id = lookupNodeName(node);
+ ps = c.prepareStatement("insert into NODESETS (SETID, NODEID) values (?,?)");
+ ps.setInt(1, this.nodelist);
+ ps.setInt(2, id);
+ ps.execute();
+ ps.close();
+ }
+
+ // Create the INGRESS_ROUTES row
+ ps = c.prepareStatement("insert into INGRESS_ROUTES (SEQUENCE, FEEDID, USERID, SUBNET, NODESET) values (?, ?, ?, ?, ?)");
+ ps.setInt(1, this.seq);
+ ps.setInt(2, this.feedid);
+ ps.setString(3, this.userid);
+ ps.setString(4, this.subnet);
+ ps.setInt(5, this.nodelist);
+ ps.execute();
+ ps.close();
+ rv = true;
+ } catch (SQLException e) {
+ intlogger.warn("PROV0005 doInsert: "+e.getMessage());
+ e.printStackTrace();
+ } finally {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ return rv;
+ }
+
+ @Override
+ public boolean doUpdate(Connection c) {
+ return doDelete(c) && doInsert(c);
+ }
+
+ @Override
+ public JSONObject asJSONObject() {
+ JSONObject jo = new JSONObject();
+ jo.put("feedid", feedid);
+ // Note: for user and subnet, null, "", and "-" are equivalent
+ if (userid != null && !userid.equals("-") && !userid.equals(""))
+ jo.put("user", userid);
+ if (subnet != null && !subnet.equals("-") && !subnet.equals(""))
+ jo.put("subnet", subnet);
+ jo.put("seq", seq);
+ jo.put("node", nodes);
+ return jo;
+ }
+
+ @Override
+ public String getKey() {
+ return String.format("%d/%s/%s/%d", feedid, (userid == null)?"":userid, (subnet == null)?"":subnet, seq);
+ }
+
+ @Override
+ public int hashCode() {
+ return toString().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ try {
+ if (!(obj instanceof IngressRoute))
+ return false;
+ return this.compareTo((IngressRoute) obj) == 0;
+ } catch (NullPointerException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public int compareTo(IngressRoute in) {
+ if (in == null)
+ throw new NullPointerException();
+ int n = this.feedid - in.feedid;
+ if (n != 0)
+ return n;
+ n = this.seq - in.seq;
+ if (n != 0)
+ return n;
+ n = this.userid.compareTo(in.userid);
+ if (n != 0)
+ return n;
+ n = this.subnet.compareTo(in.subnet);
+ if (n != 0)
+ return n;
+ return this.nodes.equals(in.nodes) ? 0 : 1;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("INGRESS: feed=%d, userid=%s, subnet=%s, seq=%d", feedid, (userid == null)?"":userid, (subnet == null)?"":subnet, seq);
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Insertable.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Insertable.java
new file mode 100644
index 00000000..6604ab41
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Insertable.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.beans;
+
+import java.sql.Connection;
+
+/**
+ * An object that can be INSERT-ed into the database.
+ * @author Robert Eby
+ * @version $Id: Insertable.java,v 1.2 2013/05/29 14:44:36 eby Exp $
+ */
+public interface Insertable {
+ /**
+ * Insert this object into the DB.
+ * @param c the JDBC Connection to use
+ * @return true if the INSERT succeeded, false otherwise
+ */
+ public boolean doInsert(Connection c);
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/JSONable.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/JSONable.java
new file mode 100644
index 00000000..cbea9ad0
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/JSONable.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.beans;
+
+import org.json.JSONObject;
+
+/**
+ * An object that can be represented as a {@link JSONObject}.
+ * @author Robert Eby
+ * @version $Id: JSONable.java,v 1.1 2013/04/26 21:00:26 eby Exp $
+ */
+public interface JSONable {
+ /**
+ * Get a JSONObject representing this object.
+ * @return the JSONObject
+ */
+ public JSONObject asJSONObject();
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/LOGJSONable.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/LOGJSONable.java
new file mode 100644
index 00000000..93cdfaa9
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/LOGJSONable.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.beans;
+
+import org.json.LOGJSONObject;
+
+/**
+ * An object that can be represented as a {@link JSONObject}.
+ * @author Robert Eby
+ * @version $Id: JSONable.java,v 1.1 2013/04/26 21:00:26 eby Exp $
+ */
+public interface LOGJSONable {
+ /**
+ * Get a JSONObject representing this object.
+ * @return the JSONObject
+ */
+ public LOGJSONObject asJSONObject();
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Loadable.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Loadable.java
new file mode 100644
index 00000000..3676f450
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Loadable.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.beans;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+import com.att.research.datarouter.provisioning.utils.LogfileLoader;
+
+/**
+ * This interface is used by bean classes that can be loaded into the LOG_RECORDS table using the
+ * PreparedStatement at {@link LogfileLoader}.INSERT_SQL.
+ *
+ * @author Robert Eby
+ * @version $Id: Loadable.java,v 1.2 2013/08/06 13:28:33 eby Exp $
+ */
+public interface Loadable {
+ /**
+ * Load the 18 fields in the PreparedStatement <i>ps</i>. The fields are:
+ * <ol>
+ * <li>type (String)</li>
+ * <li>event_time (long)</li>
+ * <li>publish ID (String)</li>
+ * <li>feed ID (int)</li>
+ * <li>request URI (String)</li>
+ * <li>method (String)</li>
+ * <li>content type (String)</li>
+ * <li>content length (long)</li>
+ * <li>feed File ID (String)</li>
+ * <li>remote address (String)</li>
+ * <li>user (String)</li>
+ * <li>status (int)</li>
+ * <li>delivery subscriber id (int)</li>
+ * <li>delivery File ID (String)</li>
+ * <li>result (int)</li>
+ * <li>attempts (int)</li>
+ * <li>reason (String)</li>
+ * <li>record ID (long)</li>
+ * </ol>
+ * @param ps the PreparedStatement to load
+ */
+ public void load(PreparedStatement ps) throws SQLException;
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/LogRecord.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/LogRecord.java
new file mode 100644
index 00000000..1ddc5094
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/LogRecord.java
@@ -0,0 +1,235 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.beans;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Types;
+import java.text.ParseException;
+import java.util.Iterator;
+
+import com.att.research.datarouter.provisioning.utils.DB;
+import com.att.research.datarouter.provisioning.utils.RLEBitSet;
+
+/**
+ * The representation of a Log Record, as retrieved from the DB. Since this record format is only used
+ * to replicate between provisioning servers, it is very bare-bones; e.g. there are no field setters and only 1 getter.
+ * @author Robert Eby
+ * @version $Id: LogRecord.java,v 1.7 2014/03/12 19:45:41 eby Exp $
+ */
+public class LogRecord extends BaseLogRecord {
+ /**
+ * Print all log records whose RECORD_IDs are in the bit set provided.
+ * @param os the {@link OutputStream} to print the records on
+ * @param bs the {@link RLEBitSet} listing the record IDs to print
+ * @throws IOException
+ */
+ public static void printLogRecords(OutputStream os, RLEBitSet bs) throws IOException {
+ final String sql = "select * from LOG_RECORDS where RECORD_ID >= ? AND RECORD_ID <= ?";
+ DB db = new DB();
+ Connection conn = null;
+ try {
+ conn = db.getConnection();
+ Statement stmt = conn.createStatement();
+ Iterator<Long[]> iter = bs.getRangeIterator();
+ PreparedStatement ps = conn.prepareStatement(sql);
+ while (iter.hasNext()) {
+ Long[] n = iter.next();
+ ps.setLong(1, n[0]);
+ ps.setLong(2, n[1]);
+ ResultSet rs = ps.executeQuery();
+ while (rs.next()) {
+ LogRecord lr = new LogRecord(rs);
+ os.write(lr.toString().getBytes());
+ }
+ rs.close();
+ ps.clearParameters();
+ }
+ ps.close();
+ stmt.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ } finally {
+ if (conn != null)
+ db.release(conn);
+ }
+ }
+
+ private final String type;
+ private final String feedFileid;
+ private final String remoteAddr;
+ private final String user;
+ private final int status;
+ private final int subid;
+ private final String fileid;
+ private final int result;
+ private final int attempts;
+ private final String reason;
+ private final long record_id;
+ private final long clength2;
+
+ public LogRecord(ResultSet rs) throws SQLException {
+ super(rs);
+ this.type = rs.getString("TYPE");
+ this.feedFileid = rs.getString("FEED_FILEID");
+ this.remoteAddr = rs.getString("REMOTE_ADDR");
+ this.user = rs.getString("USER");
+ this.status = rs.getInt("STATUS");
+
+ this.subid = rs.getInt("DELIVERY_SUBID");
+ this.fileid = rs.getString("DELIVERY_FILEID");
+ this.result = rs.getInt("RESULT");
+
+ this.attempts = rs.getInt("ATTEMPTS");
+ this.reason = rs.getString("REASON");
+
+ this.record_id = rs.getLong("RECORD_ID");
+ this.clength2 = rs.getLong("CONTENT_LENGTH_2");
+ }
+ public LogRecord(String[] pp) throws ParseException {
+ super(pp);
+ this.type = pp[8];
+ this.feedFileid = pp[9];
+ this.remoteAddr = pp[10];
+ this.user = pp[11];
+ this.status = Integer.parseInt(pp[12]);
+
+ this.subid = Integer.parseInt(pp[13]);
+ this.fileid = pp[14];
+ this.result = Integer.parseInt(pp[15]);
+
+ this.attempts = Integer.parseInt(pp[16]);
+ this.reason = pp[17];
+
+ this.record_id = Long.parseLong(pp[18]);
+ this.clength2 = (pp.length == 20) ? Long.parseLong(pp[19]) : 0;
+ }
+
+ public long getRecordId() {
+ return record_id;
+ }
+
+ @Override
+ public String toString() {
+ return
+ sdf.format(getEventTime()) + "|"
+ + "LOG|"
+ + getPublishId() + "|"
+ + getFeedid() + "|"
+ + getRequestUri() + "|"
+ + getMethod() + "|"
+ + getContentType() + "|"
+ + getContentLength() + "|"
+ + type + "|"
+ + feedFileid + "|"
+ + remoteAddr + "|"
+ + user + "|"
+ + status + "|"
+ + subid + "|"
+ + fileid + "|"
+ + result + "|"
+ + attempts + "|"
+ + reason + "|"
+ + record_id + "|"
+ + clength2
+ + "\n";
+ }
+
+ @Override
+ public void load(PreparedStatement ps) throws SQLException {
+ ps.setString(1, type);
+ super.load(ps); // loads fields 2-8
+ if (type.equals("pub")) {
+ ps.setString(9, feedFileid);
+ ps.setString(10, remoteAddr);
+ ps.setString(11, user);
+ ps.setInt (12, status);
+ ps.setNull (13, Types.INTEGER);
+ ps.setNull (14, Types.VARCHAR);
+ ps.setNull (15, Types.INTEGER);
+ ps.setNull (16, Types.INTEGER);
+ ps.setNull (17, Types.VARCHAR);
+ ps.setLong (18, record_id);
+ ps.setNull (19, Types.BIGINT);
+ } else if (type.equals("del")) {
+ ps.setNull (9, Types.VARCHAR);
+ ps.setNull (10, Types.VARCHAR);
+ ps.setString(11, user);
+ ps.setNull (12, Types.INTEGER);
+ ps.setInt (13, subid);
+ ps.setString(14, fileid);
+ ps.setInt (15, result);
+ ps.setNull (16, Types.INTEGER);
+ ps.setNull (17, Types.VARCHAR);
+ ps.setLong (18, record_id);
+ ps.setNull (19, Types.BIGINT);
+ } else if (type.equals("exp")) {
+ ps.setNull (9, Types.VARCHAR);
+ ps.setNull (10, Types.VARCHAR);
+ ps.setNull (11, Types.VARCHAR);
+ ps.setNull (12, Types.INTEGER);
+ ps.setInt (13, subid);
+ ps.setString(14, fileid);
+ ps.setNull (15, Types.INTEGER);
+ ps.setInt (16, attempts);
+ ps.setString(17, reason);
+ ps.setLong (18, record_id);
+ ps.setNull (19, Types.BIGINT);
+ } else if (type.equals("pbf")) {
+ ps.setString( 9, feedFileid);
+ ps.setString(10, remoteAddr);
+ ps.setString(11, user);
+ ps.setNull (12, Types.INTEGER);
+ ps.setNull (13, Types.INTEGER);
+ ps.setNull (14, Types.VARCHAR);
+ ps.setNull (15, Types.INTEGER);
+ ps.setNull (16, Types.INTEGER);
+ ps.setNull (17, Types.VARCHAR);
+ ps.setLong (18, record_id);
+ ps.setLong (19, clength2);
+ } else if (type.equals("dlx")) {
+ ps.setNull ( 9, Types.VARCHAR);
+ ps.setNull (10, Types.VARCHAR);
+ ps.setNull (11, Types.VARCHAR);
+ ps.setNull (12, Types.INTEGER);
+ ps.setInt (13, subid);
+ ps.setNull (14, Types.VARCHAR);
+ ps.setNull (15, Types.INTEGER);
+ ps.setNull (16, Types.INTEGER);
+ ps.setNull (17, Types.VARCHAR);
+ ps.setLong (18, record_id);
+ ps.setLong (19, clength2);
+ }
+ }
+
+ public static void main(String[] a) throws IOException {
+ LogRecord.printLogRecords(System.out, new RLEBitSet(a[0]));
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/NetworkRoute.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/NetworkRoute.java
new file mode 100644
index 00000000..59f21928
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/NetworkRoute.java
@@ -0,0 +1,230 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.beans;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.apache.log4j.Logger;
+import org.json.JSONObject;
+
+import com.att.research.datarouter.provisioning.utils.DB;
+
+/**
+ * The representation of one route in the Network Route Table.
+ *
+ * @author Robert P. Eby
+ * @version $Id: NetworkRoute.java,v 1.2 2013/12/16 20:30:23 eby Exp $
+ */
+public class NetworkRoute extends NodeClass implements Comparable<NetworkRoute> {
+ private static Logger intlogger = Logger.getLogger("com.att.research.datarouter.provisioning.internal");
+ private final int fromnode;
+ private final int tonode;
+ private final int vianode;
+
+ /**
+ * Get a set of all Network Routes in the DB. The set is sorted according to the natural sorting order
+ * of the routes (based on the from and to node names in each route).
+ * @return the sorted set
+ */
+ public static SortedSet<NetworkRoute> getAllNetworkRoutes() {
+ SortedSet<NetworkRoute> set = new TreeSet<NetworkRoute>();
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery("select FROMNODE, TONODE, VIANODE from NETWORK_ROUTES");
+ while (rs.next()) {
+ int fromnode = rs.getInt("FROMNODE");
+ int tonode = rs.getInt("TONODE");
+ int vianode = rs.getInt("VIANODE");
+ set.add(new NetworkRoute(fromnode, tonode, vianode));
+ }
+ rs.close();
+ stmt.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return set;
+ }
+
+ public NetworkRoute(String fromnode, String tonode) throws IllegalArgumentException {
+ this.fromnode = lookupNodeName(fromnode);
+ this.tonode = lookupNodeName(tonode);
+ this.vianode = -1;
+ }
+
+ public NetworkRoute(String fromnode, String tonode, String vianode) throws IllegalArgumentException {
+ this.fromnode = lookupNodeName(fromnode);
+ this.tonode = lookupNodeName(tonode);
+ this.vianode = lookupNodeName(vianode);
+ }
+
+ public NetworkRoute(JSONObject jo) throws IllegalArgumentException {
+ this.fromnode = lookupNodeName(jo.getString("from"));
+ this.tonode = lookupNodeName(jo.getString("to"));
+ this.vianode = lookupNodeName(jo.getString("via"));
+ }
+
+ public NetworkRoute(int fromnode, int tonode, int vianode) throws IllegalArgumentException {
+ this.fromnode = fromnode;
+ this.tonode = tonode;
+ this.vianode = vianode;
+ }
+
+ public int getFromnode() {
+ return fromnode;
+ }
+
+ public int getTonode() {
+ return tonode;
+ }
+
+ public int getVianode() {
+ return vianode;
+ }
+
+ @Override
+ public boolean doDelete(Connection c) {
+ boolean rv = true;
+ PreparedStatement ps = null;
+ try {
+ String sql = "delete from NETWORK_ROUTES where FROMNODE = ? AND TONODE = ?";
+ ps = c.prepareStatement(sql);
+ ps.setInt(1, fromnode);
+ ps.setInt(2, tonode);
+ ps.execute();
+ } catch (SQLException e) {
+ rv = false;
+ intlogger.warn("PROV0007 doDelete: "+e.getMessage());
+ e.printStackTrace();
+ } finally {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ return rv;
+ }
+
+ @Override
+ public boolean doInsert(Connection c) {
+ boolean rv = false;
+ if (this.vianode >= 0) {
+ PreparedStatement ps = null;
+ try {
+ // Create the NETWORK_ROUTES row
+ String sql = "insert into NETWORK_ROUTES (FROMNODE, TONODE, VIANODE) values (?, ?, ?)";
+ ps = c.prepareStatement(sql);
+ ps.setInt(1, this.fromnode);
+ ps.setInt(2, this.tonode);
+ ps.setInt(3, this.vianode);
+ ps.execute();
+ ps.close();
+ rv = true;
+ } catch (SQLException e) {
+ intlogger.warn("PROV0005 doInsert: "+e.getMessage());
+ e.printStackTrace();
+ } finally {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ return rv;
+ }
+
+ @Override
+ public boolean doUpdate(Connection c) {
+ boolean rv = true;
+ PreparedStatement ps = null;
+ try {
+ String sql = "update NETWORK_ROUTES set VIANODE = ? where FROMNODE = ? and TONODE = ?";
+ ps = c.prepareStatement(sql);
+ ps.setInt(1, vianode);
+ ps.setInt(2, fromnode);
+ ps.setInt(3, tonode);
+ ps.executeUpdate();
+ } catch (SQLException e) {
+ rv = false;
+ intlogger.warn("PROV0006 doUpdate: "+e.getMessage());
+ e.printStackTrace();
+ } finally {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ return rv;
+ }
+
+ @Override
+ public JSONObject asJSONObject() {
+ JSONObject jo = new JSONObject();
+ jo.put("from", lookupNodeID(fromnode));
+ jo.put("to", lookupNodeID(tonode));
+ jo.put("via", lookupNodeID(vianode));
+ return jo;
+ }
+
+ @Override
+ public String getKey() {
+ return lookupNodeID(fromnode)+":"+lookupNodeID(tonode);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof NetworkRoute))
+ return false;
+ NetworkRoute on = (NetworkRoute)obj;
+ return (fromnode == on.fromnode) && (tonode == on.tonode) && (vianode == on.vianode);
+ }
+
+ @Override
+ public int compareTo(NetworkRoute o) {
+ if (this.fromnode == o.fromnode) {
+ if (this.tonode == o.tonode)
+ return this.vianode - o.vianode;
+ return this.tonode - o.tonode;
+ }
+ return this.fromnode - o.fromnode;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("NETWORK: from=%d, to=%d, via=%d", fromnode, tonode, vianode);
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/NodeClass.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/NodeClass.java
new file mode 100644
index 00000000..321885b4
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/NodeClass.java
@@ -0,0 +1,179 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package com.att.research.datarouter.provisioning.beans;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.log4j.Logger;
+
+import com.att.research.datarouter.provisioning.utils.DB;
+
+/**
+ * This class is used to aid in the mapping of node names from/to node IDs.
+ *
+ * @author Robert P. Eby
+ * @version $Id: NodeClass.java,v 1.2 2014/01/15 16:08:43 eby Exp $
+ */
+public abstract class NodeClass extends Syncable {
+ private static Map<String, Integer> map;
+
+ public NodeClass() {
+ // init on first use
+ if (map == null) {
+ reload();
+ }
+ }
+
+ /**
+ * Add nodes to the NODES table, when the NODES parameter value is changed.
+ * Nodes are only added to the table, they are never deleted. The node name is normalized
+ * to contain the domain (if missing).
+ * @param nodes a pipe separated list of the current nodes
+ */
+ public static void setNodes(String[] nodes) {
+ if (map == null)
+ reload();
+ int nextid = 0;
+ for (Integer n : map.values()) {
+ if (n >= nextid)
+ nextid = n+1;
+ }
+ // take | separated list, add domain if needed.
+ Logger intlogger = Logger.getLogger("com.att.research.datarouter.provisioning.internal");
+ for (String node : nodes) {
+ node = normalizeNodename(node);
+ if (!map.containsKey(node)) {
+ intlogger.info("..adding "+node+" to NODES with index "+nextid);
+ map.put(node, nextid);
+ PreparedStatement ps = null;
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ ps = conn.prepareStatement("insert into NODES (NODEID, NAME, ACTIVE) values (?, ?, 1)");
+ ps.setInt(1, nextid);
+ ps.setString(2, node);
+ ps.execute();
+ ps.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ intlogger.warn("PROV0005 doInsert: "+e.getMessage());
+ e.printStackTrace();
+ } finally {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ nextid++;
+ }
+ }
+ }
+
+ public static void reload() {
+ Map<String, Integer> m = new HashMap<String, Integer>();
+ PreparedStatement ps = null;
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ String sql = "select NODEID, NAME from NODES";
+ ps = conn.prepareStatement(sql);
+ ResultSet rs = ps.executeQuery();
+ while (rs.next()) {
+ int id = rs.getInt("NODEID");
+ String name = rs.getString("NAME");
+ m.put(name, id);
+ }
+ rs.close();
+ ps.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ map = m;
+ }
+
+ public static Integer lookupNodeName(final String name) throws IllegalArgumentException {
+ Integer n = map.get(name);
+ if (n == null)
+ throw new IllegalArgumentException("Invalid node name: "+name);
+ return n;
+ }
+
+ public static Collection<String> lookupNodeNames(String patt) throws IllegalArgumentException {
+ Collection<String> coll = new TreeSet<String>();
+ final Set<String> keyset = map.keySet();
+ for (String s : patt.toLowerCase().split(",")) {
+ if (s.endsWith("*")) {
+ s = s.substring(0, s.length()-1);
+ for (String s2 : keyset) {
+ if (s2.startsWith(s))
+ coll.add(s2);
+ }
+ } else if (keyset.contains(s)) {
+ coll.add(s);
+ } else if (keyset.contains(normalizeNodename(s))) {
+ coll.add(normalizeNodename(s));
+ } else {
+ throw new IllegalArgumentException("Invalid node name: "+s);
+ }
+ }
+ return coll;
+ }
+
+ protected String lookupNodeID(int n) {
+ for (String s : map.keySet()) {
+ if (map.get(s) == n)
+ return s;
+ }
+ return null;
+ }
+
+ public static String normalizeNodename(String s) {
+ if (s != null && s.indexOf('.') <= 0) {
+ Parameters p = Parameters.getParameter(Parameters.PROV_DOMAIN);
+ if (p != null) {
+ String domain = p.getValue();
+ s += "." + domain;
+ }
+ }
+ return s.toLowerCase();
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Parameters.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Parameters.java
new file mode 100644
index 00000000..1cb4bcad
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Parameters.java
@@ -0,0 +1,257 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package com.att.research.datarouter.provisioning.beans;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+import org.json.JSONObject;
+
+import com.att.research.datarouter.provisioning.utils.DB;
+
+/**
+ * Methods to provide access to Provisioning parameters in the DB.
+ * This class also provides constants of the standard parameters used by the Data Router.
+ * @author Robert Eby
+ * @version $Id: Parameters.java,v 1.11 2014/03/12 19:45:41 eby Exp $
+ */
+public class Parameters extends Syncable {
+ public static final String PROV_REQUIRE_SECURE = "PROV_REQUIRE_SECURE";
+ public static final String PROV_REQUIRE_CERT = "PROV_REQUIRE_CERT";
+ public static final String PROV_AUTH_ADDRESSES = "PROV_AUTH_ADDRESSES";
+ public static final String PROV_AUTH_SUBJECTS = "PROV_AUTH_SUBJECTS";
+ public static final String PROV_NAME = "PROV_NAME";
+ public static final String PROV_ACTIVE_NAME = "PROV_ACTIVE_NAME";
+ public static final String PROV_DOMAIN = "PROV_DOMAIN";
+ public static final String PROV_MAXFEED_COUNT = "PROV_MAXFEED_COUNT";
+ public static final String PROV_MAXSUB_COUNT = "PROV_MAXSUB_COUNT";
+ public static final String PROV_POKETIMER1 = "PROV_POKETIMER1";
+ public static final String PROV_POKETIMER2 = "PROV_POKETIMER2";
+ public static final String PROV_SPECIAL_SUBNET = "PROV_SPECIAL_SUBNET";
+ public static final String PROV_LOG_RETENTION = "PROV_LOG_RETENTION";
+ public static final String NODES = "NODES";
+ public static final String ACTIVE_POD = "ACTIVE_POD";
+ public static final String STANDBY_POD = "STANDBY_POD";
+ public static final String LOGROLL_INTERVAL = "LOGROLL_INTERVAL";
+ public static final String DELIVERY_INIT_RETRY_INTERVAL = "DELIVERY_INIT_RETRY_INTERVAL";
+ public static final String DELIVERY_MAX_RETRY_INTERVAL = "DELIVERY_MAX_RETRY_INTERVAL";
+ public static final String DELIVERY_RETRY_RATIO = "DELIVERY_RETRY_RATIO";
+ public static final String DELIVERY_MAX_AGE = "DELIVERY_MAX_AGE";
+ public static final String THROTTLE_FILTER = "THROTTLE_FILTER";
+ public static final String STATIC_ROUTING_NODES = "STATIC_ROUTING_NODES"; //Adding new param for static Routing - Rally:US664862-1610
+
+ private static Logger intlogger = Logger.getLogger("com.att.research.datarouter.provisioning.internal");
+
+ private String keyname;
+ private String value;
+
+ /**
+ * Get all parameters in the DB as a Map.
+ * @return the Map of keynames/values from the DB.
+ */
+ public static Map<String,String> getParameters() {
+ Map<String,String> props = new HashMap<String,String>();
+ for (Parameters p : getParameterCollection()) {
+ props.put(p.getKeyname(), p.getValue());
+ }
+ return props;
+ }
+ public static Collection<Parameters> getParameterCollection() {
+ Collection<Parameters> coll = new ArrayList<Parameters>();
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ Statement stmt = conn.createStatement();
+ String sql = "select * from PARAMETERS";
+ ResultSet rs = stmt.executeQuery(sql);
+ while (rs.next()) {
+ Parameters p = new Parameters(rs);
+ coll.add(p);
+ }
+ rs.close();
+ stmt.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return coll;
+ }
+ /**
+ * Get a specific parameter value from the DB.
+ * @param k the key to lookup
+ * @return the value, or null if non-existant
+ */
+ public static Parameters getParameter(String k) {
+ Parameters v = null;
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ Statement stmt = conn.createStatement();
+ String sql = "select KEYNAME, VALUE from PARAMETERS where KEYNAME = \"" + k + "\"";
+ ResultSet rs = stmt.executeQuery(sql);
+ if (rs.next()) {
+ v = new Parameters(rs);
+ }
+ rs.close();
+ stmt.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return v;
+ }
+
+ public Parameters() {
+ this("", "");
+ }
+ public Parameters(String k, String v) {
+ this.keyname = k;
+ this.value = v;
+ }
+ public Parameters(ResultSet rs) throws SQLException {
+ this.keyname = rs.getString("KEYNAME");
+ this.value = rs.getString("VALUE");
+ }
+ public String getKeyname() {
+ return keyname;
+ }
+ public void setKeyname(String keyname) {
+ this.keyname = keyname;
+ }
+ public String getValue() {
+ return value;
+ }
+ public void setValue(String value) {
+ this.value = value;
+ }
+ @Override
+ public JSONObject asJSONObject() {
+ JSONObject jo = new JSONObject();
+ jo.put("keyname", keyname);
+ jo.put("value", value);
+ return jo;
+ }
+ @Override
+ public boolean doInsert(Connection c) {
+ boolean rv = true;
+ PreparedStatement ps = null;
+ try {
+ // Create the SUBSCRIPTIONS row
+ String sql = "insert into PARAMETERS values (?, ?)";
+ ps = c.prepareStatement(sql);
+ ps.setString(1, getKeyname());
+ ps.setString(2, getValue());
+ ps.execute();
+ } catch (SQLException e) {
+ rv = false;
+ intlogger.warn("PROV0005 doInsert: "+e.getMessage());
+ e.printStackTrace();
+ } finally {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ return rv;
+ }
+ @Override
+ public boolean doUpdate(Connection c) {
+ boolean rv = true;
+ PreparedStatement ps = null;
+ try {
+ // Update the PARAMETERS row
+ String sql = "update PARAMETERS set VALUE = ? where KEYNAME = ?";
+ ps = c.prepareStatement(sql);
+ ps.setString(1, getValue());
+ ps.setString(2, getKeyname());
+ ps.executeUpdate();
+ } catch (SQLException e) {
+ rv = false;
+ intlogger.warn("PROV0006 doUpdate: "+e.getMessage());
+ e.printStackTrace();
+ } finally {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ return rv;
+ }
+ @Override
+ public boolean doDelete(Connection c) {
+ boolean rv = true;
+ PreparedStatement ps = null;
+ try {
+ // Create the SUBSCRIPTIONS row
+ String sql = "delete from PARAMETERS where KEYNAME = ?";
+ ps = c.prepareStatement(sql);
+ ps.setString(1, getKeyname());
+ ps.execute();
+ } catch (SQLException e) {
+ rv = false;
+ intlogger.warn("PROV0007 doDelete: "+e.getMessage());
+ e.printStackTrace();
+ } finally {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ return rv;
+ }
+ @Override
+ public String getKey() {
+ return getKeyname();
+ }
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Parameters))
+ return false;
+ Parameters of = (Parameters) obj;
+ if (!keyname.equals(of.keyname))
+ return false;
+ if (!value.equals(of.value))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "PARAM: keyname=" + keyname + ", value=" + value;
+ }
+}
+
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/PubFailRecord.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/PubFailRecord.java
new file mode 100644
index 00000000..1fe1473b
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/PubFailRecord.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package com.att.research.datarouter.provisioning.beans;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.text.ParseException;
+
+/**
+ * The representation of a Publish Failure (PBF) Record, as retrieved from the DB.
+ * @author Robert Eby
+ * @version $Id: PubFailRecord.java,v 1.1 2013/10/28 18:06:53 eby Exp $
+ */
+public class PubFailRecord extends BaseLogRecord {
+ private long contentLengthReceived;
+ private String sourceIP;
+ private String user;
+ private String error;
+
+ public PubFailRecord(String[] pp) throws ParseException {
+ super(pp);
+ this.contentLengthReceived = Long.parseLong(pp[8]);
+ this.sourceIP = pp[9];
+ this.user = pp[10];
+ this.error = pp[11];
+ }
+ public PubFailRecord(ResultSet rs) throws SQLException {
+ super(rs);
+ // Note: because this record should be "rare" these fields are mapped to unconventional fields in the DB
+ this.contentLengthReceived = rs.getLong("CONTENT_LENGTH_2");
+ this.sourceIP = rs.getString("REMOTE_ADDR");
+ this.user = rs.getString("USER");
+ this.error = rs.getString("FEED_FILEID");
+ }
+ public long getContentLengthReceived() {
+ return contentLengthReceived;
+ }
+ public String getSourceIP() {
+ return sourceIP;
+ }
+ public String getUser() {
+ return user;
+ }
+ public String getError() {
+ return error;
+ }
+ @Override
+ public void load(PreparedStatement ps) throws SQLException {
+ ps.setString(1, "pbf"); // field 1: type
+ super.load(ps); // loads fields 2-8
+ ps.setString( 9, getError());
+ ps.setString(10, getSourceIP());
+ ps.setString(11, getUser());
+ ps.setNull (12, Types.INTEGER);
+ ps.setNull (13, Types.INTEGER);
+ ps.setNull (14, Types.VARCHAR);
+ ps.setNull (15, Types.INTEGER);
+ ps.setNull (16, Types.INTEGER);
+ ps.setNull (17, Types.VARCHAR);
+ ps.setLong (19, getContentLengthReceived());
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/PublishRecord.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/PublishRecord.java
new file mode 100644
index 00000000..a844c768
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/PublishRecord.java
@@ -0,0 +1,153 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package com.att.research.datarouter.provisioning.beans;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.text.ParseException;
+import java.util.LinkedHashMap;
+
+import org.json.LOGJSONObject;
+
+/**
+ * The representation of a Publish Record, as retrieved from the DB.
+ * @author Robert Eby
+ * @version $Id: PublishRecord.java,v 1.6 2013/10/28 18:06:53 eby Exp $
+ */
+public class PublishRecord extends BaseLogRecord {
+ private String feedFileid;
+ private String remoteAddr;
+ private String user;
+ private int status;
+
+ public PublishRecord(String[] pp) throws ParseException {
+ super(pp);
+// This is too slow!
+// Matcher m = Pattern.compile(".*/publish/(\\d+)/(.*)$").matcher(pp[4]);
+// if (!m.matches())
+// throw new ParseException("bad pattern", 0);
+// this.feedFileid = m.group(2);
+ int ix = pp[4].indexOf("/publish/");
+ if (ix < 0)
+ throw new ParseException("bad pattern", 0);
+ ix = pp[4].indexOf('/', ix+9);
+ if (ix < 0)
+ throw new ParseException("bad pattern", 0);
+ this.feedFileid = pp[4].substring(ix+1);
+ this.remoteAddr = pp[8];
+ this.user = pp[9];
+ this.status = Integer.parseInt(pp[10]);
+ }
+ public PublishRecord(ResultSet rs) throws SQLException {
+ super(rs);
+ this.feedFileid = rs.getString("FEED_FILEID");
+ this.remoteAddr = rs.getString("REMOTE_ADDR");
+ this.user = rs.getString("USER");
+ this.status = rs.getInt("STATUS");
+ }
+ public String getFeedFileid() {
+ return feedFileid;
+ }
+
+ public void setFeedFileid(String feedFileid) {
+ this.feedFileid = feedFileid;
+ }
+
+ public String getRemoteAddr() {
+ return remoteAddr;
+ }
+
+ public void setRemoteAddr(String remoteAddr) {
+ this.remoteAddr = remoteAddr;
+ }
+
+ public String getUser() {
+ return user;
+ }
+
+ public void setUser(String user) {
+ this.user = user;
+ }
+
+ public int getStatus() {
+ return status;
+ }
+
+ public void setStatus(int status) {
+ this.status = status;
+ }
+
+
+ public LOGJSONObject reOrderObject(LOGJSONObject jo) {
+ LinkedHashMap<String,Object> logrecordObj = new LinkedHashMap<String,Object>();
+
+
+ logrecordObj.put("statusCode", jo.get("statusCode"));
+ logrecordObj.put("publishId", jo.get("publishId"));
+ logrecordObj.put("requestURI", jo.get("requestURI"));
+ logrecordObj.put("sourceIP", jo.get("sourceIP"));
+ logrecordObj.put("method", jo.get("method"));
+ logrecordObj.put("contentType", jo.get("contentType"));
+ logrecordObj.put("endpointId", jo.get("endpointId"));
+ logrecordObj.put("type", jo.get("type"));
+ logrecordObj.put("date", jo.get("date"));
+ logrecordObj.put("contentLength", jo.get("contentLength"));
+
+ LOGJSONObject newjo = new LOGJSONObject(logrecordObj);
+ return newjo;
+ }
+
+ @Override
+ public LOGJSONObject asJSONObject() {
+ LOGJSONObject jo = super.asJSONObject();
+ jo.put("type", "pub");
+// jo.put("feedFileid", feedFileid);
+// jo.put("remoteAddr", remoteAddr);
+// jo.put("user", user);
+ jo.put("sourceIP", remoteAddr);
+ jo.put("endpointId", user);
+ jo.put("statusCode", status);
+
+ LOGJSONObject newjo = this.reOrderObject(jo);
+
+ return newjo;
+ }
+ @Override
+ public void load(PreparedStatement ps) throws SQLException {
+ ps.setString(1, "pub"); // field 1: type
+ super.load(ps); // loads fields 2-8
+ ps.setString( 9, getFeedFileid());
+ ps.setString(10, getRemoteAddr());
+ ps.setString(11, getUser());
+ ps.setInt (12, getStatus());
+ ps.setNull (13, Types.INTEGER);
+ ps.setNull (14, Types.VARCHAR);
+ ps.setNull (15, Types.INTEGER);
+ ps.setNull (16, Types.INTEGER);
+ ps.setNull (17, Types.VARCHAR);
+ ps.setNull (19, Types.BIGINT);
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/SubDelivery.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/SubDelivery.java
new file mode 100644
index 00000000..66e44af5
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/SubDelivery.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.beans;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.json.JSONObject;
+
+/**
+ * The representation of Subscription delivery information. This includes the URL to deliver to,
+ * login and password, and whether to use the "HTTP 100-continue" feature for this subscription.
+ * @author Robert Eby
+ * @version $Id: SubDelivery.java,v 1.2 2013/06/20 14:11:05 eby Exp $
+ */
+public class SubDelivery implements JSONable {
+ private String url;
+ private String user;
+ private String password;
+ private boolean use100;
+
+ public SubDelivery() {
+ this("", "", "", false);
+ }
+ public SubDelivery(String url, String user, String password, boolean use100) {
+ this.url = url;
+ this.user = user;
+ this.password = password;
+ this.use100 = use100;
+ }
+ public SubDelivery(ResultSet rs) throws SQLException {
+ this.url = rs.getString("DELIVERY_URL");
+ this.user = rs.getString("DELIVERY_USER");
+ this.password = rs.getString("DELIVERY_PASSWORD");
+ this.use100 = rs.getBoolean("DELIVERY_USE100");
+
+ }
+ public String getUrl() {
+ return url;
+ }
+ public void setUrl(String url) {
+ this.url = url;
+ }
+ public String getUser() {
+ return user;
+ }
+ public void setUser(String user) {
+ this.user = user;
+ }
+ public String getPassword() {
+ return password;
+ }
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public boolean isUse100() {
+ return use100;
+ }
+ public void setUse100(boolean use100) {
+ this.use100 = use100;
+ }
+ @Override
+ public JSONObject asJSONObject() {
+ JSONObject jo = new JSONObject();
+ jo.put("url", url);
+ jo.put("user", user);
+ jo.put("password", password);
+ jo.put("use100", use100);
+ return jo;
+ }
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof SubDelivery))
+ return false;
+ SubDelivery os = (SubDelivery) obj;
+ if (!url.equals(os.url))
+ return false;
+ if (!user.equals(os.user))
+ return false;
+ if (!password.equals(os.password))
+ return false;
+ if (use100 != os.use100)
+ return false;
+ return true;
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/SubLinks.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/SubLinks.java
new file mode 100644
index 00000000..27128d86
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/SubLinks.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.beans;
+
+import java.io.InvalidObjectException;
+
+import org.json.JSONObject;
+
+/**
+ * The URLs associated with a Subscription.
+ * @author Robert Eby
+ * @version $Id: SubLinks.java,v 1.3 2013/07/05 13:48:05 eby Exp $
+ */
+public class SubLinks implements JSONable {
+ private String self;
+ private String feed;
+ private String log;
+
+ public SubLinks() {
+ self = feed = log = null;
+ }
+ public SubLinks(JSONObject jo) throws InvalidObjectException {
+ this();
+ self = jo.getString("self");
+ feed = jo.getString("feed");
+ log = jo.getString("log");
+ }
+ public SubLinks(String self, String feed, String log) {
+ this.self = self;
+ this.feed = feed;
+ this.log = log;
+ }
+ public String getSelf() {
+ return self;
+ }
+ public void setSelf(String self) {
+ this.self = self;
+ }
+ public String getFeed() {
+ return feed;
+ }
+ public void setFeed(String feed) {
+ this.feed = feed;
+ }
+ public String getLog() {
+ return log;
+ }
+ public void setLog(String log) {
+ this.log = log;
+ }
+
+ @Override
+ public JSONObject asJSONObject() {
+ JSONObject jo = new JSONObject();
+ jo.put("self", self);
+ jo.put("feed", feed);
+ jo.put("log", log);
+ return jo;
+ }
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof SubLinks))
+ return false;
+ SubLinks os = (SubLinks) obj;
+ if (!self.equals(os.self))
+ return false;
+ if (!feed.equals(os.feed))
+ return false;
+ if (!log.equals(os.log))
+ return false;
+ return true;
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Subscription.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Subscription.java
new file mode 100644
index 00000000..7ab10a45
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Subscription.java
@@ -0,0 +1,511 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package com.att.research.datarouter.provisioning.beans;
+
+import java.io.InvalidObjectException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.json.JSONObject;
+import java.util.Properties;
+
+import com.att.research.datarouter.provisioning.utils.DB;
+import com.att.research.datarouter.provisioning.utils.URLUtilities;
+
+/**
+ * The representation of a Subscription. Subscriptions can be retrieved from the DB, or stored/updated in the DB.
+ * @author Robert Eby
+ * @version $Id: Subscription.java,v 1.9 2013/10/28 18:06:53 eby Exp $
+ */
+public class Subscription extends Syncable {
+ private static Logger intlogger = Logger.getLogger("com.att.research.datarouter.provisioning.internal");
+ private static int next_subid = getMaxSubID() + 1;
+
+ private int subid;
+ private int feedid;
+ private int groupid; //New field is added - Groups feature Rally:US708115 - 1610
+ private SubDelivery delivery;
+ private boolean metadataOnly;
+ private String subscriber;
+ private SubLinks links;
+ private boolean suspended;
+ private Date last_mod;
+ private Date created_date;
+
+ public static Subscription getSubscriptionMatching(Subscription sub) {
+ SubDelivery deli = sub.getDelivery();
+ String sql = String.format(
+ "select * from SUBSCRIPTIONS where FEEDID = %d and DELIVERY_URL = \"%s\" and DELIVERY_USER = \"%s\" and DELIVERY_PASSWORD = \"%s\" and DELIVERY_USE100 = %d and METADATA_ONLY = %d",
+ sub.getFeedid(),
+ deli.getUrl(),
+ deli.getUser(),
+ deli.getPassword(),
+ deli.isUse100() ? 1 : 0,
+ sub.isMetadataOnly() ? 1 : 0
+ );
+ List<Subscription> list = getSubscriptionsForSQL(sql);
+ return list.size() > 0 ? list.get(0) : null;
+ }
+ public static Subscription getSubscriptionById(int id) {
+ String sql = "select * from SUBSCRIPTIONS where SUBID = " + id;
+ List<Subscription> list = getSubscriptionsForSQL(sql);
+ return list.size() > 0 ? list.get(0) : null;
+ }
+ public static Collection<Subscription> getAllSubscriptions() {
+ return getSubscriptionsForSQL("select * from SUBSCRIPTIONS");
+ }
+ private static List<Subscription> getSubscriptionsForSQL(String sql) {
+ List<Subscription> list = new ArrayList<Subscription>();
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery(sql);
+ while (rs.next()) {
+ Subscription sub = new Subscription(rs);
+ list.add(sub);
+ }
+ rs.close();
+ stmt.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return list;
+ }
+ public static int getMaxSubID() {
+ int max = 0;
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery("select MAX(subid) from SUBSCRIPTIONS");
+ if (rs.next()) {
+ max = rs.getInt(1);
+ }
+ rs.close();
+ stmt.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ intlogger.info("getMaxSubID: "+e.getMessage());
+ e.printStackTrace();
+ }
+ return max;
+ }
+ public static Collection<String> getSubscriptionUrlList(int feedid) {
+ List<String> list = new ArrayList<String>();
+ String sql = "select SUBID from SUBSCRIPTIONS where FEEDID = "+feedid;
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery(sql);
+ while (rs.next()) {
+ int subid = rs.getInt("SUBID");
+ list.add(URLUtilities.generateSubscriptionURL(subid));
+ }
+ rs.close();
+ stmt.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return list;
+ }
+ /**
+ * Return a count of the number of active subscriptions in the DB.
+ * @return the count
+ */
+ public static int countActiveSubscriptions() {
+ int count = 0;
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery("select count(*) from SUBSCRIPTIONS");
+ if (rs.next()) {
+ count = rs.getInt(1);
+ }
+ rs.close();
+ stmt.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ intlogger.warn("PROV0008 countActiveSubscriptions: "+e.getMessage());
+ e.printStackTrace();
+ }
+ return count;
+ }
+
+ public Subscription() {
+ this("", "", "");
+ }
+ public Subscription(String url, String user, String password) {
+ this.subid = -1;
+ this.feedid = -1;
+ this.groupid = -1; //New field is added - Groups feature Rally:US708115 - 1610
+ this.delivery = new SubDelivery(url, user, password, false);
+ this.metadataOnly = false;
+ this.subscriber = "";
+ this.links = new SubLinks();
+ this.suspended = false;
+ this.last_mod = new Date();
+ this.created_date = new Date();
+ }
+ public Subscription(ResultSet rs) throws SQLException {
+ this.subid = rs.getInt("SUBID");
+ this.feedid = rs.getInt("FEEDID");
+ this.groupid = rs.getInt("GROUPID"); //New field is added - Groups feature Rally:US708115 - 1610
+ this.delivery = new SubDelivery(rs);
+ this.metadataOnly = rs.getBoolean("METADATA_ONLY");
+ this.subscriber = rs.getString("SUBSCRIBER");
+ this.links = new SubLinks(rs.getString("SELF_LINK"), URLUtilities.generateFeedURL(feedid), rs.getString("LOG_LINK"));
+ this.suspended = rs.getBoolean("SUSPENDED");
+ this.last_mod = rs.getDate("LAST_MOD");
+ this.created_date = rs.getDate("CREATED_DATE");
+ }
+ public Subscription(JSONObject jo) throws InvalidObjectException {
+ this("", "", "");
+ try {
+ // The JSONObject is assumed to contain a vnd.att-dr.subscription representation
+ this.subid = jo.optInt("subid", -1);
+ this.feedid = jo.optInt("feedid", -1);
+ this.groupid = jo.optInt("groupid", -1); //New field is added - Groups feature Rally:US708115 - 1610
+
+ JSONObject jdeli = jo.getJSONObject("delivery");
+ String url = jdeli.getString("url");
+ String user = jdeli.getString("user");
+ String password = jdeli.getString("password");
+ boolean use100 = jdeli.getBoolean("use100");
+
+
+ //Data Router Subscriber HTTPS Relaxation feature USERSTORYID:US674047.
+ Properties p = (new DB()).getProperties();
+ if(p.get("com.att.research.datarouter.provserver.https.relaxation").toString().equals("false") && !jo.has("sync")) {
+ if (!url.startsWith("https://"))
+ throw new InvalidObjectException("delivery URL is not HTTPS");
+ }
+
+ if (url.length() > 256)
+ throw new InvalidObjectException("delivery url field is too long");
+ if (user.length() > 20)
+ throw new InvalidObjectException("delivery user field is too long");
+ if (password.length() > 32)
+ throw new InvalidObjectException("delivery password field is too long");
+ this.delivery = new SubDelivery(url, user, password, use100);
+
+ this.metadataOnly = jo.getBoolean("metadataOnly");
+ this.suspended = jo.optBoolean("suspend", false);
+
+ this.subscriber = jo.optString("subscriber", "");
+ JSONObject jol = jo.optJSONObject("links");
+ this.links = (jol == null) ? (new SubLinks()) : (new SubLinks(jol));
+ } catch (InvalidObjectException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new InvalidObjectException("invalid JSON: "+e.getMessage());
+ }
+ }
+ public int getSubid() {
+ return subid;
+ }
+ public void setSubid(int subid) {
+ this.subid = subid;
+
+ // Create link URLs
+ SubLinks sl = getLinks();
+ sl.setSelf(URLUtilities.generateSubscriptionURL(subid));
+ sl.setLog(URLUtilities.generateSubLogURL(subid));
+ }
+ public int getFeedid() {
+ return feedid;
+ }
+ public void setFeedid(int feedid) {
+ this.feedid = feedid;
+
+ // Create link URLs
+ SubLinks sl = getLinks();
+ sl.setFeed(URLUtilities.generateFeedURL(feedid));
+ }
+
+ //New getter setters for Groups feature Rally:US708115 - 1610
+ public int getGroupid() {
+ return groupid;
+ }
+ public void setGroupid(int groupid) {
+ this.groupid = groupid;
+ }
+
+ public SubDelivery getDelivery() {
+ return delivery;
+ }
+ public void setDelivery(SubDelivery delivery) {
+ this.delivery = delivery;
+ }
+ public boolean isMetadataOnly() {
+ return metadataOnly;
+ }
+ public void setMetadataOnly(boolean metadataOnly) {
+ this.metadataOnly = metadataOnly;
+ }
+ public boolean isSuspended() {
+ return suspended;
+ }
+ public void setSuspended(boolean suspended) {
+ this.suspended = suspended;
+ }
+ public String getSubscriber() {
+ return subscriber;
+ }
+ public void setSubscriber(String subscriber) {
+ if (subscriber != null) {
+ if (subscriber.length() > 8)
+ subscriber = subscriber.substring(0, 8);
+ this.subscriber = subscriber;
+ }
+ }
+ public SubLinks getLinks() {
+ return links;
+ }
+ public void setLinks(SubLinks links) {
+ this.links = links;
+ }
+
+ @Override
+ public JSONObject asJSONObject() {
+ JSONObject jo = new JSONObject();
+ jo.put("subid", subid);
+ jo.put("feedid", feedid);
+ jo.put("groupid", groupid); //New field is added - Groups feature Rally:US708115 - 1610
+ jo.put("delivery", delivery.asJSONObject());
+ jo.put("metadataOnly", metadataOnly);
+ jo.put("subscriber", subscriber);
+ jo.put("links", links.asJSONObject());
+ jo.put("suspend", suspended);
+ jo.put("last_mod", last_mod.getTime());
+ jo.put("created_date", created_date.getTime());
+ return jo;
+ }
+ public JSONObject asLimitedJSONObject() {
+ JSONObject jo = asJSONObject();
+ jo.remove("subid");
+ jo.remove("feedid");
+ jo.remove("last_mod");
+ return jo;
+ }
+ public JSONObject asJSONObject(boolean hidepasswords) {
+ JSONObject jo = asJSONObject();
+ if (hidepasswords) {
+ jo.remove("subid"); // we no longer hide passwords, however we do hide these
+ jo.remove("feedid");
+ jo.remove("last_mod");
+ jo.remove("created_date");
+ }
+ return jo;
+ }
+ @Override
+ public boolean doInsert(Connection c) {
+ boolean rv = true;
+ PreparedStatement ps = null;
+ try {
+ if (subid == -1) {
+ // No feed ID assigned yet, so assign the next available one
+ setSubid(next_subid++);
+ }
+ // In case we insert a feed from synchronization
+ if (subid > next_subid)
+ next_subid = subid+1;
+
+ // Create the SUBSCRIPTIONS row
+ String sql = "insert into SUBSCRIPTIONS (SUBID, FEEDID, DELIVERY_URL, DELIVERY_USER, DELIVERY_PASSWORD, DELIVERY_USE100, METADATA_ONLY, SUBSCRIBER, SUSPENDED, GROUPID) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+ ps = c.prepareStatement(sql, new String[] { "SUBID" });
+ ps.setInt(1, subid);
+ ps.setInt(2, feedid);
+ ps.setString(3, getDelivery().getUrl());
+ ps.setString(4, getDelivery().getUser());
+ ps.setString(5, getDelivery().getPassword());
+ ps.setInt(6, getDelivery().isUse100()?1:0);
+ ps.setInt(7, isMetadataOnly()?1:0);
+ ps.setString(8, getSubscriber());
+ ps.setBoolean(9, isSuspended());
+ ps.setInt(10, groupid); //New field is added - Groups feature Rally:US708115 - 1610
+ ps.execute();
+ ps.close();
+// ResultSet rs = ps.getGeneratedKeys();
+// rs.first();
+// setSubid(rs.getInt(1)); // side effect - sets the link URLs
+// ps.close();
+
+ // Update the row to set the URLs
+ sql = "update SUBSCRIPTIONS set SELF_LINK = ?, LOG_LINK = ? where SUBID = ?";
+ ps = c.prepareStatement(sql);
+ ps.setString(1, getLinks().getSelf());
+ ps.setString(2, getLinks().getLog());
+ ps.setInt(3, subid);
+ ps.execute();
+ ps.close();
+ } catch (SQLException e) {
+ rv = false;
+ intlogger.warn("PROV0005 doInsert: "+e.getMessage());
+ e.printStackTrace();
+ } finally {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ return rv;
+ }
+ @Override
+ public boolean doUpdate(Connection c) {
+ boolean rv = true;
+ PreparedStatement ps = null;
+ try {
+ String sql = "update SUBSCRIPTIONS set DELIVERY_URL = ?, DELIVERY_USER = ?, DELIVERY_PASSWORD = ?, DELIVERY_USE100 = ?, METADATA_ONLY = ?, SUSPENDED = ?, GROUPID = ? where SUBID = ?";
+ ps = c.prepareStatement(sql);
+ ps.setString(1, delivery.getUrl());
+ ps.setString(2, delivery.getUser());
+ ps.setString(3, delivery.getPassword());
+ ps.setInt(4, delivery.isUse100()?1:0);
+ ps.setInt(5, isMetadataOnly()?1:0);
+ ps.setInt(6, suspended ? 1 : 0);
+ ps.setInt(7, groupid); //New field is added - Groups feature Rally:US708115 - 1610
+ ps.setInt(8, subid);
+ ps.executeUpdate();
+ } catch (SQLException e) {
+ rv = false;
+ intlogger.warn("PROV0006 doUpdate: "+e.getMessage());
+ e.printStackTrace();
+ } finally {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ return rv;
+ }
+
+
+
+ /**Rally US708115
+ * Change Ownership of Subscription - 1610
+ * */
+ public boolean changeOwnerShip() {
+ boolean rv = true;
+ PreparedStatement ps = null;
+ try {
+
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection c = db.getConnection();
+ String sql = "update SUBSCRIPTIONS set SUBSCRIBER = ? where SUBID = ?";
+ ps = c.prepareStatement(sql);
+ ps.setString(1, this.subscriber);
+ ps.setInt(2, subid);
+ ps.execute();
+ ps.close();
+ } catch (SQLException e) {
+ rv = false;
+ intlogger.warn("PROV0006 doUpdate: "+e.getMessage());
+ e.printStackTrace();
+ } finally {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ return rv;
+ }
+
+
+
+ @Override
+ public boolean doDelete(Connection c) {
+ boolean rv = true;
+ PreparedStatement ps = null;
+ try {
+ String sql = "delete from SUBSCRIPTIONS where SUBID = ?";
+ ps = c.prepareStatement(sql);
+ ps.setInt(1, subid);
+ ps.execute();
+ } catch (SQLException e) {
+ rv = false;
+ intlogger.warn("PROV0007 doDelete: "+e.getMessage());
+ e.printStackTrace();
+ } finally {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ return rv;
+ }
+ @Override
+ public String getKey() {
+ return ""+getSubid();
+ }
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Subscription))
+ return false;
+ Subscription os = (Subscription) obj;
+ if (subid != os.subid)
+ return false;
+ if (feedid != os.feedid)
+ return false;
+ if (groupid != os.groupid) //New field is added - Groups feature Rally:US708115 - 1610
+ return false;
+ if (!delivery.equals(os.delivery))
+ return false;
+ if (metadataOnly != os.metadataOnly)
+ return false;
+ if (!subscriber.equals(os.subscriber))
+ return false;
+ if (!links.equals(os.links))
+ return false;
+ if (suspended != os.suspended)
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "SUB: subid=" + subid + ", feedid=" + feedid;
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Syncable.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Syncable.java
new file mode 100644
index 00000000..00163c1c
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Syncable.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.beans;
+
+import java.sql.Connection;
+
+import org.json.JSONObject;
+
+/**
+ * This abstract class defines the "contract" for beans that can be sync-ed with the database,
+ * by means of straight comparison. The <i>getKey</i> method is used to return the primary key
+ * used to identify a record.
+ *
+ * @author Robert Eby
+ * @version $Id: Syncable.java,v 1.1 2013/07/05 13:48:05 eby Exp $
+ */
+public abstract class Syncable implements Deleteable, Insertable, Updateable, JSONable {
+ @Override
+ abstract public JSONObject asJSONObject();
+
+ @Override
+ abstract public boolean doUpdate(Connection c);
+
+ @Override
+ abstract public boolean doInsert(Connection c);
+
+ @Override
+ abstract public boolean doDelete(Connection c);
+
+ /**
+ * Get the "natural key" for this object type, as a String.
+ * @return the key
+ */
+ abstract public String getKey();
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Updateable.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Updateable.java
new file mode 100644
index 00000000..a9b19e7c
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/Updateable.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package com.att.research.datarouter.provisioning.beans;
+
+import java.sql.Connection;
+
+/**
+ * An object that can be UPDATE-ed in the database.
+ * @author Robert Eby
+ * @version $Id: Updateable.java,v 1.2 2013/05/29 14:44:36 eby Exp $
+ */
+public interface Updateable {
+ /**
+ * Update this object in the DB.
+ * @param c the JDBC Connection to use
+ * @return true if the UPDATE succeeded, false otherwise
+ */
+ public boolean doUpdate(Connection c);
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/package.html b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/package.html
new file mode 100644
index 00000000..4b28053a
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/beans/package.html
@@ -0,0 +1,31 @@
+#-------------------------------------------------------------------------------
+# ============LICENSE_START==================================================
+# * org.onap.dmaap
+# * ===========================================================================
+# * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+# * ===========================================================================
+# * 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.
+# * ============LICENSE_END====================================================
+# *
+# * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+# *
+#-------------------------------------------------------------------------------
+
+<html>
+<body>
+<p>
+This package provides beans to represent the basic provisioning objects of the Data Router application.
+These objects are defined by the document <b>Data Router Release 1 Provisioning API</b> <i>Version 1.2</i>.
+</p>
+</body>
+</html>
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/eelf/EelfMsgs.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/eelf/EelfMsgs.java
new file mode 100644
index 00000000..3a230416
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/eelf/EelfMsgs.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package com.att.research.datarouter.provisioning.eelf;
+
+import com.att.eelf.i18n.EELFResolvableErrorEnum;
+import com.att.eelf.i18n.EELFResourceManager;
+
+public enum EelfMsgs implements EELFResolvableErrorEnum {
+
+ /**
+ * Application message prints user (accepts one argument)
+ */
+ MESSAGE_WITH_BEHALF,
+
+ /**
+ * Application message prints user and FeedID (accepts two arguments)
+ */
+
+ MESSAGE_WITH_BEHALF_AND_FEEDID,
+
+ /**
+ * Application message prints user and SUBID (accepts two arguments)
+ */
+
+ MESSAGE_WITH_BEHALF_AND_SUBID;
+
+
+
+ /**
+ * Static initializer to ensure the resource bundles for this class are loaded...
+ * Here this application loads messages from three bundles
+ */
+ static {
+ EELFResourceManager.loadMessageBundle("EelfMessages");
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/eelf/JettyFilter.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/eelf/JettyFilter.java
new file mode 100644
index 00000000..cfef910c
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/eelf/JettyFilter.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package com.att.research.datarouter.provisioning.eelf;
+
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.filter.Filter;
+import ch.qos.logback.core.spi.FilterReply;
+
+public class JettyFilter extends Filter<ILoggingEvent>{
+ @Override
+ public FilterReply decide(ILoggingEvent event) {
+ if (event.getLoggerName().contains("org.eclipse.jetty")) {
+ return FilterReply.ACCEPT;
+ } else {
+ return FilterReply.DENY;
+ }
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/package.html b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/package.html
new file mode 100644
index 00000000..7b009312
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/package.html
@@ -0,0 +1,123 @@
+#-------------------------------------------------------------------------------
+# ============LICENSE_START==================================================
+# * org.onap.dmaap
+# * ===========================================================================
+# * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+# * ===========================================================================
+# * 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.
+# * ============LICENSE_END====================================================
+# *
+# * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+# *
+#-------------------------------------------------------------------------------
+
+<html>
+<body>
+<p>
+This package provides the servlets used by the provisioning server for the Data Router application.
+URLs are from the document <b>URLs for DR Release 1</b> <i>Version 1.2</i>.
+</p>
+<div class="contentContainer">
+<table class="packageSummary" border="0" cellpadding="3" cellspacing="0">
+<caption><span>URL Path Summary</span><span class="tabEnd">&nbsp;</span></caption>
+<tr class="altColor">
+ <th class="colFirst">URL Path</th>
+ <th class="colOne">Symbolic Name</th>
+ <th class="colLast">Servlet Name</th>
+ <th class="colLast" colspan="4">Allowed Methods</th>
+</tr>
+<tr>
+ <td class="colFirst" class="colOne">/</td>
+ <td class="colOne" class="colOne">&lt;drFeedsUrl&gt;</td>
+ <td class="colLast" class="colOne">{@link com.att.research.datarouter.provisioning.DRFeedsServlet}</td>
+ <td class="colLast" class="colOne" style="background-color: pink">DELETE</td>
+ <td class="colLast" class="colOne" style="background-color: lightgreen">GET</td>
+ <td class="colLast" class="colOne" style="background-color: lightgreen">POST</td>
+ <td class="colLast" class="colOne" style="background-color: pink">PUT</td>
+</tr>
+<tr class="altColor">
+ <td class="colFirst" class="colOne">/feed/feedid</td>
+ <td class="colOne" class="colOne">&lt;feedUrl&gt;</td>
+ <td class="colLast" class="colOne">{@link com.att.research.datarouter.provisioning.FeedServlet}</td>
+ <td class="colLast" style="background-color: lightgreen">DELETE</td>
+ <td class="colLast" style="background-color: lightgreen">GET</td>
+ <td class="colLast" style="background-color: pink">POST</td>
+ <td class="colLast" style="background-color: lightgreen">PUT</td>
+</tr>
+<tr>
+ <td class="colFirst">/publish/feedid</td>
+ <td class="colOne">&lt;publishUrl&gt;</td>
+ <td class="colLast">{@link com.att.research.datarouter.provisioning.PublishServlet}</td>
+ <td class="colLast" style="background-color: lightgreen">DELETE</td>
+ <td class="colLast" style="background-color: lightgreen">GET</td>
+ <td class="colLast" style="background-color: lightgreen">POST</td>
+ <td class="colLast" style="background-color: lightgreen">PUT</td>
+</tr>
+<tr class="altColor">
+ <td class="colFirst">/subscribe/feedid</td>
+ <td class="colOne">&lt;subscribeUrl&gt;</td>
+ <td class="colLast">{@link com.att.research.datarouter.provisioning.SubscribeServlet}</td>
+ <td class="colLast" style="background-color: pink">DELETE</td>
+ <td class="colLast" style="background-color: lightgreen">GET</td>
+ <td class="colLast" style="background-color: lightgreen">POST</td>
+ <td class="colLast" style="background-color: pink">PUT</td>
+</tr>
+<tr>
+ <td class="colFirst">/feedlog/feedid</td>
+ <td class="colOne">&lt;feedLogUrl&gt;</td>
+ <td class="colLast">{@link com.att.research.datarouter.provisioning.FeedLogServlet}</td>
+ <td class="colLast" style="background-color: pink">DELETE</td>
+ <td class="colLast" style="background-color: lightgreen">GET</td>
+ <td class="colLast" style="background-color: pink">POST</td>
+ <td class="colLast" style="background-color: pink">PUT</td>
+</tr>
+<tr class="altColor">
+ <td class="colFirst">/subs/subid</td>
+ <td class="colOne">&lt;subscriptionUrl&gt;</td>
+ <td class="colLast">{@link com.att.research.datarouter.provisioning.SubscriptionServlet}</td>
+ <td class="colLast" style="background-color: lightgreen">DELETE</td>
+ <td class="colLast" style="background-color: lightgreen">GET</td>
+ <td class="colLast" style="background-color: lightgreen">POST</td>
+ <td class="colLast" style="background-color: lightgreen">PUT</td>
+</tr>
+<tr>
+ <td class="colFirst">/sublog/subid</td>
+ <td class="colOne">&lt;subLogUrl&gt;</td>
+ <td class="colLast">{@link com.att.research.datarouter.provisioning.SubLogServlet}</td>
+ <td class="colLast" style="background-color: pink">DELETE</td>
+ <td class="colLast" style="background-color: lightgreen">GET</td>
+ <td class="colLast" style="background-color: pink">POST</td>
+ <td class="colLast" style="background-color: pink">PUT</td>
+</tr>
+<tr class="altColor">
+ <td class="colFirst">/internal/*</td>
+ <td class="colOne">&lt;internalUrl&gt;</td>
+ <td class="colLast">{@link com.att.research.datarouter.provisioning.InternalServlet}</td>
+ <td class="colLast" style="background-color: lightgreen">DELETE</td>
+ <td class="colLast" style="background-color: lightgreen">GET</td>
+ <td class="colLast" style="background-color: lightgreen">POST</td>
+ <td class="colLast" style="background-color: lightgreen">PUT</td>
+</tr>
+<tr>
+ <td class="colFirst">/internal/route/*</td>
+ <td class="colOne">&lt;routeUrl&gt;</td>
+ <td class="colLast">{@link com.att.research.datarouter.provisioning.RouteServlet}</td>
+ <td class="colLast" style="background-color: lightgreen">DELETE</td>
+ <td class="colLast" style="background-color: lightgreen">GET</td>
+ <td class="colLast" style="background-color: lightgreen">POST</td>
+ <td class="colLast" style="background-color: pink">PUT</td>
+</tr>
+</table>
+</div>
+</body>
+</html>
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/DB.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/DB.java
new file mode 100644
index 00000000..ec4b0e68
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/DB.java
@@ -0,0 +1,711 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.utils;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.LineNumberReader;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.NoSuchElementException;
+import java.util.Properties;
+import java.util.Queue;
+import java.util.Set;
+
+import org.apache.log4j.Logger;
+
+import com.att.research.datarouter.provisioning.beans.DeliveryRecord;
+import com.att.research.datarouter.provisioning.beans.ExpiryRecord;
+import com.att.research.datarouter.provisioning.beans.Loadable;
+import com.att.research.datarouter.provisioning.beans.PublishRecord;
+
+/**
+ * Load the DB JDBC driver, and manage a simple pool of connections to the DB.
+ *
+ * @author Robert Eby
+ * @version $Id$
+ */
+public class DB {
+ /** The name of the properties file (in CLASSPATH) */
+ public static final String CONFIG_FILE = "provserver.properties";
+
+ private static String DB_DRIVER = "com.mysql.jdbc.Driver";
+ private static String DB_URL = "jdbc:mysql://127.0.0.1:3306/datarouter";
+ private static String DB_LOGIN = "datarouter";
+ private static String DB_PASSWORD = "datarouter";
+ private static Properties props;
+ private static Logger intlogger = Logger.getLogger("com.att.research.datarouter.provisioning.internal");
+ private static Queue<Connection> queue = new LinkedList<Connection>();
+
+ public static String HTTPS_PORT;
+ public static String HTTP_PORT;
+
+ /**
+ * Construct a DB object. If this is the very first creation of this object, it will load a copy
+ * of the properties for the server, and attempt to load the JDBC driver for the database. If a fatal
+ * error occurs (e.g. either the properties file or the DB driver is missing), the JVM will exit.
+ */
+ public DB() {
+ if (props == null) {
+ props = new Properties();
+ InputStream inStream = getClass().getClassLoader().getResourceAsStream(CONFIG_FILE);
+ try {
+ props.load(inStream);
+ DB_DRIVER = (String) props.get("com.att.research.datarouter.db.driver");
+ DB_URL = (String) props.get("com.att.research.datarouter.db.url");
+ DB_LOGIN = (String) props.get("com.att.research.datarouter.db.login");
+ DB_PASSWORD = (String) props.get("com.att.research.datarouter.db.password");
+ HTTPS_PORT = (String) props.get("com.att.research.datarouter.provserver.https.port");
+ HTTP_PORT = (String) props.get("com.att.research.datarouter.provserver.http.port");
+ Class.forName(DB_DRIVER);
+ } catch (IOException e) {
+ intlogger.fatal("PROV9003 Opening properties: "+e.getMessage());
+ e.printStackTrace();
+ System.exit(1);
+ } catch (ClassNotFoundException e) {
+ intlogger.fatal("PROV9004 cannot find the DB driver: "+e);
+ e.printStackTrace();
+ System.exit(1);
+ } finally {
+ try {
+ inStream.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+ /**
+ * Get the provisioning server properties (loaded from provserver.properties).
+ * @return the Properties object
+ */
+ public Properties getProperties() {
+ return props;
+ }
+ /**
+ * Get a JDBC connection to the DB from the pool. Creates a new one if none are available.
+ * @return the Connection
+ * @throws SQLException
+ */
+ @SuppressWarnings("resource")
+ public Connection getConnection() throws SQLException {
+ Connection c = null;
+ while (c == null) {
+ synchronized (queue) {
+ try {
+ c = queue.remove();
+ } catch (NoSuchElementException e) {
+ int n = 0;
+ do {
+ // Try up to 3 times to get a connection
+ try {
+ c = DriverManager.getConnection(DB_URL, DB_LOGIN, DB_PASSWORD);
+ } catch (SQLException e1) {
+ if (++n >= 3)
+ throw e1;
+ }
+ } while (c == null);
+ }
+ }
+ if (c != null && !c.isValid(1)) {
+ c.close();
+ c = null;
+ }
+ }
+ return c;
+ }
+ /**
+ * Returns a JDBC connection to the pool.
+ * @param c the Connection to return
+ * @throws SQLException
+ */
+ public void release(Connection c) {
+ if (c != null) {
+ synchronized (queue) {
+ if (!queue.contains(c))
+ queue.add(c);
+ }
+ }
+ }
+
+ /**
+ * Run all necessary retrofits required to bring the database up to the level required for this version
+ * of the provisioning server. This should be run before the server itself is started.
+ * @return true if all retrofits worked, false otherwise
+ */
+ public boolean runRetroFits() {
+ return retroFit1()
+ && retroFit2()
+ && retroFit3()
+ && retroFit4()
+ && retroFit5()
+ && retroFit6()
+ && retroFit7()
+ && retroFit8()
+ && retroFit9() //New retroFit call to add CREATED_DATE column Rally:US674199 - 1610
+ && retroFit10() //New retroFit call to add BUSINESS_DESCRIPTION column Rally:US708102 - 1610
+ && retroFit11() //New retroFit call for groups feature Rally:US708115 - 1610
+ ;
+ }
+ /**
+ * Retrofit 1 - Make sure the expected tables are in MySQL and are initialized.
+ * Uses mysql_init_0000 and mysql_init_0001 to setup the DB.
+ * @return true if the retrofit worked, false otherwise
+ */
+ private boolean retroFit1() {
+ final String[] expected_tables = {
+ "FEEDS", "FEED_ENDPOINT_ADDRS", "FEED_ENDPOINT_IDS", "PARAMETERS", "SUBSCRIPTIONS"
+ };
+ Connection c = null;
+ try {
+ c = getConnection();
+ Set<String> tables = getTableSet(c);
+ boolean initialize = false;
+ for (String s : expected_tables) {
+ initialize |= !tables.contains(s);
+ }
+ if (initialize) {
+ intlogger.info("PROV9001: First time startup; The database is being initialized.");
+ runInitScript(c, 0); // script 0 creates the provisioning tables
+ runInitScript(c, 1); // script 1 initializes PARAMETERS
+ }
+ } catch (SQLException e) {
+ intlogger.fatal("PROV9000: The database credentials are not working: "+e.getMessage());
+ return false;
+ } finally {
+ if (c != null)
+ release(c);
+ }
+ return true;
+ }
+ /**
+ * Retrofit 2 - if the LOG_RECORDS table is missing, add it.
+ * Uses mysql_init_0002 to create this table.
+ * @return true if the retrofit worked, false otherwise
+ */
+ private boolean retroFit2() {
+ Connection c = null;
+ try {
+ // If LOG_RECORDS table is missing, add it
+ c = getConnection();
+ Set<String> tables = getTableSet(c);
+ if (!tables.contains("LOG_RECORDS")) {
+ intlogger.info("PROV9002: Creating LOG_RECORDS table.");
+ runInitScript(c, 2); // script 2 creates the LOG_RECORDS table
+ }
+ } catch (SQLException e) {
+ intlogger.fatal("PROV9000: The database credentials are not working: "+e.getMessage());
+ return false;
+ } finally {
+ if (c != null)
+ release(c);
+ }
+ return true;
+ }
+ /**
+ * Retrofit 3 - if the FEEDS_UNIQUEID table (from release 1.0.*) exists, drop it.
+ * If SUBSCRIPTIONS.SUBID still has the auto_increment attribute, remove it.
+ * @return true if the retrofit worked, false otherwise
+ */
+ @SuppressWarnings("resource")
+ private boolean retroFit3() {
+ Connection c = null;
+ try {
+ // if SUBSCRIPTIONS.SUBID still has auto_increment, remove it
+ boolean doremove = false;
+ c = getConnection();
+ DatabaseMetaData md = c.getMetaData();
+ ResultSet rs = md.getColumns("datarouter", "", "SUBSCRIPTIONS", "SUBID");
+ if (rs != null) {
+ while (rs.next()) {
+ doremove = rs.getString("IS_AUTOINCREMENT").equals("YES");
+ }
+ rs.close();
+ rs = null;
+ }
+ if (doremove) {
+ intlogger.info("PROV9002: Modifying SUBSCRIPTIONS SUBID column to remove auto increment.");
+ Statement s = c.createStatement();
+ s.execute("ALTER TABLE SUBSCRIPTIONS MODIFY COLUMN SUBID INT UNSIGNED NOT NULL");
+ s.close();
+ }
+
+ // Remove the FEEDS_UNIQUEID table, if it exists
+ Set<String> tables = getTableSet(c);
+ if (tables.contains("FEEDS_UNIQUEID")) {
+ intlogger.info("PROV9002: Dropping FEEDS_UNIQUEID table.");
+ Statement s = c.createStatement();
+ s.execute("DROP TABLE FEEDS_UNIQUEID");
+ s.close();
+ }
+ } catch (SQLException e) {
+ intlogger.fatal("PROV9000: The database credentials are not working: "+e.getMessage());
+ return false;
+ } finally {
+ if (c != null)
+ release(c);
+ }
+ return true;
+ }
+ private long nextid = 0; // used for initial creation of LOG_RECORDS table.
+ /**
+ * Retrofit 4 - if old log tables exist (from release 1.0.*), copy them to LOG_RECORDS, then drop them.
+ * @return true if the retrofit worked, false otherwise
+ */
+ @SuppressWarnings("resource")
+ private boolean retroFit4() {
+ Connection c = null;
+ try {
+ c = getConnection();
+ Set<String> tables = getTableSet(c);
+ if (tables.contains("PUBLISH_RECORDS")) {
+ intlogger.info("PROV9002: Copying PUBLISH_RECORDS to LOG_RECORDS table.");
+ copyLogTable("PUBLISH_RECORDS", PublishRecord.class);
+ intlogger.info("PROV9002: Dropping PUBLISH_RECORDS table.");
+ Statement s = c.createStatement();
+ s.execute("DROP TABLE PUBLISH_RECORDS");
+ s.close();
+ }
+ if (tables.contains("DELIVERY_RECORDS")) {
+ intlogger.info("PROV9002: Copying DELIVERY_RECORDS to LOG_RECORDS table.");
+ copyLogTable("DELIVERY_RECORDS", DeliveryRecord.class);
+ intlogger.info("PROV9002: Dropping DELIVERY_RECORDS table.");
+ Statement s = c.createStatement();
+ s.execute("DROP TABLE DELIVERY_RECORDS");
+ s.close();
+ }
+ if (tables.contains("EXPIRY_RECORDS")) {
+ intlogger.info("PROV9002: Copying EXPIRY_RECORDS to LOG_RECORDS table.");
+ copyLogTable("EXPIRY_RECORDS", ExpiryRecord.class);
+ intlogger.info("PROV9002: Dropping EXPIRY_RECORDS table.");
+ Statement s = c.createStatement();
+ s.execute("DROP TABLE EXPIRY_RECORDS");
+ s.close();
+ }
+ } catch (SQLException e) {
+ intlogger.fatal("PROV9000: The database credentials are not working: "+e.getMessage());
+ return false;
+ } finally {
+ if (c != null)
+ release(c);
+ }
+ return true;
+ }
+ /**
+ * Retrofit 5 - Create the new routing tables required for Release 2.
+ * Adds a new "SUSPENDED" column to FEEDS and SUBSCRIPTIONS.
+ * Modifies the LOG_RECORDS table to handle new R2 records.
+ * @return true if the retrofit worked, false otherwise
+ */
+ @SuppressWarnings("resource")
+ private boolean retroFit5() {
+ final String[] expected_tables = {
+ "INGRESS_ROUTES", "EGRESS_ROUTES", "NETWORK_ROUTES", "NODESETS", "NODES"
+ };
+ Connection c = null;
+ try {
+ // If expected tables are not present, then add new routing tables
+ c = getConnection();
+ Set<String> tables = getTableSet(c);
+ boolean initialize = false;
+ for (String s : expected_tables) {
+ initialize |= !tables.contains(s);
+ }
+ if (initialize) {
+ intlogger.info("PROV9002: Adding routing tables for Release 2.0.");
+ runInitScript(c, 3); // script 3 creates the routing tables
+ }
+
+ // Add SUSPENDED column to FEEDS/SUBSCRIPTIONS
+ DatabaseMetaData md = c.getMetaData();
+ for (String tbl : new String[] {"FEEDS", "SUBSCRIPTIONS" }) {
+ boolean add_col = true;
+ ResultSet rs = md.getColumns("datarouter", "", tbl, "SUSPENDED");
+ if (rs != null) {
+ add_col = !rs.next();
+ rs.close();
+ rs = null;
+ }
+ if (add_col) {
+ intlogger.info("PROV9002: Adding SUSPENDED column to "+tbl+" table.");
+ Statement s = c.createStatement();
+ s.execute("ALTER TABLE "+tbl+" ADD COLUMN SUSPENDED BOOLEAN DEFAULT FALSE");
+ s.close();
+ }
+ }
+
+ // Modify LOG_RECORDS for R2
+ intlogger.info("PROV9002: Modifying LOG_RECORDS table.");
+ Statement s = c.createStatement();
+ s.execute("ALTER TABLE LOG_RECORDS MODIFY COLUMN TYPE ENUM('pub', 'del', 'exp', 'pbf', 'dlx') NOT NULL");
+ s.close();
+ s = c.createStatement();
+ s.execute("ALTER TABLE LOG_RECORDS MODIFY COLUMN REASON ENUM('notRetryable', 'retriesExhausted', 'diskFull', 'other')");
+ s.close();
+ boolean add_col = true;
+ ResultSet rs = md.getColumns("datarouter", "", "LOG_RECORDS", "CONTENT_LENGTH_2");
+ if (rs != null) {
+ add_col = !rs.next();
+ rs.close();
+ rs = null;
+ }
+ if (add_col) {
+ intlogger.info("PROV9002: Fixing two columns in LOG_RECORDS table (this may take some time).");
+ s = c.createStatement();
+ s.execute("ALTER TABLE LOG_RECORDS MODIFY COLUMN CONTENT_LENGTH BIGINT NOT NULL, ADD COLUMN CONTENT_LENGTH_2 BIGINT AFTER RECORD_ID");
+ s.close();
+ }
+ } catch (SQLException e) {
+ intlogger.fatal("PROV9000: The database credentials are not working: "+e.getMessage());
+ return false;
+ } finally {
+ if (c != null)
+ release(c);
+ }
+ return true;
+ }
+ /**
+ * Retrofit 6 - Adjust LOG_RECORDS.USER to be 50 chars (MR #74).
+ * @return true if the retrofit worked, false otherwise
+ */
+ @SuppressWarnings("resource")
+ private boolean retroFit6() {
+ Connection c = null;
+ try {
+ c = getConnection();
+ // Modify LOG_RECORDS for R2
+ intlogger.info("PROV9002: Modifying LOG_RECORDS.USER length.");
+ Statement s = c.createStatement();
+ s.execute("ALTER TABLE LOG_RECORDS MODIFY COLUMN USER VARCHAR(50)");
+ s.close();
+ } catch (SQLException e) {
+ intlogger.fatal("PROV9000: The database credentials are not working: "+e.getMessage());
+ return false;
+ } finally {
+ if (c != null)
+ release(c);
+ }
+ return true;
+ }
+ /**
+ * Retrofit 7 - Adjust LOG_RECORDS.FEED_FILEID and LOG_RECORDS.DELIVERY_FILEID to be 256 chars.
+ * @return true if the retrofit worked, false otherwise
+ */
+ @SuppressWarnings("resource")
+ private boolean retroFit7() {
+ Connection c = null;
+ try {
+ c = getConnection();
+ // Modify LOG_RECORDS for long (>128) FILEIDs
+ intlogger.info("PROV9002: Modifying LOG_RECORDS.USER length.");
+ Statement s = c.createStatement();
+ s.execute("ALTER TABLE LOG_RECORDS MODIFY COLUMN FEED_FILEID VARCHAR(256), MODIFY COLUMN DELIVERY_FILEID VARCHAR(256)");
+ s.close();
+ } catch (SQLException e) {
+ intlogger.fatal("PROV9000: The database credentials are not working: "+e.getMessage());
+ return false;
+ } finally {
+ if (c != null)
+ release(c);
+ }
+ return true;
+ }
+ /**
+ * Retrofit 8 - Adjust FEEDS.NAME to be 255 chars (MR #74).
+ * @return true if the retrofit worked, false otherwise
+ */
+ @SuppressWarnings("resource")
+ private boolean retroFit8() {
+ Connection c = null;
+ try {
+ c = getConnection();
+ intlogger.info("PROV9002: Modifying FEEDS.NAME length.");
+ Statement s = c.createStatement();
+ s.execute("ALTER TABLE FEEDS MODIFY COLUMN NAME VARCHAR(255)");
+ s.close();
+ } catch (SQLException e) {
+ intlogger.fatal("PROV9000: The database credentials are not working: "+e.getMessage());
+ return false;
+ } finally {
+ if (c != null)
+ release(c);
+ }
+ return true;
+ }
+
+ /**
+ * Retrofit 9 - Add column FEEDS.CREATED_DATE and SUBSCRIPTIONS.CREATED_DATE, 1610 release user story US674199.
+ * @return true if the retrofit worked, false otherwise
+ */
+
+ @SuppressWarnings("resource")
+ private boolean retroFit9() {
+ Connection c = null;
+ try {
+ c = getConnection();
+ // Add CREATED_DATE column to FEEDS/SUBSCRIPTIONS tables
+ DatabaseMetaData md = c.getMetaData();
+ for (String tbl : new String[] {"FEEDS", "SUBSCRIPTIONS" }) {
+ boolean add_col = true;
+ ResultSet rs = md.getColumns("datarouter", "", tbl, "CREATED_DATE");
+ if (rs != null) {
+ add_col = !rs.next();
+ rs.close();
+ rs = null;
+ }
+ if (add_col) {
+ intlogger.info("PROV9002: Adding CREATED_DATE column to "+tbl+" table.");
+ Statement s = c.createStatement();
+ s.execute("ALTER TABLE "+tbl+" ADD COLUMN CREATED_DATE timestamp DEFAULT CURRENT_TIMESTAMP");
+ s.close();
+ }
+ }
+ } catch (SQLException e) {
+ intlogger.fatal("PROV9000: The database credentials are not working: "+e.getMessage());
+ return false;
+ } finally {
+ if (c != null)
+ release(c);
+ }
+ return true;
+ }
+
+ /**
+ * Retrofit 10 -Adding business BUSINESS_DESCRIPTION to FEEDS table (Rally
+ * US708102).
+ *
+ * @return true if the retrofit worked, false otherwise
+ */
+
+ @SuppressWarnings("resource")
+ private boolean retroFit10() {
+ Connection c = null;
+ boolean addColumn = true;
+
+ try {
+
+ c = getConnection();
+ // Add BUSINESS_DESCRIPTION column to FEEDS table
+ DatabaseMetaData md = c.getMetaData();
+ boolean add_col = true;
+ ResultSet rs = md.getColumns("datarouter", "", "FEEDS", "BUSINESS_DESCRIPTION");
+ if (rs != null) {
+ add_col = !rs.next();
+ rs.close();
+ rs = null;
+ }
+ if(add_col) {
+ intlogger
+ .info("PROV9002: Adding BUSINESS_DESCRIPTION column to FEEDS table.");
+ Statement s = c.createStatement();
+ s.execute("ALTER TABLE FEEDS ADD COLUMN BUSINESS_DESCRIPTION varchar(1000) DEFAULT NULL AFTER DESCRIPTION, MODIFY COLUMN DESCRIPTION VARCHAR(1000)");
+ s.close();
+ }
+ }
+ catch (SQLException e) {
+ intlogger
+ .fatal("PROV9000: The database credentials are not working: "
+ + e.getMessage());
+ return false;
+ } finally {
+ if (c != null)
+ release(c);
+ }
+ return true;
+ }
+
+
+ /*New retroFit method is added for groups feature Rally:US708115 - 1610
+ * @retroFit11()
+ * @parmas: none
+ * @return - boolean if table and fields are created (Group table, group id in FEEDS, SUBSCRIPTION TABLES)
+ */
+ @SuppressWarnings("resource")
+ private boolean retroFit11() {
+ final String[] expected_tables = {
+ "GROUPS"
+ };
+ Connection c = null;
+
+ try {
+ // If expected tables are not present, then add new routing tables
+ c = getConnection();
+ Set<String> tables = getTableSet(c);
+ boolean initialize = false;
+ for (String s : expected_tables) {
+ initialize |= !tables.contains(s);
+ }
+ if (initialize) {
+ intlogger.info("PROV9002: Adding GROUPS table for Release 1610.");
+ runInitScript(c, 4); // script 4 creates the routing tables
+ }
+
+ // Add GROUPID column to FEEDS/SUBSCRIPTIONS
+ DatabaseMetaData md = c.getMetaData();
+ for (String tbl : new String[] {"FEEDS", "SUBSCRIPTIONS" }) {
+ boolean add_col = true;
+ ResultSet rs = md.getColumns("datarouter", "", tbl, "GROUPID");
+ if (rs != null) {
+ add_col = !rs.next();
+ rs.close();
+ rs = null;
+ }
+ if (add_col) {
+ intlogger.info("PROV9002: Adding GROUPID column to "+tbl+" table.");
+ Statement s = c.createStatement();
+ s.execute("ALTER TABLE "+tbl+" ADD COLUMN GROUPID INT(10) UNSIGNED NOT NULL DEFAULT 0 AFTER FEEDID");
+ s.close();
+ }
+ }
+ } catch (SQLException e) {
+ intlogger.fatal("PROV9000: The database credentials are not working: "+e.getMessage());
+ return false;
+ } finally {
+ if (c != null)
+ release(c);
+ }
+ return true;
+ }
+
+
+ /**
+ * Copy the log table <i>table_name</i> to LOG_RECORDS;
+ * @param table_name the name of the old (1.0.*) table to copy
+ * @param table_class the class used to instantiate a record from the table
+ * @throws SQLException if there is a problem getting a MySQL connection
+ */
+ @SuppressWarnings("resource")
+ private void copyLogTable(String table_name, Class<? extends Loadable> table_class) throws SQLException {
+ long start = System.currentTimeMillis();
+ int n = 0;
+ Connection c1 = getConnection();
+ Connection c2 = getConnection();
+
+ try {
+ Constructor<? extends Loadable> cnst = table_class.getConstructor(ResultSet.class);
+ PreparedStatement ps = c2.prepareStatement(LogfileLoader.INSERT_SQL);
+ Statement stmt = c1.createStatement();
+ ResultSet rs = stmt.executeQuery("select * from "+table_name);
+ while (rs.next()) {
+ Loadable rec = cnst.newInstance(rs);
+ rec.load(ps);
+ ps.setLong(18, ++nextid);
+ ps.executeUpdate();
+ if ((++n % 10000) == 0)
+ intlogger.debug(" "+n+" records done.");
+ }
+ stmt.close();
+ ps.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ } catch (NoSuchMethodException e) {
+ e.printStackTrace();
+ } catch (SecurityException e) {
+ e.printStackTrace();
+ } catch (InstantiationException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ }
+
+ release(c1);
+ release(c2);
+ long x = (System.currentTimeMillis() - start);
+ intlogger.debug(" "+n+" records done in "+x+" ms.");
+ }
+
+ /**
+ * Get a set of all table names in the DB.
+ * @param c a DB connection
+ * @return the set of table names
+ */
+ private Set<String> getTableSet(Connection c) {
+ Set<String> tables = new HashSet<String>();
+ try {
+ DatabaseMetaData md = c.getMetaData();
+ ResultSet rs = md.getTables("datarouter", "", "", null);
+ if (rs != null) {
+ while (rs.next()) {
+ tables.add(rs.getString("TABLE_NAME"));
+ }
+ rs.close();
+ }
+ } catch (SQLException e) {
+ }
+ return tables;
+ }
+ /**
+ * Initialize the tables by running the initialization scripts located in the directory specified
+ * by the property <i>com.att.research.datarouter.provserver.dbscripts</i>. Scripts have names of
+ * the form mysql_init_NNNN.
+ * @param c a DB connection
+ * @param n the number of the mysql_init_NNNN script to run
+ */
+ private void runInitScript(Connection c, int n) {
+ String scriptdir = (String) props.get("com.att.research.datarouter.provserver.dbscripts");
+ StringBuilder sb = new StringBuilder();
+ try {
+ String scriptfile = String.format("%s/mysql_init_%04d", scriptdir, n);
+ if (!(new File(scriptfile)).exists())
+ return;
+
+ LineNumberReader in = new LineNumberReader(new FileReader(scriptfile));
+ String line;
+ while ((line = in.readLine()) != null) {
+ if (!line.startsWith("--")) {
+ line = line.trim();
+ sb.append(line);
+ if (line.endsWith(";")) {
+ // Execute one DDL statement
+ String sql = sb.toString();
+ sb.setLength(0);
+ Statement s = c.createStatement();
+ s.execute(sql);
+ s.close();
+ }
+ }
+ }
+ in.close();
+ sb.setLength(0);
+ } catch (Exception e) {
+ intlogger.fatal("PROV9002 Error when initializing table: "+e.getMessage());
+ System.exit(1);
+ }
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/DRRouteCLI.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/DRRouteCLI.java
new file mode 100644
index 00000000..36d46e3b
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/DRRouteCLI.java
@@ -0,0 +1,456 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.utils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.security.KeyStore;
+import java.util.Arrays;
+import java.util.Properties;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.StatusLine;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.impl.client.AbstractHttpClient;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.util.EntityUtils;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+
+/**
+ * This class provides a Command Line Interface for the routing tables in the DR Release 2.0 DB.
+ * A full description of this command is <a href="http://wiki.proto.research.att.com/doku.php?id=datarouter-route-cli">here</a>.
+ *
+ * @author Robert Eby
+ * @version $Id: DRRouteCLI.java,v 1.2 2013/11/05 15:54:16 eby Exp $
+ */
+public class DRRouteCLI {
+ /**
+ * Invoke the CLI. The CLI can be run with a single command (given as command line arguments),
+ * or in an interactive mode where the user types a sequence of commands to the program. The CLI is invoked via:
+ * <pre>
+ * java com.att.research.datarouter.provisioning.utils.DRRouteCLI [ -s <i>server</i> ] [ <i>command</i> ]
+ * </pre>
+ * A full description of the arguments to this command are
+ * <a href="http://wiki.proto.research.att.com/doku.php?id=datarouter-route-cli">here</a>.
+ *
+ * @param args command line arguments
+ * @throws Exception for any unrecoverable problem
+ */
+ public static void main(String[] args) throws Exception {
+ String server = System.getenv(ENV_VAR);
+ if (args.length >= 2 && args[0].equals("-s")) {
+ server = args[1];
+ String[] t = new String[args.length-2];
+ if (t.length > 0)
+ System.arraycopy(args, 2, t, 0, t.length);
+ args = t;
+ }
+ if (server == null || server.equals("")) {
+ System.err.println("dr-route: you need to specify a server, either via $PROVSRVR or the '-s' option.");
+ System.exit(1);
+ }
+ DRRouteCLI cli = new DRRouteCLI(server);
+ if (args.length > 0) {
+ boolean b = cli.runCommand(args);
+ System.exit(b ? 0 : 1);
+ } else {
+ cli.interactive();
+ System.exit(0);
+ }
+ }
+
+ public static final String ENV_VAR = "PROVSRVR";
+ public static final String PROMPT = "dr-route> ";
+ public static final String DEFAULT_TRUSTSTORE_PATH = /* $JAVA_HOME + */ "/jre/lib/security/cacerts";
+
+ private final String server;
+ private int width = 120; // screen width (for list)
+ private AbstractHttpClient httpclient;
+
+ /**
+ * Create a DRRouteCLI object connecting to the specified server.
+ * @param server the server to send command to
+ * @throws Exception
+ */
+ public DRRouteCLI(String server) throws Exception {
+ this.server = server;
+ this.width = 120;
+ this.httpclient = new DefaultHttpClient();
+
+ Properties p = (new DB()).getProperties();
+ String truststore_file = p.getProperty("com.att.research.datarouter.provserver.truststore.path");
+ String truststore_pw = p.getProperty("com.att.research.datarouter.provserver.truststore.password");
+
+ KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ if (truststore_file == null || truststore_file.equals("")) {
+ String jhome = System.getenv("JAVA_HOME");
+ if (jhome == null || jhome.equals(""))
+ jhome = "/opt/java/jdk/jdk180";
+ truststore_file = jhome + DEFAULT_TRUSTSTORE_PATH;
+ }
+ File f = new File(truststore_file);
+ if (f.exists()) {
+ FileInputStream instream = new FileInputStream(f);
+ try {
+ trustStore.load(instream, truststore_pw.toCharArray());
+ } catch (Exception x) {
+ System.err.println("Problem reading truststore: "+x);
+ throw x;
+ } finally {
+ try { instream.close(); } catch (Exception ignore) {}
+ }
+ }
+
+ SSLSocketFactory socketFactory = new SSLSocketFactory(trustStore);
+ Scheme sch = new Scheme("https", 443, socketFactory);
+ httpclient.getConnectionManager().getSchemeRegistry().register(sch);
+ }
+
+ private void interactive() throws IOException {
+ LineNumberReader in = new LineNumberReader(new InputStreamReader(System.in));
+ while (true) {
+ System.out.print(PROMPT);
+ String line = in.readLine();
+ if (line == null)
+ return;
+ line = line.trim();
+ if (line.equalsIgnoreCase("exit")) // "exit" may only be used in interactive mode
+ return;
+ if (line.equalsIgnoreCase("quit")) // "quit" may only be used in interactive mode
+ return;
+ String[] args = line.split("[ \t]+");
+ if (args.length > 0)
+ runCommand(args);
+ }
+ }
+
+ /**
+ * Run the command specified by the arguments.
+ * @param args The command line arguments.
+ * @return true if the command was valid and succeeded
+ */
+ public boolean runCommand(String[] args) {
+ String cmd = args[0].trim().toLowerCase();
+ if (cmd.equals("add")) {
+ if (args.length > 2) {
+ if (args[1].startsWith("in") && args.length >= 6) {
+ return addIngress(args);
+ }
+ if (args[1].startsWith("eg") && args.length == 4) {
+ return addEgress(args);
+ }
+ if (args[1].startsWith("ne") && args.length == 5) {
+ return addRoute(args);
+ }
+ }
+ System.err.println("Add command should be one of:");
+ System.err.println(" add in[gress] feedid user subnet nodepatt [ seq ]");
+ System.err.println(" add eg[ress] subid node");
+ System.err.println(" add ne[twork] fromnode tonode vianode");
+ } else if (cmd.startsWith("del")) {
+ if (args.length > 2) {
+ if (args[1].startsWith("in") && args.length == 5) {
+ return delIngress(args);
+ }
+ if (args[1].startsWith("in") && args.length == 3) {
+ return delIngress(args);
+ }
+ if (args[1].startsWith("eg") && args.length == 3) {
+ return delEgress(args);
+ }
+ if (args[1].startsWith("ne") && args.length == 4) {
+ return delRoute(args);
+ }
+ }
+ System.err.println("Delete command should be one of:");
+ System.err.println(" del in[gress] feedid user subnet");
+ System.err.println(" del in[gress] seq");
+ System.err.println(" del eg[ress] subid");
+ System.err.println(" del ne[twork] fromnode tonode");
+ } else if (cmd.startsWith("lis")) {
+ return list(args);
+ } else if (cmd.startsWith("wid") && args.length > 1) {
+ width = Integer.parseInt(args[1]);
+ return true;
+ } else if (cmd.startsWith("?") || cmd.startsWith("hel") || cmd.startsWith("usa")) {
+ usage();
+ } else if (cmd.startsWith("#")) {
+ // comment -- ignore
+ } else {
+ System.err.println("Command should be one of add, del, list, exit, quit");
+ }
+ return false;
+ }
+
+ private void usage() {
+ System.out.println("Enter one of the following commands:");
+ System.out.println(" add in[gress] feedid user subnet nodepatt [ seq ]");
+ System.out.println(" add eg[ress] subid node");
+ System.out.println(" add ne[twork] fromnode tonode vianode");
+ System.out.println(" del in[gress] feedid user subnet");
+ System.out.println(" del in[gress] seq");
+ System.out.println(" del eg[ress] subid");
+ System.out.println(" del ne[twork] fromnode tonode");
+ System.out.println(" list [ all | ingress | egress | network ]");
+ System.out.println(" exit");
+ System.out.println(" quit");
+ }
+
+ private boolean addIngress(String[] args) {
+ String url = String.format("https://%s/internal/route/ingress/?feed=%s&user=%s&subnet=%s&nodepatt=%s", server, args[2], args[3], args[4], args[5]);
+ if (args.length > 6)
+ url += "&seq=" + args[6];
+ return doPost(url);
+ }
+
+ private boolean addEgress(String[] args) {
+ String url = String.format("https://%s/internal/route/egress/?sub=%s&node=%s", server, args[2], args[3]);
+ return doPost(url);
+ }
+
+ private boolean addRoute(String[] args) {
+ String url = String.format("https://%s/internal/route/network/?from=%s&to=%s&via=%s", server, args[2], args[3], args[4]);
+ return doPost(url);
+ }
+
+ private boolean delIngress(String[] args) {
+ String url;
+ if (args.length == 5) {
+ String subnet = args[4].replaceAll("/", "!"); // replace the / with a !
+ url = String.format("https://%s/internal/route/ingress/%s/%s/%s", server, args[2], args[3], subnet);
+ } else {
+ url = String.format("https://%s/internal/route/ingress/%s", server, args[2]);
+ }
+ return doDelete(url);
+ }
+
+ private boolean delEgress(String[] args) {
+ String url = String.format("https://%s/internal/route/egress/%s", server, args[2]);
+ return doDelete(url);
+ }
+
+ private boolean delRoute(String[] args) {
+ String url = String.format("https://%s/internal/route/network/%s/%s", server, args[2], args[3]);
+ return doDelete(url);
+ }
+
+ private boolean list(String[] args) {
+ String tbl = (args.length == 1) ? "all" : args[1].toLowerCase();
+ JSONObject jo = doGet("https://"+server+"/internal/route/"); // Returns all 3 tables
+ StringBuilder sb = new StringBuilder();
+ if (tbl.startsWith("al") || tbl.startsWith("in")) {
+ // Display the IRT
+ JSONArray irt = jo.optJSONArray("ingress");
+ int cw1 = 6, cw2 = 6, cw3 = 6, cw4 = 6; // determine column widths for first 4 cols
+ for (int i = 0; irt != null && i < irt.length(); i++) {
+ JSONObject e = irt.getJSONObject(i);
+ cw1 = Math.max(cw1, (""+ e.getInt("seq")).length());
+ cw2 = Math.max(cw2, (""+e.getInt("feedid")).length());
+ String t = e.optString("user");
+ cw3 = Math.max(cw3, (t == null) ? 1 : t.length());
+ t = e.optString("subnet");
+ cw4 = Math.max(cw4, (t == null) ? 1 : t.length());
+ }
+
+ int nblank = cw1 + cw2 + cw3 + cw4 + 8;
+ sb.append("Ingress Routing Table\n");
+ sb.append(String.format("%s %s %s %s Nodes\n", ext("Seq", cw1), ext("FeedID", cw2), ext("User", cw3), ext("Subnet", cw4)));
+ for (int i = 0; irt != null && i < irt.length(); i++) {
+ JSONObject e = irt.getJSONObject(i);
+ String seq = ""+e.getInt("seq");
+ String feedid = ""+e.getInt("feedid");
+ String user = e.optString("user");
+ String subnet = e.optString("subnet");
+ if (user.equals("")) user = "-";
+ if (subnet.equals("")) subnet = "-";
+ JSONArray nodes = e.getJSONArray("node");
+ int sol = sb.length();
+ sb.append(String.format("%s %s %s %s ", ext(seq, cw1), ext(feedid, cw2), ext(user, cw3), ext(subnet, cw4)));
+ for (int j = 0; j < nodes.length(); j++) {
+ String nd = nodes.getString(j);
+ int cursor = sb.length() - sol;
+ if (j > 0 && (cursor + nd.length() > width)) {
+ sb.append("\n");
+ sol = sb.length();
+ sb.append(ext(" ", nblank));
+ }
+ sb.append(nd);
+ if ((j+1) < nodes.length()) {
+ sb.append(", ");
+ }
+ }
+ sb.append("\n");
+ }
+ }
+ if (tbl.startsWith("al") || tbl.startsWith("eg")) {
+ // Display the ERT
+ JSONObject ert = jo.optJSONObject("egress");
+ String[] subs = (ert == null) ? new String[0] : JSONObject.getNames(ert);
+ if (subs == null)
+ subs = new String[0];
+ Arrays.sort(subs);
+ int cw1 = 5;
+ for (int i = 0; i < subs.length; i++) {
+ cw1 = Math.max(cw1, subs[i].length());
+ }
+
+ if (sb.length() > 0)
+ sb.append("\n");
+ sb.append("Egress Routing Table\n");
+ sb.append(String.format("%s Node\n", ext("SubID", cw1)));
+ for (int i = 0; i < subs.length; i++) {
+ String node = ert.getString(subs[i]);
+ sb.append(String.format("%s %s\n", ext(subs[i], cw1), node));
+ }
+ }
+ if (tbl.startsWith("al") || tbl.startsWith("ne")) {
+ // Display the NRT
+ JSONArray nrt = jo.optJSONArray("routing");
+ int cw1 = 4, cw2 = 4;
+ for (int i = 0; nrt != null && i < nrt.length(); i++) {
+ JSONObject e = nrt.getJSONObject(i);
+ String from = e.getString("from");
+ String to = e.getString("to");
+ cw1 = Math.max(cw1, from.length());
+ cw2 = Math.max(cw2, to.length());
+ }
+
+ if (sb.length() > 0)
+ sb.append("\n");
+ sb.append("Network Routing Table\n");
+ sb.append(String.format("%s %s Via\n", ext("From", cw1), ext("To", cw2)));
+ for (int i = 0; nrt != null && i < nrt.length(); i++) {
+ JSONObject e = nrt.getJSONObject(i);
+ String from = e.getString("from");
+ String to = e.getString("to");
+ String via = e.getString("via");
+ sb.append(String.format("%s %s %s\n", ext(from, cw1), ext(to, cw2), via));
+ }
+ }
+ System.out.print(sb.toString());
+ return true;
+ }
+ private String ext(String s, int n) {
+ if (s == null)
+ s = "-";
+ while (s.length() < n)
+ s += " ";
+ return s;
+ }
+
+ private boolean doDelete(String url) {
+ boolean rv = false;
+ HttpDelete meth = new HttpDelete(url);
+ try {
+ HttpResponse response = httpclient.execute(meth);
+ HttpEntity entity = response.getEntity();
+ StatusLine sl = response.getStatusLine();
+ rv = (sl.getStatusCode() == HttpServletResponse.SC_OK);
+ if (rv) {
+ System.out.println("Routing entry deleted.");
+ EntityUtils.consume(entity);
+ } else {
+ printErrorText(entity);
+ }
+ } catch (Exception e) {
+ } finally {
+ meth.releaseConnection();
+ }
+ return rv;
+ }
+
+ private JSONObject doGet(String url) {
+ JSONObject rv = new JSONObject();
+ HttpGet meth = new HttpGet(url);
+ try {
+ HttpResponse response = httpclient.execute(meth);
+ HttpEntity entity = response.getEntity();
+ StatusLine sl = response.getStatusLine();
+ if (sl.getStatusCode() == HttpServletResponse.SC_OK) {
+ rv = new JSONObject(new JSONTokener(entity.getContent()));
+ } else {
+ printErrorText(entity);
+ }
+ } catch (Exception e) {
+ System.err.println(e);
+ } finally {
+ meth.releaseConnection();
+ }
+ return rv;
+ }
+
+ private boolean doPost(String url) {
+ boolean rv = false;
+ HttpPost meth = new HttpPost(url);
+ try {
+ HttpResponse response = httpclient.execute(meth);
+ HttpEntity entity = response.getEntity();
+ StatusLine sl = response.getStatusLine();
+ rv = (sl.getStatusCode() == HttpServletResponse.SC_OK);
+ if (rv) {
+ System.out.println("Routing entry added.");
+ EntityUtils.consume(entity);
+ } else {
+ printErrorText(entity);
+ }
+ } catch (Exception e) {
+ } finally {
+ meth.releaseConnection();
+ }
+ return rv;
+ }
+
+ private void printErrorText(HttpEntity entity) throws IllegalStateException, IOException {
+ // Look for and print only the part of the output between <pre>...</pre>
+ InputStream is = entity.getContent();
+ StringBuilder sb = new StringBuilder();
+ byte[] b = new byte[512];
+ int n = 0;
+ while ((n = is.read(b)) > 0) {
+ sb.append(new String(b, 0, n));
+ }
+ is.close();
+ int ix = sb.indexOf("<pre>");
+ if (ix > 0)
+ sb.delete(0, ix+5);
+ ix = sb.indexOf("</pre>");
+ if (ix > 0)
+ sb.delete(ix, sb.length());
+ System.err.println(sb.toString());
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/JSONUtilities.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/JSONUtilities.java
new file mode 100644
index 00000000..e1676588
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/JSONUtilities.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.utils;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Collection;
+
+/**
+ * Some utility functions used when creating/validating JSON.
+ *
+ * @author Robert Eby
+ * @version $Id: JSONUtilities.java,v 1.1 2013/04/26 21:00:26 eby Exp $
+ */
+public class JSONUtilities {
+ /**
+ * Does the String <i>v</i> represent a valid Internet address (with or without a
+ * mask length appended).
+ * @param v the string to check
+ * @return true if valid, false otherwise
+ */
+ public static boolean validIPAddrOrSubnet(String v) {
+ String[] pp = { v, "" };
+ if (v.indexOf('/') > 0)
+ pp = v.split("/");
+ try {
+ InetAddress addr = InetAddress.getByName(pp[0]);
+ if (pp[1].length() > 0) {
+ // check subnet mask
+ int mask = Integer.parseInt(pp[1]);
+ if (mask > (addr.getAddress().length * 8))
+ return false;
+ }
+ return true;
+ } catch (UnknownHostException e) {
+ return false;
+ }
+ }
+ /**
+ * Build a JSON array from a collection of Strings.
+ * @param coll the collection
+ * @return a String containing a JSON array
+ */
+ public static String createJSONArray(Collection<String> coll) {
+ StringBuilder sb = new StringBuilder("[");
+ String pfx = "\n";
+ for (String t : coll) {
+ sb.append(pfx).append(" \"").append(t).append("\"");
+ pfx = ",\n";
+ }
+ sb.append("\n]\n");
+ return sb.toString();
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/LogfileLoader.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/LogfileLoader.java
new file mode 100644
index 00000000..f9c11f18
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/LogfileLoader.java
@@ -0,0 +1,549 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.utils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.io.Reader;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.text.ParseException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeSet;
+import java.util.zip.GZIPInputStream;
+
+import org.apache.log4j.Logger;
+
+import com.att.research.datarouter.provisioning.BaseServlet;
+import com.att.research.datarouter.provisioning.beans.DeliveryExtraRecord;
+import com.att.research.datarouter.provisioning.beans.DeliveryRecord;
+import com.att.research.datarouter.provisioning.beans.ExpiryRecord;
+import com.att.research.datarouter.provisioning.beans.Loadable;
+import com.att.research.datarouter.provisioning.beans.LogRecord;
+import com.att.research.datarouter.provisioning.beans.Parameters;
+import com.att.research.datarouter.provisioning.beans.PubFailRecord;
+import com.att.research.datarouter.provisioning.beans.PublishRecord;
+
+/**
+ * This class provides methods that run in a separate thread, in order to process logfiles uploaded into the spooldir.
+ * These logfiles are loaded into the MySQL LOG_RECORDS table. In a running provisioning server, there should only be
+ * two places where records can be loaded into this table; here, and in the method DB.retroFit4() which may be run at
+ * startup to load the old (1.0) style log tables into LOG_RECORDS;
+ * <p>This method maintains an {@link RLEBitSet} which can be used to easily see what records are presently in the
+ * database.
+ * This bit set is used to synchronize between provisioning servers.</p>
+ *
+ * @author Robert Eby
+ * @version $Id: LogfileLoader.java,v 1.22 2014/03/12 19:45:41 eby Exp $
+ */
+public class LogfileLoader extends Thread {
+ /** Default number of log records to keep when pruning. Keep 10M by default. */
+ public static final long DEFAULT_LOG_RETENTION = 10000000L;
+ /** NOT USED: Percentage of free space required before old records are removed. */
+ public static final int REQUIRED_FREE_PCT = 20;
+
+ /** This is a singleton -- there is only one LogfileLoader object in the server */
+ private static LogfileLoader p;
+
+ /**
+ * Get the singleton LogfileLoader object, and start it if it is not running.
+ * @return the LogfileLoader
+ */
+ public static synchronized LogfileLoader getLoader() {
+ if (p == null)
+ p = new LogfileLoader();
+ if (!p.isAlive())
+ p.start();
+ return p;
+ }
+
+ /** The PreparedStatement which is loaded by a <i>Loadable</i>. */
+ public static final String INSERT_SQL = "insert into LOG_RECORDS values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+ /** Each server can assign this many IDs */
+ private static final long SET_SIZE = (1L << 56);
+
+ private final Logger logger;
+ private final DB db;
+ private final String spooldir;
+ private final long set_start;
+ private final long set_end;
+ private RLEBitSet seq_set;
+ private long nextid;
+ private boolean idle;
+
+ private LogfileLoader() {
+ this.logger = Logger.getLogger("com.att.research.datarouter.provisioning.internal");
+ this.db = new DB();
+ this.spooldir = db.getProperties().getProperty("com.att.research.datarouter.provserver.spooldir");
+ this.set_start = getIdRange();
+ this.set_end = set_start + SET_SIZE - 1;
+ this.seq_set = new RLEBitSet();
+ this.nextid = 0;
+ this.idle = false;
+
+ // This is a potentially lengthy operation, so has been moved to run()
+ //initializeNextid();
+ this.setDaemon(true);
+ this.setName("LogfileLoader");
+ }
+
+ private long getIdRange() {
+ long n;
+ if (BaseServlet.isInitialActivePOD())
+ n = 0;
+ else if (BaseServlet.isInitialStandbyPOD())
+ n = SET_SIZE;
+ else
+ n = SET_SIZE * 2;
+ String r = String.format("[%X .. %X]", n, n+SET_SIZE-1);
+ logger.debug("This server shall assign RECORD_IDs in the range "+r);
+ return n;
+ }
+ /**
+ * Return the bit set representing the record ID's that are loaded in this database.
+ * @return the bit set
+ */
+ public RLEBitSet getBitSet() {
+ return seq_set;
+ }
+ /**
+ * True if the LogfileLoader is currently waiting for work.
+ * @return true if idle
+ */
+ public boolean isIdle() {
+ return idle;
+ }
+ /**
+ * Run continuously to look for new logfiles in the spool directory and import them into the DB.
+ * The spool is checked once per second. If free space on the MySQL filesystem falls below
+ * REQUIRED_FREE_PCT (normally 20%) then the oldest logfile entries are removed and the LOG_RECORDS
+ * table is compacted until free space rises above the threshold.
+ */
+ @Override
+ public void run() {
+ initializeNextid(); // moved from the constructor
+ while (true) {
+ try {
+ File dirfile = new File(spooldir);
+ while (true) {
+ // process IN files
+ File[] infiles = dirfile.listFiles(new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String name) {
+ return name.startsWith("IN.");
+ }
+ });
+
+ if (infiles.length == 0) {
+ idle = true;
+ try {
+ Thread.sleep(1000L);
+ } catch (InterruptedException e) {
+ }
+ idle = false;
+ } else {
+ // Remove old rows
+ if (pruneRecords()) {
+ // Removed at least some entries, recompute the bit map
+ initializeNextid();
+ }
+
+ // Process incoming logfiles
+ for (File f : infiles) {
+ if (logger.isDebugEnabled())
+ logger.debug("PROV8001 Starting " + f + " ...");
+ long time = System.currentTimeMillis();
+ int[] n = process(f);
+ time = System.currentTimeMillis() - time;
+ logger.info(String
+ .format("PROV8000 Processed %s in %d ms; %d of %d records.",
+ f.toString(), time, n[0], n[1]));
+ f.delete();
+ }
+ }
+ }
+ } catch (Exception e) {
+ logger.warn("PROV0020: Caught exception in LogfileLoader: " + e);
+ e.printStackTrace();
+ }
+ }
+ }
+ private boolean pruneRecords() {
+ boolean did1 = false;
+ long count = countRecords();
+ long threshold = DEFAULT_LOG_RETENTION;
+ Parameters param = Parameters.getParameter(Parameters.PROV_LOG_RETENTION);
+ if (param != null) {
+ try {
+ long n = Long.parseLong(param.getValue());
+ // This check is to prevent inadvertent errors from wiping the table out
+ if (n > 1000000L)
+ threshold = n;
+ } catch (NumberFormatException e) {
+ // ignore
+ }
+ }
+ logger.debug("Pruning LOG_RECORD table: records in DB="+count+", threshold="+threshold);
+ if (count > threshold) {
+ count -= threshold; // we need to remove this many records;
+ Map<Long,Long> hist = getHistogram(); // histogram of records per day
+ // Determine the cutoff point to remove the needed number of records
+ long sum = 0;
+ long cutoff = 0;
+ for (Long day : new TreeSet<Long>(hist.keySet())) {
+ sum += hist.get(day);
+ cutoff = day;
+ if (sum >= count)
+ break;
+ }
+ cutoff++;
+ cutoff *= 86400000L; // convert day to ms
+ logger.debug(" Pruning records older than="+(cutoff/86400000L)+" ("+new Date(cutoff)+")");
+
+ Connection conn = null;
+ try {
+ // Limit to a million at a time to avoid typing up the DB for too long.
+ conn = db.getConnection();
+ PreparedStatement ps = conn.prepareStatement("DELETE from LOG_RECORDS where EVENT_TIME < ? limit 1000000");
+ ps.setLong(1, cutoff);
+ while (count > 0) {
+ if (!ps.execute()) {
+ int dcount = ps.getUpdateCount();
+ count -= dcount;
+ logger.debug(" "+dcount+" rows deleted.");
+ did1 |= (dcount!=0);
+ if (dcount == 0)
+ count = 0; // prevent inf. loops
+ } else {
+ count = 0; // shouldn't happen!
+ }
+ }
+ ps.close();
+ Statement stmt = conn.createStatement();
+ stmt.execute("OPTIMIZE TABLE LOG_RECORDS");
+ stmt.close();
+ } catch (SQLException e) {
+ System.err.println(e);
+ e.printStackTrace();
+ } finally {
+ db.release(conn);
+ }
+ }
+ return did1;
+ }
+ private long countRecords() {
+ long count = 0;
+ Connection conn = null;
+ try {
+ conn = db.getConnection();
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery("SELECT COUNT(*) as COUNT from LOG_RECORDS");
+ if (rs.next()) {
+ count = rs.getLong("COUNT");
+ }
+ rs.close();
+ stmt.close();
+ } catch (SQLException e) {
+ System.err.println(e);
+ e.printStackTrace();
+ } finally {
+ db.release(conn);
+ }
+ return count;
+ }
+ private Map<Long,Long> getHistogram() {
+ Map<Long,Long> map = new HashMap<Long,Long>();
+ Connection conn = null;
+ try {
+ logger.debug(" LOG_RECORD table histogram...");
+ conn = db.getConnection();
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery("SELECT FLOOR(EVENT_TIME/86400000) AS DAY, COUNT(*) AS COUNT FROM LOG_RECORDS GROUP BY DAY");
+ while (rs.next()) {
+ long day = rs.getLong("DAY");
+ long cnt = rs.getLong("COUNT");
+ map.put(day, cnt);
+ logger.debug(" "+day + " "+cnt);
+ }
+ rs.close();
+ stmt.close();
+ } catch (SQLException e) {
+ System.err.println(e);
+ e.printStackTrace();
+ } finally {
+ db.release(conn);
+ }
+ return map;
+ }
+ private void initializeNextid() {
+ Connection conn = null;
+ try {
+ conn = db.getConnection();
+ Statement stmt = conn.createStatement();
+ // Build a bitset of all records in the LOG_RECORDS table
+ // We need to run this SELECT in stages, because otherwise we run out of memory!
+ RLEBitSet nbs = new RLEBitSet();
+ final long stepsize = 6000000L;
+ boolean go_again = true;
+ for (long i = 0; go_again; i += stepsize) {
+ String sql = String.format("select RECORD_ID from LOG_RECORDS LIMIT %d,%d", i, stepsize);
+ ResultSet rs = stmt.executeQuery(sql);
+ go_again = false;
+ while (rs.next()) {
+ long n = rs.getLong("RECORD_ID");
+ nbs.set(n);
+ go_again = true;
+ }
+ rs.close();
+ }
+ stmt.close();
+ seq_set = nbs;
+
+ // Compare with the range for this server
+ // Determine the next ID for this set of record IDs
+ RLEBitSet tbs = (RLEBitSet) nbs.clone();
+ RLEBitSet idset = new RLEBitSet();
+ idset.set(set_start, set_start+SET_SIZE);
+ tbs.and(idset);
+ long t = tbs.length();
+ nextid = (t == 0) ? set_start : (t - 1);
+ if (nextid >= set_start+SET_SIZE) {
+ // Handle wraparound, when the IDs reach the end of our "range"
+ Long[] last = null;
+ Iterator<Long[]> li = tbs.getRangeIterator();
+ while (li.hasNext()) {
+ last = li.next();
+ }
+ if (last != null) {
+ tbs.clear(last[0], last[1]+1);
+ t = tbs.length();
+ nextid = (t == 0) ? set_start : (t - 1);
+ }
+ }
+ logger.debug(String.format("initializeNextid, next ID is %d (%x)", nextid, nextid));
+ } catch (SQLException e) {
+ System.err.println(e);
+ e.printStackTrace();
+ } finally {
+ db.release(conn);
+ }
+ }
+// OLD CODE - commented here for historical purposes
+//
+// private boolean pruneRecordsOldAlgorithm() {
+// // Determine space available -- available space must be at least 20% under /opt/app/mysql
+// int pct = getFreePercentage();
+// boolean did1 = false;
+// while (pct < REQUIRED_FREE_PCT) {
+// logger.info("PROV8008: Free space is " + pct + "% - removing old log entries");
+// boolean didit = removeOldestEntries();
+// pct = didit ? getFreePercentage() : 100; // don't loop endlessly
+// did1 |= didit;
+// }
+// return did1;
+// }
+// private int getFreePercentage() {
+// FileSystem fs = (Paths.get("/opt/app/mysql")).getFileSystem();
+// long total = 0;
+// long avail = 0;
+// try {
+// for (FileStore store : fs.getFileStores()) {
+// total += store.getTotalSpace();
+// avail += store.getUsableSpace();
+// }
+// } catch (IOException e) {
+// }
+// try { fs.close(); } catch (Exception e) { }
+// return (int)((avail * 100) / total);
+// }
+// private boolean removeOldestEntries() {
+// // Remove the last days worth of entries
+// Connection conn = null;
+// try {
+// conn = db.getConnection();
+// Statement stmt = conn.createStatement();
+// ResultSet rs = stmt.executeQuery("select min(event_time) as MIN from LOG_RECORDS");
+// if (rs != null) {
+// if (rs.next()) {
+// // Compute the end of the first day of logs
+// long first = rs.getLong("MIN");
+// Calendar cal = new GregorianCalendar();
+// cal.setTime(new Date(first));
+// cal.add(Calendar.DAY_OF_YEAR, 1);
+// cal.set(Calendar.HOUR_OF_DAY, 0);
+// cal.set(Calendar.MINUTE, 0);
+// cal.set(Calendar.SECOND, 0);
+// cal.set(Calendar.MILLISECOND, 0);
+// if (!stmt.execute("delete from LOG_RECORDS where event_time < " + cal.getTimeInMillis())) {
+// int count = stmt.getUpdateCount();
+// logger.info("PROV0009: Removed "+count+" old log entries.");
+// stmt.execute("OPTIMIZE TABLE LOG_RECORDS");
+// }
+// rs.close();
+// stmt.close();
+// return true;
+// }
+// rs.close();
+// }
+// stmt.close();
+// } catch (SQLException e) {
+// System.err.println(e);
+// e.printStackTrace();
+// } finally {
+// db.release(conn);
+// }
+// return false;
+// }
+ @SuppressWarnings("resource")
+ private int[] process(File f) {
+ int ok = 0, total = 0;
+ try {
+ Connection conn = db.getConnection();
+ PreparedStatement ps = conn.prepareStatement(INSERT_SQL);
+ Reader r = f.getPath().endsWith(".gz")
+ ? new InputStreamReader(new GZIPInputStream(new FileInputStream(f)))
+ : new FileReader(f);
+ LineNumberReader in = new LineNumberReader(r);
+ String line;
+ while ((line = in.readLine()) != null) {
+ try {
+ for (Loadable rec : buildRecords(line)) {
+ rec.load(ps);
+ if (rec instanceof LogRecord) {
+ LogRecord lr = ((LogRecord)rec);
+ if (!seq_set.get(lr.getRecordId())) {
+ ps.executeUpdate();
+ seq_set.set(lr.getRecordId());
+ } else
+ logger.debug("Duplicate record ignored: "+lr.getRecordId());
+ } else {
+ if (++nextid > set_end)
+ nextid = set_start;
+ ps.setLong(18, nextid);
+ ps.executeUpdate();
+ seq_set.set(nextid);
+ }
+ ps.clearParameters();
+ ok++;
+ }
+ } catch (SQLException e) {
+ logger.warn("PROV8003 Invalid value in record: "+line);
+ logger.debug(e);
+ e.printStackTrace();
+ } catch (NumberFormatException e) {
+ logger.warn("PROV8004 Invalid number in record: "+line);
+ logger.debug(e);
+ e.printStackTrace();
+ } catch (ParseException e) {
+ logger.warn("PROV8005 Invalid date in record: "+line);
+ logger.debug(e);
+ e.printStackTrace();
+ } catch (Exception e) {
+ logger.warn("PROV8006 Invalid pattern in record: "+line);
+ logger.debug(e);
+ e.printStackTrace();
+ }
+ total++;
+ }
+ in.close();
+ ps.close();
+ db.release(conn);
+ conn = null;
+ } catch (FileNotFoundException e) {
+ logger.warn("PROV8007 Exception reading "+f+": "+e);
+ } catch (IOException e) {
+ logger.warn("PROV8007 Exception reading "+f+": "+e);
+ } catch (SQLException e) {
+ logger.warn("PROV8007 Exception reading "+f+": "+e);
+ }
+ return new int[] { ok, total };
+ }
+ private Loadable[] buildRecords(String line) throws ParseException {
+ String[] pp = line.split("\\|");
+ if (pp != null && pp.length >= 7) {
+ String rtype = pp[1].toUpperCase();
+ if (rtype.equals("PUB") && pp.length == 11) {
+ // Fields are: date|PUB|pubid|feedid|requrl|method|ctype|clen|srcip|user|status
+ return new Loadable[] { new PublishRecord(pp) };
+ }
+ if (rtype.equals("DEL") && pp.length == 12) {
+ // Fields are: date|DEL|pubid|feedid|subid|requrl|method|ctype|clen|user|status|xpubid
+ String[] subs = pp[4].split("\\s+");
+ if (subs != null) {
+ Loadable[] rv = new Loadable[subs.length];
+ for (int i = 0; i < subs.length; i++) {
+ // create a new record for each individual sub
+ pp[4] = subs[i];
+ rv[i] = new DeliveryRecord(pp);
+ }
+ return rv;
+ }
+ }
+ if (rtype.equals("EXP") && pp.length == 11) {
+ // Fields are: date|EXP|pubid|feedid|subid|requrl|method|ctype|clen|reason|attempts
+ ExpiryRecord e = new ExpiryRecord(pp);
+ if (e.getReason().equals("other"))
+ logger.info("Invalid reason '"+pp[9]+"' changed to 'other' for record: "+e.getPublishId());
+ return new Loadable[] { e };
+ }
+ if (rtype.equals("PBF") && pp.length == 12) {
+ // Fields are: date|PBF|pubid|feedid|requrl|method|ctype|clen-expected|clen-received|srcip|user|error
+ return new Loadable[] { new PubFailRecord(pp) };
+ }
+ if (rtype.equals("DLX") && pp.length == 7) {
+ // Fields are: date|DLX|pubid|feedid|subid|clen-tosend|clen-sent
+ return new Loadable[] { new DeliveryExtraRecord(pp) };
+ }
+ if (rtype.equals("LOG") && (pp.length == 19 || pp.length == 20)) {
+ // Fields are: date|LOG|pubid|feedid|requrl|method|ctype|clen|type|feedFileid|remoteAddr|user|status|subid|fileid|result|attempts|reason|record_id
+ return new Loadable[] { new LogRecord(pp) };
+ }
+ }
+ logger.warn("PROV8002 bad record: "+line);
+ return new Loadable[0];
+ }
+
+ /**
+ * The LogfileLoader can be run stand-alone by invoking the main() method of this class.
+ * @param a ignored
+ * @throws InterruptedException
+ */
+ public static void main(String[] a) throws InterruptedException {
+ LogfileLoader.getLoader();
+ Thread.sleep(200000L);
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/PurgeLogDirTask.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/PurgeLogDirTask.java
new file mode 100644
index 00000000..b705e6f8
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/PurgeLogDirTask.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.utils;
+
+import java.io.File;
+import java.util.Properties;
+import java.util.TimerTask;
+
+/**
+ * This class provides a {@link TimerTask} that purges old logfiles
+ * (older than the number of days specified by the com.att.research.datarouter.provserver.logretention property).
+ * @author Robert Eby
+ * @version $Id: PurgeLogDirTask.java,v 1.2 2013/07/05 13:48:05 eby Exp $
+ */
+public class PurgeLogDirTask extends TimerTask {
+ private static final long ONEDAY = 86400000L;
+
+ private final String logdir;
+ private final long interval;
+
+ public PurgeLogDirTask() {
+ Properties p = (new DB()).getProperties();
+ logdir = p.getProperty("com.att.research.datarouter.provserver.accesslog.dir");
+ String s = p.getProperty("com.att.research.datarouter.provserver.logretention", "30");
+ long n = 30;
+ try {
+ n = Long.parseLong(s);
+ } catch (NumberFormatException e) {
+ // ignore
+ }
+ interval = n * ONEDAY;
+ }
+ @Override
+ public void run() {
+ try {
+ File dir = new File(logdir);
+ if (dir.exists()) {
+ long exptime = System.currentTimeMillis() - interval;
+ for (File logfile : dir.listFiles()) {
+ if (logfile.lastModified() < exptime)
+ logfile.delete();
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/RLEBitSet.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/RLEBitSet.java
new file mode 100644
index 00000000..58617414
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/RLEBitSet.java
@@ -0,0 +1,418 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.utils;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * This class provides operations similar to the standard Java {@link java.util.BitSet} class.
+ * It is designed for bit sets where there are long runs of 1s and 0s; it is not appropriate
+ * for sparsely populated bits sets. In addition, this class uses <code>long</code>s rather
+ * than <code>int</code>s to represent the indices of the bits.
+ *
+ * @author Robert Eby
+ * @version $Id$
+ */
+public class RLEBitSet {
+ /**
+ * Used to represent a continues set of <i>nbits</i> 1 bits starting at <i>start</i>.
+ */
+ private class RLE implements Comparable<RLE> {
+ private final long start;
+ private long nbits;
+ public RLE(long from, long nbits) {
+ this.start = from;
+ this.nbits = (nbits > 0) ? nbits : 0;
+ }
+ /**
+ * Returns the index of the first set bit in this RLE.
+ * @return the index
+ */
+ public long firstBit() {
+ return start;
+ }
+ /**
+ * Returns the index of the last set bit in this RLE.
+ * @return the index
+ */
+ public long lastBit() {
+ return start+nbits-1;
+ }
+ public boolean intersects(RLE b2) {
+ if (b2.lastBit() < this.firstBit())
+ return false;
+ if (b2.firstBit() > this.lastBit())
+ return false;
+ return true;
+ }
+ public boolean isSubset(RLE b2) {
+ if (firstBit() < b2.firstBit())
+ return false;
+ if (firstBit() > b2.lastBit())
+ return false;
+ if (lastBit() < b2.firstBit())
+ return false;
+ if (lastBit() > b2.lastBit())
+ return false;
+ return true;
+ }
+ public RLE union(RLE b2) {
+ RLE b1 = this;
+ if (b1.firstBit() > b2.firstBit()) {
+ b1 = b2;
+ b2 = this;
+ }
+ long end = b1.lastBit();
+ if (b2.lastBit() > b1.lastBit())
+ end = b2.lastBit();
+ return new RLE(b1.firstBit(), end-b1.firstBit()+1);
+ }
+ /**
+ * Returns the number of bits set to {@code true} in this {@code RLE}.
+ * @return the number of bits set to {@code true} in this {@code RLE}.
+ */
+ public int cardinality() {
+ return (int) nbits;
+ }
+ @Override
+ public int compareTo(RLE o) {
+ if (this.equals(o))
+ return 0;
+ return (start < o.start) ? -1 : 1;
+ }
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof RLE) {
+ RLE b = (RLE) obj;
+ return (start == b.start) && (nbits == b.nbits);
+ }
+ return false;
+ }
+ @Override
+ public int hashCode() {
+ return new Long(start ^ nbits).hashCode();
+ }
+ @Override
+ public String toString() {
+ return "["+firstBit()+".."+lastBit()+"]";
+ }
+ }
+ private SortedSet<RLE> bitsets;
+
+ /**
+ * Creates a new bit set. All bits are initially <code>false</code>.
+ */
+ public RLEBitSet() {
+ bitsets = new TreeSet<RLE>();
+ }
+ /**
+ * Creates a new bit set, with bits set according to the value of <code>s</code>.
+ * @param s the initialization String
+ */
+ public RLEBitSet(String s) {
+ bitsets = new TreeSet<RLE>();
+ set(s);
+ }
+ /**
+ * Returns the "logical size" of this {@code RLEBitSet}: the index of the highest set bit
+ * in the {@code RLEBitSet} plus one. Returns zero if the {@code RLEBitSet} contains no set bits.
+ * @return the logical size of this {@code RLEBitSet}
+ */
+ public long length() {
+ if (isEmpty())
+ return 0;
+ return bitsets.last().lastBit()+1;
+ }
+ /**
+ * Returns the value of the bit with the specified index. The value is {@code true} if the bit
+ * with the index bit is currently set in this BitSet; otherwise, the result is {@code false}.
+ * @param bit the bit index
+ * @return the value of the bit with the specified index
+ */
+ public boolean get(long bit) {
+ synchronized (bitsets) {
+ for (RLE bs : bitsets) {
+ if (bit >= bs.firstBit() && bit <= bs.lastBit())
+ return true;
+ }
+ }
+ return false;
+ }
+ /**
+ * Set one or more bits to true, based on the value of <code>s</code>.
+ * @param s the initialization String, which consists of a comma or space separated list of
+ * non-negative numbers and ranges. An individual number represents the bit index to set.
+ * A range (two numbers separated by a dash) causes all bit indexes between the two numbers
+ * (inclusive) to be set.
+ * @exception NumberFormatException - if a number is incorrectly formatted
+ * @exception IndexOutOfBoundsException - if an index is negative
+ */
+ public void set(String s) throws NumberFormatException {
+ s = s.trim();
+ if (!s.isEmpty()) {
+ for (String s2 : s.split("[, \n]+")) {
+ if (s2.indexOf('-') >= 0) {
+ String[] pp = s2.split("-");
+ long f = Long.parseLong(pp[0]);
+ long t = Long.parseLong(pp[1]);
+ set(f, t+1);
+ } else
+ set(Long.parseLong(s2));
+ }
+ }
+ }
+ /**
+ * Sets the bit at the specified index to {@code true}.
+ * @param bit a bit index
+ */
+ public void set(long bit) {
+ set(bit, bit+1);
+ }
+ /**
+ * Sets the bits from the specified {@code from} (inclusive) to the
+ * specified {@code to} (exclusive) to {@code true}.
+ * @param from index of the first bit to be set
+ * @param to index after the last bit to be set
+ * @throws IndexOutOfBoundsException if {@code from} is negative,
+ * or {@code to} is negative,
+ * or {@code from} is larger than {@code to}
+ */
+ public void set(long from, long to) {
+ checkRange(from, to);
+ RLE newbits = new RLE(from, to-from);
+ synchronized (bitsets) {
+ for (RLE bs : bitsets) {
+ if (bs.intersects(newbits)) {
+ if (!newbits.isSubset(bs)) {
+ bitsets.remove(bs);
+ bitsets.add(newbits.union(bs));
+ coalesce();
+ }
+ return;
+ }
+ }
+ bitsets.add(newbits);
+ }
+ coalesce();
+ }
+ /**
+ * Sets all of the bits in this BitSet to {@code false}.
+ */
+ public void clear() {
+ synchronized (bitsets) {
+ bitsets.clear();
+ }
+ }
+ /**
+ * Sets the bit specified by the index to {@code false}.
+ * @param bit the index of the bit to be cleared
+ */
+ public void clear(long bit) {
+ clear(bit, bit+1);
+ }
+ /**
+ * Sets the bits from the specified {@code from} (inclusive) to the
+ * specified {@code to} (exclusive) to {@code false}.
+ * @param from index of the first bit to be cleared
+ * @param to index after the last bit to be cleared
+ * @throws IndexOutOfBoundsException if {@code from} is negative,
+ * or {@code to} is negative,
+ * or {@code from} is larger than {@code to}
+ */
+ public void clear(long from, long to) {
+ checkRange(from, to);
+ RLE newbits = new RLE(from, to-from);
+ List<RLE> newranges = new ArrayList<RLE>();
+ synchronized (bitsets) {
+ for (RLE bs : bitsets) {
+ if (bs.intersects(newbits)) {
+ // preserve the bits that are not being cleared
+ long len = newbits.firstBit() - bs.firstBit();
+ if (len > 0)
+ newranges.add(new RLE(bs.firstBit(), len));
+ len = bs.lastBit() - newbits.lastBit();
+ if (len > 0)
+ newranges.add(new RLE(newbits.lastBit()+1, len));
+ bs.nbits = 0;
+ }
+ }
+ if (!newranges.isEmpty()) {
+ for (RLE bs : newranges) {
+ bitsets.add(bs);
+ }
+ }
+ }
+ coalesce();
+ }
+ /** Combine abutting RLEBitSets, and remove 0 length RLEBitSets. */
+ private void coalesce() {
+ RLE last = null;
+ synchronized (bitsets) {
+ Iterator<RLE> iter = bitsets.iterator();
+ while (iter.hasNext()) {
+ RLE bs = iter.next();
+ if (last != null && (last.lastBit()+1 == bs.firstBit())) {
+ last.nbits += bs.nbits;
+ iter.remove();
+ } else if (bs.nbits == 0) {
+ iter.remove();
+ } else {
+ last = bs;
+ }
+ }
+ }
+ }
+ /**
+ * Checks that fromIndex ... toIndex is a valid range of bit indices.
+ */
+ private static void checkRange(long from, long to) {
+ if (from < 0)
+ throw new IndexOutOfBoundsException("fromIndex < 0: " + from);
+ if (to < 0)
+ throw new IndexOutOfBoundsException("toIndex < 0: " + to);
+ if (from > to)
+ throw new IndexOutOfBoundsException("fromIndex: " + from + " > toIndex: " + to);
+ }
+ /**
+ * Performs a logical <b>AND</b> of this target bit set with the argument bit set.
+ * This bit set is modified so that each bit in it has the value {@code true} if and only if
+ * it both initially had the value {@code true} and the corresponding bit in the bit set
+ * argument also had the value {@code true}.
+ * @param set a {@code RLEBitSet}
+ */
+ public void and(RLEBitSet set) {
+ long last = 0;
+ synchronized (set.bitsets) {
+ for (RLE bs : set.bitsets) {
+ clear(last, bs.start);
+ last = bs.start + bs.nbits;
+ }
+ }
+ clear(last, Long.MAX_VALUE);
+ }
+ /**
+ * Clears all of the bits in this {@code RLEBitSet} whose corresponding bit is set in
+ * the specified {@code RLEBitSet}.
+ * @param set the {@code RLEBitSet} with which to mask this {@code RLEBitSet}
+ */
+ public void andNot(RLEBitSet set) {
+ synchronized (set.bitsets) {
+ for (RLE bs : set.bitsets) {
+ clear(bs.start, bs.start + bs.nbits);
+ }
+ }
+ }
+ /**
+ * Returns true if this {@code RLEBitSet} contains no bits that are set
+ * to {@code true}.
+ *
+ * @return boolean indicating whether this {@code BitSet} is empty
+ */
+ public boolean isEmpty() {
+ return bitsets.isEmpty();
+ }
+ /**
+ * Returns the number of bits set to {@code true} in this {@code RLEBitSet}.
+ * @return the number of bits set to {@code true} in this {@code RLEBitSet}.
+ */
+ public int cardinality() {
+ int n = 0;
+ synchronized (bitsets) {
+ for (RLE bs : bitsets) {
+ n += bs.cardinality();
+ }
+ }
+ return n;
+ }
+ /**
+ * Cloning this RLEBitSet produces a new RLEBitSet that is equal to it. The clone of the
+ * bit set is another bit set that has exactly the same bits set to true as this bit set.
+ * @return a clone of this bit set
+ */
+ public Object clone() {
+ RLEBitSet rv = new RLEBitSet();
+ synchronized (bitsets) {
+ for (RLE bs : bitsets) {
+ rv.bitsets.add(new RLE(bs.start, bs.nbits));
+ }
+ }
+ return rv;
+ }
+ /**
+ * Returns a string representation of this bit set, using the same notation as is required for
+ * the String constructor. For every index for which this {@code RLEBitSet} contains a bit in
+ * the set state, the decimal representation of that index is included in the result. Such
+ * indices are listed in order from lowest to highest, separated by ",". Ranges of set bits are
+ * indicated by <i>lobit</i>-<i>hibit</i>.
+ * @return the String
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ String prefix = "";
+ synchronized (bitsets) {
+ for (RLE bs : bitsets) {
+ sb.append(prefix);
+ prefix = ",";
+ long s = bs.firstBit();
+ long e = bs.lastBit();
+ sb.append(s);
+ if (s != e)
+ sb.append('-').append(e);
+ }
+ }
+ return sb.toString();
+ }
+ /**
+ * Return an Iterator which provides pairs of {@code Long}s representing the beginning and
+ * ending index of a range of set bits in this {@code RLEBitSet}.
+ * @return the Iterator
+ */
+ public Iterator<Long[]> getRangeIterator() {
+ return new Iterator<Long[]>() {
+ private Iterator<RLE> i = bitsets.iterator();
+
+ @Override
+ public boolean hasNext() {
+ return i.hasNext();
+ }
+
+ @Override
+ public Long[] next() {
+ RLE bs = i.next();
+ return new Long[] { bs.firstBit(), bs.lastBit() };
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/ThrottleFilter.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/ThrottleFilter.java
new file mode 100644
index 00000000..6eb866c8
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/ThrottleFilter.java
@@ -0,0 +1,316 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.Vector;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.att.research.datarouter.provisioning.beans.Parameters;
+
+import org.apache.log4j.Logger;
+import org.eclipse.jetty.continuation.Continuation;
+import org.eclipse.jetty.continuation.ContinuationSupport;
+import org.eclipse.jetty.server.AbstractHttpConnection;
+import org.eclipse.jetty.server.Request;
+
+/**
+ * This filter checks /publish requests to the provisioning server to allow ill-behaved publishers to be throttled.
+ * It is configured via the provisioning parameter THROTTLE_FILTER.
+ * The THROTTLE_FILTER provisioning parameter can have these values:
+ * <table>
+ * <tr><td>(no value)</td><td>filter disabled</td></tr>
+ * <tr><td>off</td><td>filter disabled</td></tr>
+ * <tr><td>N[,M[,action]]</td><td>set N, M, and action (used in the algorithm below).
+ * Action is <i>drop</i> or <i>throttle</i>.
+ * If M is missing, it defaults to 5 minutes.
+ * If the action is missing, it defaults to <i>drop</i>.
+ * </td></tr>
+ * </table>
+ * <p>
+ * The <i>action</i> is triggered iff:
+ * <ol>
+ * <li>the filter is enabled, and</li>
+ * <li>N /publish requests come to the provisioning server in M minutes
+ * <ol>
+ * <li>from the same IP address</li>
+ * <li>for the same feed</li>
+ * <li>lacking the <i>Expect: 100-continue</i> header</li>
+ * </ol>
+ * </li>
+ * </ol>
+ * The action that can be performed (if triggered) are:
+ * <ol>
+ * <li><i>drop</i> - the connection is dropped immediately.</li>
+ * <li><i>throttle</i> - [not supported] the connection is put into a low priority queue with all other throttled connections.
+ * These are then processed at a slower rate. Note: this option does not work correctly, and is disabled.
+ * The only action that is supported is <i>drop</i>.
+ * </li>
+ * </ol>
+ *
+ * @author Robert Eby
+ * @version $Id: ThrottleFilter.java,v 1.2 2014/03/12 19:45:41 eby Exp $
+ */
+public class ThrottleFilter extends TimerTask implements Filter {
+ public static final int DEFAULT_N = 10;
+ public static final int DEFAULT_M = 5;
+ public static final String THROTTLE_MARKER = "com.att.research.datarouter.provisioning.THROTTLE_MARKER";
+ private static final String JETTY_REQUEST = "org.eclipse.jetty.server.Request";
+ private static final long ONE_MINUTE = 60000L;
+ private static final int ACTION_DROP = 0;
+ private static final int ACTION_THROTTLE = 1;
+
+ // Configuration
+ private static boolean enabled = false; // enabled or not
+ private static int n_requests = 0; // number of requests in M minutes
+ private static int m_minutes = 0; // sampling period
+ private static int action = ACTION_DROP; // action to take (throttle or drop)
+
+ private static Logger logger = Logger.getLogger("com.att.research.datarouter.provisioning.internal");
+ private static Map<String, Counter> map = new HashMap<String, Counter>();
+ private static final Timer rolex = new Timer();
+
+ @Override
+ public void init(FilterConfig arg0) throws ServletException {
+ configure();
+ rolex.scheduleAtFixedRate(this, 5*60000L, 5*60000L); // Run once every 5 minutes to clean map
+ }
+
+ /**
+ * Configure the throttle. This should be called from BaseServlet.provisioningParametersChanged(), to make sure it stays up to date.
+ */
+ public static void configure() {
+ Parameters p = Parameters.getParameter(Parameters.THROTTLE_FILTER);
+ if (p != null) {
+ try {
+ Class.forName(JETTY_REQUEST);
+ String v = p.getValue();
+ if (v != null && !v.equals("off")) {
+ String[] pp = v.split(",");
+ if (pp != null) {
+ n_requests = (pp.length > 0) ? getInt(pp[0], DEFAULT_N) : DEFAULT_N;
+ m_minutes = (pp.length > 1) ? getInt(pp[1], DEFAULT_M) : DEFAULT_M;
+ action = (pp.length > 2 && pp[2] != null && pp[2].equalsIgnoreCase("throttle")) ? ACTION_THROTTLE : ACTION_DROP;
+ enabled = true;
+ // ACTION_THROTTLE is not currently working, so is not supported
+ if (action == ACTION_THROTTLE) {
+ action = ACTION_DROP;
+ logger.info("Throttling is not currently supported; action changed to DROP");
+ }
+ logger.info("ThrottleFilter is ENABLED for /publish requests; N="+n_requests+", M="+m_minutes+", Action="+action);
+ return;
+ }
+ }
+ } catch (ClassNotFoundException e) {
+ logger.warn("Class "+JETTY_REQUEST+" is not available; this filter requires Jetty.");
+ }
+ }
+ logger.info("ThrottleFilter is DISABLED for /publish requests.");
+ enabled = false;
+ map.clear();
+ }
+ private static int getInt(String s, int deflt) {
+ try {
+ return Integer.parseInt(s);
+ } catch (NumberFormatException x) {
+ return deflt;
+ }
+ }
+ @Override
+ public void destroy() {
+ rolex.cancel();
+ map.clear();
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException
+ {
+ if (enabled && action == ACTION_THROTTLE) {
+ throttleFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);
+ } else if (enabled) {
+ dropFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);
+ } else {
+ chain.doFilter(request, response);
+ }
+ }
+ public void dropFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
+ throws IOException, ServletException
+ {
+ int rate = getRequestRate((HttpServletRequest) request);
+ if (rate >= n_requests) {
+ // drop request - only works under Jetty
+ String m = String.format("Dropping connection: %s %d bad connections in %d minutes", getConnectionId((HttpServletRequest) request), rate, m_minutes);
+ logger.info(m);
+ Request base_request = (request instanceof Request)
+ ? (Request) request
+ : AbstractHttpConnection.getCurrentConnection().getRequest();
+ base_request.getConnection().getEndPoint().close();
+ } else {
+ chain.doFilter(request, response);
+ }
+ }
+ public void throttleFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
+ throws IOException, ServletException
+ {
+ // throttle request
+ String id = getConnectionId((HttpServletRequest) request);
+ int rate = getRequestRate((HttpServletRequest) request);
+ Object results = request.getAttribute(THROTTLE_MARKER);
+ if (rate >= n_requests && results == null) {
+ String m = String.format("Throttling connection: %s %d bad connections in %d minutes", getConnectionId((HttpServletRequest) request), rate, m_minutes);
+ logger.info(m);
+ Continuation continuation = ContinuationSupport.getContinuation(request);
+ continuation.suspend();
+ register(id, continuation);
+ continuation.undispatch();
+ } else {
+ chain.doFilter(request, response);
+ @SuppressWarnings("resource")
+ InputStream is = request.getInputStream();
+ byte[] b = new byte[4096];
+ int n = is.read(b);
+ while (n > 0) {
+ n = is.read(b);
+ }
+ resume(id);
+ }
+ }
+ private Map<String, List<Continuation>> suspended_requests = new HashMap<String, List<Continuation>>();
+ private void register(String id, Continuation continuation) {
+ synchronized (suspended_requests) {
+ List<Continuation> list = suspended_requests.get(id);
+ if (list == null) {
+ list = new ArrayList<Continuation>();
+ suspended_requests.put(id, list);
+ }
+ list.add(continuation);
+ }
+ }
+ private void resume(String id) {
+ synchronized (suspended_requests) {
+ List<Continuation> list = suspended_requests.get(id);
+ if (list != null) {
+ // when the waited for event happens
+ Continuation continuation = list.remove(0);
+ continuation.setAttribute(ThrottleFilter.THROTTLE_MARKER, new Object());
+ continuation.resume();
+ }
+ }
+ }
+
+ /**
+ * Return a count of number of requests in the last M minutes, iff this is a "bad" request.
+ * If the request has been resumed (if it contains the THROTTLE_MARKER) it is considered good.
+ * @param request the request
+ * @return number of requests in the last M minutes, 0 means it is a "good" request
+ */
+ private int getRequestRate(HttpServletRequest request) {
+ String expecthdr = request.getHeader("Expect");
+ if (expecthdr != null && expecthdr.equalsIgnoreCase("100-continue"))
+ return 0;
+
+ String key = getConnectionId(request);
+ synchronized (map) {
+ Counter cnt = map.get(key);
+ if (cnt == null) {
+ cnt = new Counter();
+ map.put(key, cnt);
+ }
+ int n = cnt.getRequestRate();
+ return n;
+ }
+ }
+
+ public class Counter {
+ private List<Long> times = new Vector<Long>(); // a record of request times
+ public int prune() {
+ try {
+ long n = System.currentTimeMillis() - (m_minutes * ONE_MINUTE);
+ long t = times.get(0);
+ while (t < n) {
+ times.remove(0);
+ t = times.get(0);
+ }
+ } catch (IndexOutOfBoundsException e) {
+ // ignore
+ }
+ return times.size();
+ }
+ public int getRequestRate() {
+ times.add(System.currentTimeMillis());
+ return prune();
+ }
+ }
+
+ /**
+ * Identify a connection by endpoint IP address, and feed ID.
+ */
+ private String getConnectionId(HttpServletRequest req) {
+ return req.getRemoteAddr() + "/" + getFeedId(req);
+ }
+ private int getFeedId(HttpServletRequest req) {
+ String path = req.getPathInfo();
+ if (path == null || path.length() < 2)
+ return -1;
+ path = path.substring(1);
+ int ix = path.indexOf('/');
+ if (ix < 0 || ix == path.length()-1)
+ return -2;
+ try {
+ int feedid = Integer.parseInt(path.substring(0, ix));
+ return feedid;
+ } catch (NumberFormatException e) {
+ return -1;
+ }
+ }
+
+ @Override
+ public void run() {
+ // Once every 5 minutes, go through the map, and remove empty entrys
+ for (Object s : map.keySet().toArray()) {
+ synchronized (map) {
+ Counter c = map.get(s);
+ if (c.prune() <= 0)
+ map.remove(s);
+ }
+ }
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/URLUtilities.java b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/URLUtilities.java
new file mode 100644
index 00000000..c1793e52
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/URLUtilities.java
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.provisioning.utils;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+
+import com.att.research.datarouter.provisioning.BaseServlet;
+
+/**
+ * Utility functions used to generate the different URLs used by the Data Router.
+ *
+ * @author Robert Eby
+ * @version $Id: URLUtilities.java,v 1.2 2014/03/12 19:45:41 eby Exp $
+ */
+public class URLUtilities {
+ /**
+ * Generate the URL used to access a feed.
+ * @param feedid the feed id
+ * @return the URL
+ */
+ public static String generateFeedURL(int feedid) {
+ return "https://" + BaseServlet.prov_name + "/feed/" + feedid;
+ }
+ /**
+ * Generate the URL used to publish to a feed.
+ * @param feedid the feed id
+ * @return the URL
+ */
+ public static String generatePublishURL(int feedid) {
+ return "https://" + BaseServlet.prov_name + "/publish/" + feedid;
+ }
+ /**
+ * Generate the URL used to subscribe to a feed.
+ * @param feedid the feed id
+ * @return the URL
+ */
+ public static String generateSubscribeURL(int feedid) {
+ return "https://" + BaseServlet.prov_name + "/subscribe/" + feedid;
+ }
+ /**
+ * Generate the URL used to access a feed's logs.
+ * @param feedid the feed id
+ * @return the URL
+ */
+ public static String generateFeedLogURL(int feedid) {
+ return "https://" + BaseServlet.prov_name + "/feedlog/" + feedid;
+ }
+ /**
+ * Generate the URL used to access a subscription.
+ * @param subid the subscription id
+ * @return the URL
+ */
+ public static String generateSubscriptionURL(int subid) {
+ return "https://" + BaseServlet.prov_name + "/subs/" + subid;
+ }
+ /**
+ * Generate the URL used to access a subscription's logs.
+ * @param subid the subscription id
+ * @return the URL
+ */
+ public static String generateSubLogURL(int subid) {
+ return "https://" + BaseServlet.prov_name + "/sublog/" + subid;
+ }
+ /**
+ * Generate the URL used to access the provisioning data on the peer POD.
+ * @return the URL
+ */
+ public static String generatePeerProvURL() {
+ return "https://" + getPeerPodName() + "/internal/prov";
+ }
+ /**
+ * Generate the URL used to access the logfile data on the peer POD.
+ * @return the URL
+ */
+ public static String generatePeerLogsURL() {
+ //Fixes for Itrack ticket - DATARTR-4#Fixing if only one Prov is configured, not to give exception to fill logs.
+ String peerPodUrl = getPeerPodName();
+ if(peerPodUrl.equals("") || peerPodUrl.equals(null)){
+ return "";
+ }
+
+ return "https://" + peerPodUrl + "/internal/drlogs/";
+ }
+ /**
+ * Return the real (non CNAME) version of the peer POD's DNS name.
+ * @return the name
+ */
+ public static String getPeerPodName() {
+ if (other_pod == null) {
+ String this_pod = "";
+ try {
+ this_pod = InetAddress.getLocalHost().getHostName();
+ System.out.println("this_pod: "+this_pod);
+ } catch (UnknownHostException e) {
+ this_pod = "";
+ }
+ System.out.println("ALL PODS: "+Arrays.asList(BaseServlet.getPods()));
+ for (String pod : BaseServlet.getPods()) {
+ if (!pod.equals(this_pod))
+ other_pod = pod;
+ }
+ }
+ return other_pod;
+ }
+ private static String other_pod;
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/package.html b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/package.html
new file mode 100644
index 00000000..7855bb40
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/provisioning/utils/package.html
@@ -0,0 +1,30 @@
+#-------------------------------------------------------------------------------
+# ============LICENSE_START==================================================
+# * org.onap.dmaap
+# * ===========================================================================
+# * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+# * ===========================================================================
+# * 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.
+# * ============LICENSE_END====================================================
+# *
+# * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+# *
+#-------------------------------------------------------------------------------
+
+<html>
+<body>
+<p>
+This package provide various helper classes used by the provisioning server.
+</p>
+</body>
+</html>
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/reports/DailyLatencyReport.java b/datarouter-prov/src/main/java/com/att/research/datarouter/reports/DailyLatencyReport.java
new file mode 100644
index 00000000..63d612f9
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/reports/DailyLatencyReport.java
@@ -0,0 +1,194 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.reports;
+
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeSet;
+
+import com.att.research.datarouter.provisioning.utils.DB;
+
+/**
+ * Generate a daily per feed latency report. The report is a .csv file containing the following columns:
+ * <table>
+ * <tr><td>date</td><td>the date for this record</td></tr>
+ * <tr><td>feedid</td><td>the Feed ID for this record</td></tr>
+ * <tr><td>minsize</td><td>the minimum size of all files published on this feed and date</td></tr>
+ * <tr><td>maxsize</td><td>the maximum size of all files published on this feed and date</td></tr>
+ * <tr><td>avgsize</td><td>the average size of all files published on this feed and date</td></tr>
+ * <tr><td>minlat</td><td>the minimum latency in delivering this feed to all subscribers (in ms)</td></tr>
+ * <tr><td>maxlat</td><td>the maximum latency in delivering this feed to all subscribers (in ms)</td></tr>
+ * <tr><td>avglat</td><td>the average latency in delivering this feed to all subscribers (in ms)</td></tr>
+ * <tr><td>fanout</td><td>the average number of subscribers this feed was delivered to</td></tr>
+ * </table>
+ * <p>
+ * In the context of this report, latency is defined as the value
+ * <i>(D<sub>e</sub> - P<sub>s</sub>)</i>
+ * where:
+ * </p>
+ * <p>P<sub>s</sub> is the time that the publication of the file to the node starts.</p>
+ * <p>D<sub>e</sub> is the time that the delivery of the file to the subscriber ends.</p>
+ *
+ * @author Robert P. Eby
+ * @version $Id: DailyLatencyReport.java,v 1.2 2013/11/06 16:23:54 eby Exp $
+ */
+public class DailyLatencyReport extends ReportBase {
+ private static final String SELECT_SQL =
+ "select EVENT_TIME, TYPE, PUBLISH_ID, FEED_FILEID, FEEDID, CONTENT_LENGTH from LOG_RECORDS" +
+ " where EVENT_TIME >= ? and EVENT_TIME <= ?";
+
+ private class Job {
+ public long pubtime = 0;
+ public long clen = 0;
+ public List<Long> deltime = new ArrayList<Long>();
+ public long minLatency() {
+ long n = deltime.isEmpty() ? 0 : Long.MAX_VALUE;
+ for (Long l : deltime)
+ n = Math.min(n, l-pubtime);
+ return n;
+ }
+ public long maxLatency() {
+ long n = 0;
+ for (Long l : deltime)
+ n = Math.max(n, l-pubtime);
+ return n;
+ }
+ public long totalLatency() {
+ long n = 0;
+ for (Long l : deltime)
+ n += (l-pubtime);
+ return n;
+ }
+ }
+ private class Counters {
+ public final String date;
+ public final int feedid;
+ public final Map<String, Job> jobs;
+ public Counters(String d, int fid) {
+ date = d;
+ feedid = fid;
+ jobs = new HashMap<String, Job>();
+ }
+ public void addEvent(long etime, String type, String id, String fid, long clen) {
+ Job j = jobs.get(id);
+ if (j == null) {
+ j = new Job();
+ jobs.put(id, j);
+ }
+ if (type.equals("pub")) {
+ j.pubtime = getPstart(id);
+ j.clen = clen;
+ } else if (type.equals("del")) {
+ j.deltime.add(etime);
+ }
+ }
+ @Override
+ public String toString() {
+ long minsize = Long.MAX_VALUE, maxsize = 0, avgsize = 0;
+ long minl = Long.MAX_VALUE, maxl = 0;
+ long fanout = 0, totall = 0, totaln = 0;
+ for (Job j : jobs.values()) {
+ minsize = Math.min(minsize, j.clen);
+ maxsize = Math.max(maxsize, j.clen);
+ avgsize += j.clen;
+ minl = Math.min(minl, j.minLatency());
+ maxl = Math.max(maxl, j.maxLatency());
+ totall += j.totalLatency();
+ totaln += j.deltime.size();
+ fanout += j.deltime.size();
+ }
+ if (jobs.size() > 0) {
+ avgsize /= jobs.size();
+ fanout /= jobs.size();
+ }
+ long avgl = (totaln > 0) ? (totall / totaln) : 0;
+ return date + "," + feedid + "," + minsize + "," + maxsize + "," + avgsize + "," + minl + "," + maxl + "," + avgl + "," + fanout;
+ }
+ }
+ private long getPstart(String t) {
+ if (t.indexOf('.') > 0)
+ t = t.substring(0, t.indexOf('.'));
+ return Long.parseLong(t);
+ }
+
+ @Override
+ public void run() {
+ Map<String, Counters> map = new HashMap<String, Counters>();
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+ long start = System.currentTimeMillis();
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ PreparedStatement ps = conn.prepareStatement(SELECT_SQL);
+ ps.setLong(1, from);
+ ps.setLong(2, to);
+ ResultSet rs = ps.executeQuery();
+ while (rs.next()) {
+ String id = rs.getString("PUBLISH_ID");
+ int feed = rs.getInt("FEEDID");
+ long etime = rs.getLong("EVENT_TIME");
+ String type = rs.getString("TYPE");
+ String fid = rs.getString("FEED_FILEID");
+ long clen = rs.getLong("CONTENT_LENGTH");
+ String date = sdf.format(new Date(getPstart(id)));
+ String key = date + "," + feed;
+ Counters c = map.get(key);
+ if (c == null) {
+ c = new Counters(date, feed);
+ map.put(key, c);
+ }
+ c.addEvent(etime, type, id, fid, clen);
+ }
+ rs.close();
+ ps.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ logger.debug("Query time: " + (System.currentTimeMillis()-start) + " ms");
+ try {
+ PrintWriter os = new PrintWriter(outfile);
+ os.println("date,feedid,minsize,maxsize,avgsize,minlat,maxlat,avglat,fanout");
+ for (String key : new TreeSet<String>(map.keySet())) {
+ Counters c = map.get(key);
+ os.println(c.toString());
+ }
+ os.close();
+ } catch (FileNotFoundException e) {
+ System.err.println("File cannot be written: "+outfile);
+ }
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/reports/FeedReport.java b/datarouter-prov/src/main/java/com/att/research/datarouter/reports/FeedReport.java
new file mode 100644
index 00000000..9fe7e27f
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/reports/FeedReport.java
@@ -0,0 +1,395 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.reports;
+
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.LineNumberReader;
+import java.io.PrintWriter;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import com.att.research.datarouter.provisioning.utils.DB;
+
+/**
+ * Generate a feeds report. The report is a .CSV file.
+ *
+ * @author Robert P. Eby
+ * @version $Id: FeedReport.java,v 1.2 2013/11/06 16:23:55 eby Exp $
+ */
+public class FeedReport extends ReportBase {
+ private static final String SELECT_SQL =
+ // Note to use the time in the publish_id, use date(from_unixtime(substring(publish_id, 1, 10)))
+ // To just use month, substring(from_unixtime(event_time div 1000), 1, 7)
+ "select date(from_unixtime(event_time div 1000)) as date, type, feedid, delivery_subid, count(*) as count" +
+ " from LOG_RECORDS" +
+ " where type = 'pub' or type = 'del'" +
+ " group by date, type, feedid, delivery_subid";
+ private static final String SELECT_SQL_OLD =
+ "select PUBLISH_ID, TYPE, FEEDID, DELIVERY_SUBID from LOG_RECORDS where EVENT_TIME >= ? and EVENT_TIME <= ?";
+
+ @Override
+ public void run() {
+ boolean alg1 = true;
+ JSONObject jo = new JSONObject();
+ long start = System.currentTimeMillis();
+ StringBuilder sb = new StringBuilder();
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ PreparedStatement ps = conn.prepareStatement(SELECT_SQL);
+// ps.setLong(1, from);
+// ps.setLong(2, to);
+ ResultSet rs = ps.executeQuery();
+ while (rs.next()) {
+ if (alg1) {
+ String date = rs.getString("date");
+ String type = rs.getString("type");
+ int feedid = rs.getInt("feedid");
+ int subid = type.equals("del") ? rs.getInt("delivery_subid") : 0;
+ int count = rs.getInt("count");
+ sb.append(date + "," + type + "," + feedid + "," + subid + "," + count + "\n");
+ } else {
+ String date = rs.getString("date");
+ JSONObject datemap = jo.optJSONObject(date);
+ if (datemap == null) {
+ datemap = new JSONObject();
+ jo.put(date, datemap);
+ }
+ int feed = rs.getInt("FEEDID");
+ JSONObject feedmap = datemap.optJSONObject(""+feed);
+ if (feedmap == null) {
+ feedmap = new JSONObject();
+ feedmap.put("pubcount", 0);
+ datemap.put(""+feed, feedmap);
+ }
+ String type = rs.getString("TYPE");
+ int count = rs.getInt("count");
+ if (type.equals("pub")) {
+ feedmap.put("pubcount", count);
+ } else if (type.equals("del")) {
+ String subid = ""+rs.getInt("DELIVERY_SUBID");
+ feedmap.put(subid, count);
+ }
+ }
+ }
+ rs.close();
+ ps.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ logger.debug("Query time: " + (System.currentTimeMillis()-start) + " ms");
+ try {
+ PrintWriter os = new PrintWriter(outfile);
+ if (alg1) {
+ os.print("date,type,feedid,subid,count\n");
+ os.print(sb.toString());
+ } else {
+ os.println(toHTML(jo));
+ }
+ os.close();
+ } catch (FileNotFoundException e) {
+ System.err.println("File cannot be written: "+outfile);
+ }
+ }
+
+ public void run2() {
+ JSONObject jo = new JSONObject();
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+ long start = System.currentTimeMillis();
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ PreparedStatement ps = conn.prepareStatement(SELECT_SQL_OLD);
+ ps.setLong(1, from);
+ ps.setLong(2, to);
+ ps.setFetchSize(100000);
+ ResultSet rs = ps.executeQuery();
+ while (rs.next()) {
+ String id = rs.getString("PUBLISH_ID");
+ String date = sdf.format(new Date(getPstart(id)));
+ JSONObject datemap = jo.optJSONObject(date);
+ if (datemap == null) {
+ datemap = new JSONObject();
+ jo.put(date, datemap);
+ }
+ int feed = rs.getInt("FEEDID");
+ JSONObject feedmap = datemap.optJSONObject(""+feed);
+ if (feedmap == null) {
+ feedmap = new JSONObject();
+ feedmap.put("pubcount", 0);
+ datemap.put(""+feed, feedmap);
+ }
+ String type = rs.getString("TYPE");
+ if (type.equals("pub")) {
+ try {
+ int n = feedmap.getInt("pubcount");
+ feedmap.put("pubcount", n+1);
+ } catch (JSONException e) {
+ feedmap.put("pubcount", 1);
+ }
+ } else if (type.equals("del")) {
+ String subid = ""+rs.getInt("DELIVERY_SUBID");
+ try {
+ int n = feedmap.getInt(subid);
+ feedmap.put(subid, n+1);
+ } catch (JSONException e) {
+ feedmap.put(subid, 1);
+ }
+ }
+ }
+ rs.close();
+ ps.close();
+ db.release(conn);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ logger.debug("Query time: " + (System.currentTimeMillis()-start) + " ms");
+ try {
+ PrintWriter os = new PrintWriter(outfile);
+ os.println(toHTML(jo));
+ os.close();
+ } catch (FileNotFoundException e) {
+ System.err.println("File cannot be written: "+outfile);
+ }
+ }
+ private long getPstart(String t) {
+ if (t.indexOf('.') > 0)
+ t = t.substring(0, t.indexOf('.'));
+ return Long.parseLong(t);
+ }
+ @SuppressWarnings("unused")
+ private static String toHTMLNested(JSONObject jo) {
+ StringBuilder s = new StringBuilder();
+ s.append("<table>\n");
+ s.append("<tr><th>Date</th><th>Feeds</th></tr>\n");
+ String[] dates = JSONObject.getNames(jo);
+ Arrays.sort(dates);
+ for (int i = dates.length-1; i >= 0; i--) {
+ String date = dates[i];
+ JSONObject j2 = jo.getJSONObject(date);
+ String[] feeds = JSONObject.getNames(j2);
+ Arrays.sort(feeds);
+ s.append("<tr><td>"+date+"</td><td>");
+ s.append(feeds.length).append(feeds.length > 1 ? " Feeds\n" : " Feed\n");
+ s.append("<table>\n");
+ s.append("<tr><th>Feed ID</th><th>Publish Count</th><th>Subscriptions</th></tr>\n");
+ for (String feed : feeds) {
+ JSONObject j3 = j2.getJSONObject(feed);
+ String[] subs = JSONObject.getNames(j3);
+ Arrays.sort(subs);
+ s.append("<tr><td>"+feed+"</td>");
+ s.append("<td>"+j3.getInt("pubcount")+"</td>");
+ int scnt = j3.length()-1;
+ s.append("<td>").append(scnt).append(" Subcription");
+ if (scnt > 1)
+ s.append("s");
+ s.append("<table>\n");
+ s.append("<tr><th>Sub ID</th><th>Delivery Count</th></tr>\n");
+ for (String sub : subs) {
+ if (!sub.equals("pubcount")) {
+ s.append("<tr><td>"+sub+"</td>");
+ s.append("<td>"+j3.getInt(sub)+"</td>");
+ s.append("</td></tr>\n");
+ }
+ }
+ s.append("</table>\n");
+
+ s.append("</td></tr>\n");
+ }
+ s.append("</table>\n");
+ s.append("</td></tr>\n");
+ }
+ s.append("</table>\n");
+ return s.toString();
+ }
+ private static String toHTML(JSONObject jo) {
+ StringBuilder s = new StringBuilder();
+ s.append("<table>\n");
+ s.append("<tr><th>Date</th><th>Feeds</th><th>Feed ID</th><th>Publish Count</th><th>Subs</th><th>Sub ID</th><th>Delivery Count</th></tr>\n");
+ String[] dates = JSONObject.getNames(jo);
+ Arrays.sort(dates);
+ for (int i = dates.length-1; i >= 0; i--) {
+ String date = dates[i];
+ JSONObject j2 = jo.getJSONObject(date);
+ int rc1 = countrows(j2);
+ String[] feeds = JSONObject.getNames(j2);
+ Arrays.sort(feeds);
+ s.append("<tr><td rowspan=\"" + rc1 + "\">")
+ .append(date)
+ .append("</td>");
+ s.append("<td rowspan=\"" + rc1 + "\">")
+ .append(feeds.length)
+ .append("</td>");
+ String px1 = "";
+ for (String feed : feeds) {
+ JSONObject j3 = j2.getJSONObject(feed);
+ int pubcount = j3.getInt("pubcount");
+ int subcnt = j3.length()-1;
+ int rc2 = (subcnt < 1) ? 1 : subcnt;
+ String[] subs = JSONObject.getNames(j3);
+ Arrays.sort(subs);
+ s.append(px1)
+ .append("<td rowspan=\"" + rc2 + "\">")
+ .append(feed)
+ .append("</td>");
+ s.append("<td rowspan=\"" + rc2 + "\">")
+ .append(pubcount)
+ .append("</td>");
+ s.append("<td rowspan=\"" + rc2 + "\">")
+ .append(subcnt)
+ .append("</td>");
+ String px2 = "";
+ for (String sub : subs) {
+ if (!sub.equals("pubcount")) {
+ s.append(px2);
+ s.append("<td>"+sub+"</td>");
+ s.append("<td>"+j3.getInt(sub)+"</td>");
+ s.append("</tr>\n");
+ px2 = "<tr>";
+ }
+ }
+ if (px2.equals(""))
+ s.append("<td></td><td></td></tr>\n");
+ px1 = "<tr>";
+ }
+ }
+ s.append("</table>\n");
+ return s.toString();
+ }
+ private static int countrows(JSONObject x) {
+ int n = 0;
+ for (String feed : JSONObject.getNames(x)) {
+ JSONObject j3 = x.getJSONObject(feed);
+ int subcnt = j3.length()-1;
+ int rc2 = (subcnt < 1) ? 1 : subcnt;
+ n += rc2;
+ }
+ return (n > 0) ? n : 1;
+ }
+
+ /**
+ * Convert a .CSV file (as generated by the normal FeedReport mechanism) to an HTML table.
+ * @param args
+ */
+ public static void main(String[] args) {
+ int rtype = 0; // 0 -> day, 1 -> week, 2 -> month, 3 -> year
+ String infile = null;
+ String outfile = null;
+ for (int i = 0; i < args.length; i++) {
+ if (args[i].equals("-t")) {
+ switch (args[++i].charAt(0)) {
+ case 'w': rtype = 1; break;
+ case 'm': rtype = 2; break;
+ case 'y': rtype = 3; break;
+ default: rtype = 0; break;
+ }
+ } else if (infile == null) {
+ infile = args[i];
+ } else if (outfile == null) {
+ outfile = args[i];
+ }
+ }
+ if (infile == null) {
+ System.err.println("usage: FeedReport [ -t <reporttype> ] [ <input .csv> ] [ <output .html> ]");
+ System.exit(1);
+ }
+ try {
+ JSONObject jo = new JSONObject();
+ LineNumberReader lr = new LineNumberReader(new FileReader(infile));
+ String line = lr.readLine();
+ while (line != null) {
+ String[] tt = line.split(",");
+ if (tt[0].startsWith("2")) {
+ String date = tt[0];
+ switch (rtype) {
+ case 1:
+ String[] xx = date.split("-");
+ Calendar cal = new GregorianCalendar(new Integer(xx[0]), new Integer(xx[1])-1, new Integer(xx[2]));
+ date = xx[0] + "-W" + cal.get(Calendar.WEEK_OF_YEAR);
+ break;
+ case 2: date = date.substring(0, 7); break;
+ case 3: date = date.substring(0, 4); break;
+ }
+ JSONObject datemap = jo.optJSONObject(date);
+ if (datemap == null) {
+ datemap = new JSONObject();
+ jo.put(date, datemap);
+ }
+ int feed = Integer.parseInt(tt[2]);
+ JSONObject feedmap = datemap.optJSONObject(""+feed);
+ if (feedmap == null) {
+ feedmap = new JSONObject();
+ feedmap.put("pubcount", 0);
+ datemap.put(""+feed, feedmap);
+ }
+ String type = tt[1];
+ int count = Integer.parseInt(tt[4]);
+ if (type.equals("pub")) {
+ try {
+ int n = feedmap.getInt("pubcount");
+ feedmap.put("pubcount", n+count);
+ } catch (JSONException e) {
+ feedmap.put("pubcount", count);
+ }
+ } else if (type.equals("del")) {
+ String subid = tt[3];
+ try {
+ int n = feedmap.getInt(subid);
+ feedmap.put(subid, n+count);
+ } catch (JSONException e) {
+ feedmap.put(subid, count);
+ }
+ }
+ }
+ line = lr.readLine();
+ }
+ lr.close();
+ String t = toHTML(jo);
+ switch (rtype) {
+ case 1: t = t.replaceAll("<th>Date</th>", "<th>Week</th>"); break;
+ case 2: t = t.replaceAll("<th>Date</th>", "<th>Month</th>"); break;
+ case 3: t = t.replaceAll("<th>Date</th>", "<th>Year</th>"); break;
+ }
+ System.out.println(t);
+ } catch (Exception e) {
+ System.err.println(e);
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/reports/LatencyReport.java b/datarouter-prov/src/main/java/com/att/research/datarouter/reports/LatencyReport.java
new file mode 100644
index 00000000..96e096e9
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/reports/LatencyReport.java
@@ -0,0 +1,179 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.reports;
+
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.att.research.datarouter.provisioning.utils.DB;
+
+/**
+ * Generate a per-file latency report. It reports on the details related to one file published
+ * on one feed. This report can be further reduced in order to generate more specific reports
+ * based on feed ID or node name. The report is a .csv file containing the following columns:
+ * <table>
+ * <tr><td>recordid</td><td>the unique record ID assigned to a particular incoming feed</td></tr>
+ * <tr><td>feedid</td><td>the Feed ID for this record</td></tr>
+ * <tr><td>uri</td><td>the URI of the file delivered</td></tr>
+ * <tr><td>size</td><td>the size of the file delivered</td></tr>
+ * <tr><td>min</td><td>the minimum latency in delivering this feed to a subscriber (in ms)</td></tr>
+ * <tr><td>max</td><td>the maximum latency in delivering this feed to a subscriber (in ms)</td></tr>
+ * <tr><td>avg</td><td>the average latency in delivering this feed to all subscribers (in ms)</td></tr>
+ * <tr><td>fanout</td><td>the number of subscribers this feed was delivered to</td></tr>
+ * </table>
+ *
+ * @author Robert P. Eby
+ * @version $Id: LatencyReport.java,v 1.1 2013/10/28 18:06:53 eby Exp $
+ */
+public class LatencyReport extends ReportBase {
+ private static final String SELECT_SQL =
+ "select EVENT_TIME, TYPE, PUBLISH_ID, FEED_FILEID, FEEDID, CONTENT_LENGTH from LOG_RECORDS" +
+ " where EVENT_TIME >= ? and EVENT_TIME <= ? order by PUBLISH_ID, EVENT_TIME";
+
+ private class Event {
+ public final String type;
+ public final long time;
+ public Event(String t, long tm) {
+ type = t;
+ time = tm;
+ }
+ }
+ private class Counters {
+ public final String id;
+ public final int feedid;
+ public final long clen;
+ public final String fileid;
+ public final List<Event> events;
+ public Counters(String i, int fid, long c, String s) {
+ id = i;
+ feedid = fid;
+ clen = c;
+ fileid = s;
+ events = new ArrayList<Event>();
+ }
+ private long pubtime;
+ public void addEvent(String t, long tm) {
+ events.add(new Event(t, tm));
+ if (t.equals("pub"))
+ pubtime = tm;
+ }
+ public long min() {
+ long min = Long.MAX_VALUE;
+ for (Event e : events) {
+ if (e.type.equals("del")) {
+ min = Math.min(min, e.time - pubtime);
+ }
+ }
+ return min;
+ }
+ public long max() {
+ long max = 0;
+ for (Event e : events) {
+ if (e.type.equals("del")) {
+ max = Math.max(max, e.time - pubtime);
+ }
+ }
+ return max;
+ }
+ public long avg() {
+ long total = 0, c = 0;
+ for (Event e : events) {
+ if (e.type.equals("del")) {
+ total += e.time - pubtime;
+ c++;
+ }
+ }
+ return (c == 0) ? 0 : total/c;
+ }
+ public int fanout() {
+ int n = 0;
+ for (Event e : events) {
+ if (e.type.equals("del")) {
+ n++;
+ }
+ }
+ return n;
+ }
+ @Override
+ public String toString() {
+ return feedid + "," + fileid + "," + clen + "," + min() + "," + max() + "," + avg() + "," + fanout();
+ }
+ }
+
+ @Override
+ public void run() {
+ long start = System.currentTimeMillis();
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ PreparedStatement ps = conn.prepareStatement(SELECT_SQL);
+ ps.setLong(1, from);
+ ps.setLong(2, to);
+ ResultSet rs = ps.executeQuery();
+ PrintWriter os = new PrintWriter(outfile);
+ os.println("recordid,feedid,uri,size,min,max,avg,fanout");
+ Counters c = null;
+ while (rs.next()) {
+ long etime = rs.getLong("EVENT_TIME");
+ String type = rs.getString("TYPE");
+ String id = rs.getString("PUBLISH_ID");
+ String fid = rs.getString("FEED_FILEID");
+ int feed = rs.getInt("FEEDID");
+ long clen = rs.getLong("CONTENT_LENGTH");
+ if (c != null && !id.equals(c.id)) {
+ String line = id + "," + c.toString();
+ os.println(line);
+ c = null;
+ }
+ if (c == null) {
+ c = new Counters(id, feed, clen, fid);
+ }
+ if (feed != c.feedid)
+ System.err.println("Feed ID mismatch, "+feed+" <=> "+c.feedid);
+ if (clen != c.clen)
+ System.err.println("Cont Len mismatch, "+clen+" <=> "+c.clen);
+// if (fid != c.fileid)
+// System.err.println("File ID mismatch, "+fid+" <=> "+c.fileid);
+ c.addEvent(type, etime);
+ }
+ rs.close();
+ ps.close();
+ db.release(conn);
+ os.close();
+ } catch (FileNotFoundException e) {
+ System.err.println("File cannot be written: "+outfile);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ logger.debug("Query time: " + (System.currentTimeMillis()-start) + " ms");
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/reports/Report.java b/datarouter-prov/src/main/java/com/att/research/datarouter/reports/Report.java
new file mode 100644
index 00000000..bd64e0eb
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/reports/Report.java
@@ -0,0 +1,155 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.reports;
+
+import java.lang.reflect.Constructor;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
+
+/**
+ * This class provides a CLI to generate any of the reports defined in this package.
+ *
+ * @author Robert P. Eby
+ * @version $Id: Report.java,v 1.2 2013/11/06 16:23:55 eby Exp $
+ */
+public class Report {
+ /**
+ * Generate .csv report files from the database. Usage:
+ * <pre>
+ * java com.att.research.datarouter.reports.Report [ -t <i>type</i> ] [ -o <i>outfile</i> ] [ <i>fromdate</i> [ <i>todate</i> ]]
+ * </pre>
+ * <i>type</i> should be <b>volume</b> for a {@link VolumeReport},
+ * <b>feed</b> for a {@link FeedReport},
+ * <b>latency</b> for a {@link LatencyReport}, or
+ * <b>dailyLatency</b> for a {@link DailyLatencyReport}.
+ * If <i>outfile</i> is not specified, the report goes into a file <i>/tmp/nnnnnnnnnnnnn.csv</i>,
+ * where nnnnnnnnnnnnn is the current time in milliseconds.
+ * If <i>from</i> and <i>to</i> are not specified, then the report is limited to the last weeks worth of data.
+ * <i>from</i> can be the keyword <b>ALL</b> to specify all data in the DB, or the keyword <b>yesterday</b>.
+ * Otherwise, <i>from</i> and <i>to</i> should match the pattern YYYY-MM-DD.
+ * @param args the command line arguments
+ */
+ public static void main(String[] args) {
+ ReportBase report = new VolumeReport();
+ String outfile = "/tmp/" + System.currentTimeMillis() + ".csv";
+ String from = null, to = null;
+
+ for (int i = 0; i < args.length; i++) {
+ if (args[i].equals("-?")) {
+ System.err.println("usage: java com.att.research.datarouter.reports.Report [ -t <i>type</i> ] [ -o <i>outfile</i> ] [ <i>fromdate</i> [ <i>todate</i> ]]");
+ System.exit(0);
+ } else if (args[i].equals("-o")) {
+ if (++i < args.length) {
+ outfile = args[i];
+ }
+ } else if (args[i].equals("-t")) {
+ if (++i < args.length) {
+ String base = args[i];
+ base = Character.toUpperCase(base.charAt(0)) + base.substring(1);
+ base = "com.att.research.datarouter.reports."+base+"Report";
+ try {
+ @SuppressWarnings("unchecked")
+ Class<? extends ReportBase> cl = (Class<? extends ReportBase>) Class.forName(base);
+ Constructor<? extends ReportBase> con = cl.getConstructor();
+ report = con.newInstance();
+ } catch (Exception e) {
+ System.err.println("Unknown report type: "+args[i]);
+ System.exit(1);
+ }
+ }
+ } else if (from == null) {
+ from = args[i];
+ } else {
+ to = args[i];
+ }
+ }
+ long lfrom = 0, lto = 0;
+ if (from == null) {
+ // last 7 days
+ TimeZone utc = TimeZone.getTimeZone("UTC");
+ Calendar cal = new GregorianCalendar(utc);
+ cal.set(Calendar.HOUR_OF_DAY, 0);
+ cal.set(Calendar.MINUTE, 0);
+ cal.set(Calendar.SECOND, 0);
+ cal.set(Calendar.MILLISECOND, 0);
+ lfrom = cal.getTimeInMillis() - (7 * 24 * 60 * 60 * 1000L); // 1 week
+ lto = cal.getTimeInMillis() - 1;
+ } else if (to == null) {
+ try {
+ String[] dates = getDates(from);
+ lfrom = Long.parseLong(dates[0]);
+ lto = Long.parseLong(dates[1]);
+ } catch (Exception e) {
+ System.err.println("Invalid date: "+from);
+ System.exit(1);
+ }
+ } else {
+ String[] dates;
+ try {
+ dates = getDates(from);
+ lfrom = Long.parseLong(dates[0]);
+ } catch (Exception e) {
+ System.err.println("Invalid date: "+from);
+ System.exit(1);
+ }
+ try {
+ dates = getDates(to);
+ lto = Long.parseLong(dates[0]);
+ } catch (Exception e) {
+ System.err.println("Invalid date: "+to);
+ System.exit(1);
+ }
+ }
+
+ report.setFrom(lfrom);
+ report.setTo(lto);
+ report.setOutputFile(outfile);
+ report.run();
+ }
+
+ private static String[] getDates(String d) throws Exception {
+ if (d.equals("ALL"))
+ return new String[] { "1", ""+System.currentTimeMillis() };
+
+ TimeZone utc = TimeZone.getTimeZone("UTC");
+ Calendar cal = new GregorianCalendar(utc);
+ if (d.matches("20\\d\\d-\\d\\d-\\d\\d")) {
+ cal.set(Calendar.YEAR, Integer.parseInt(d.substring(0, 4)));
+ cal.set(Calendar.MONTH, Integer.parseInt(d.substring(5, 7))-1);
+ cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(d.substring(8, 10)));
+ } else if (d.equals("yesterday")) {
+ cal.add(Calendar.DAY_OF_YEAR, -1);
+ } else
+ throw new Exception("wa?");
+ cal.set(Calendar.HOUR_OF_DAY, 0);
+ cal.set(Calendar.MINUTE, 0);
+ cal.set(Calendar.SECOND, 0);
+ cal.set(Calendar.MILLISECOND, 0);
+ long start = cal.getTimeInMillis();
+ long end = start + (24 * 60 * 60 * 1000L) - 1;
+ return new String[] { ""+start, ""+end };
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/reports/ReportBase.java b/datarouter-prov/src/main/java/com/att/research/datarouter/reports/ReportBase.java
new file mode 100644
index 00000000..2bdabf19
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/reports/ReportBase.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package com.att.research.datarouter.reports;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Base class for all the report generating classes.
+ *
+ * @author Robert P. Eby
+ * @version $Id: ReportBase.java,v 1.1 2013/10/28 18:06:53 eby Exp $
+ */
+abstract public class ReportBase implements Runnable {
+ protected long from, to;
+ protected String outfile;
+ protected Logger logger;
+
+ public ReportBase() {
+ this.from = 0;
+ this.to = System.currentTimeMillis();
+ this.logger = Logger.getLogger("com.att.research.datarouter.reports");
+ }
+
+ public void setFrom(long from) {
+ this.from = from;
+ }
+
+ public void setTo(long to) {
+ this.to = to;
+ }
+
+ public String getOutfile() {
+ return outfile;
+ }
+
+ public void setOutputFile(String s) {
+ this.outfile = s;
+ }
+
+ @Override
+ abstract public void run();
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/reports/SubscriberReport.java b/datarouter-prov/src/main/java/com/att/research/datarouter/reports/SubscriberReport.java
new file mode 100644
index 00000000..b003ab12
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/reports/SubscriberReport.java
@@ -0,0 +1,157 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.reports;
+
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeSet;
+
+import com.att.research.datarouter.provisioning.utils.DB;
+
+/**
+ * Generate a subscribers report. The report is a .CSV file. It contains information per-day and per-subscriber,
+ * on the status codes returned from each delivery attempt (1XX, 2XX, etc.) as well as a count of 4XX instead of a 100.
+ *
+ * @author Robert P. Eby
+ * @version $Id: SubscriberReport.java,v 1.2 2013/11/06 16:23:55 eby Exp $
+ */
+public class SubscriberReport extends ReportBase {
+ private static final String SELECT_SQL =
+ "select date(from_unixtime(EVENT_TIME div 1000)) as DATE, DELIVERY_SUBID, RESULT, COUNT(RESULT) as COUNT" +
+ " from LOG_RECORDS" +
+ " where TYPE = 'del' and EVENT_TIME >= ? and EVENT_TIME <= ?" +
+ " group by DATE, DELIVERY_SUBID, RESULT";
+ private static final String SELECT_SQL2 =
+ "select date(from_unixtime(EVENT_TIME div 1000)) as DATE, DELIVERY_SUBID, COUNT(CONTENT_LENGTH_2) as COUNT" +
+ " from LOG_RECORDS" +
+ " where TYPE = 'dlx' and CONTENT_LENGTH_2 = -1 and EVENT_TIME >= ? and EVENT_TIME <= ?" +
+ " group by DATE, DELIVERY_SUBID";
+
+ private class Counters {
+ private String date;
+ private int sub;
+ private int c100, c200, c300, c400, c500, cm1, cdlx;
+ public Counters(String date, int sub) {
+ this.date = date;
+ this.sub = sub;
+ c100 = c200 = c300 = c400 = c500 = cm1 = cdlx = 0;
+ }
+ public void addCounts(int status, int n) {
+ if (status < 0) {
+ cm1 += n;
+ } else if (status >= 100 && status <= 199) {
+ c100 += n;
+ } else if (status >= 200 && status <= 299) {
+ c200 += n;
+ } else if (status >= 300 && status <= 399) {
+ c300 += n;
+ } else if (status >= 400 && status <= 499) {
+ c400 += n;
+ } else if (status >= 500 && status <= 599) {
+ c500 += n;
+ }
+ }
+ public void addDlxCount(int n) {
+ cdlx += n;
+ }
+ @Override
+ public String toString() {
+ return date + "," + sub + "," +
+ c100 + "," + c200 + "," + c300 + "," + c400 + "," + c500 + "," +
+ cm1 + "," + cdlx;
+ }
+ }
+
+ @Override
+ public void run() {
+ Map<String, Counters> map = new HashMap<String, Counters>();
+ long start = System.currentTimeMillis();
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ PreparedStatement ps = conn.prepareStatement(SELECT_SQL);
+ ps.setLong(1, from);
+ ps.setLong(2, to);
+ ResultSet rs = ps.executeQuery();
+ while (rs.next()) {
+ String date = rs.getString("DATE");
+ int sub = rs.getInt("DELIVERY_SUBID");
+ int res = rs.getInt("RESULT");
+ int count = rs.getInt("COUNT");
+ String key = date + "," + sub;
+ Counters c = map.get(key);
+ if (c == null) {
+ c = new Counters(date, sub);
+ map.put(key, c);
+ }
+ c.addCounts(res, count);
+ }
+ rs.close();
+ ps.close();
+
+ ps = conn.prepareStatement(SELECT_SQL2);
+ ps.setLong(1, from);
+ ps.setLong(2, to);
+ rs = ps.executeQuery();
+ while (rs.next()) {
+ String date = rs.getString("DATE");
+ int sub = rs.getInt("DELIVERY_SUBID");
+ int count = rs.getInt("COUNT");
+ String key = date + "," + sub;
+ Counters c = map.get(key);
+ if (c == null) {
+ c = new Counters(date, sub);
+ map.put(key, c);
+ }
+ c.addDlxCount(count);
+ }
+ rs.close();
+ ps.close();
+
+ db.release(conn);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ logger.debug("Query time: " + (System.currentTimeMillis()-start) + " ms");
+ try {
+ PrintWriter os = new PrintWriter(outfile);
+ os.println("date,subid,count100,count200,count300,count400,count500,countminus1,countdlx");
+ for (String key : new TreeSet<String>(map.keySet())) {
+ Counters c = map.get(key);
+ os.println(c.toString());
+ }
+ os.close();
+ } catch (FileNotFoundException e) {
+ System.err.println("File cannot be written: "+outfile);
+ }
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/reports/VolumeReport.java b/datarouter-prov/src/main/java/com/att/research/datarouter/reports/VolumeReport.java
new file mode 100644
index 00000000..92a85e2e
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/reports/VolumeReport.java
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+
+package com.att.research.datarouter.reports;
+
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeSet;
+
+import com.att.research.datarouter.provisioning.utils.DB;
+
+/**
+ * Generate a traffic volume report. The report is a .csv file containing the following columns:
+ * <table>
+ * <tr><td>date</td><td>the date for this record</td></tr>
+ * <tr><td>feedid</td><td>the Feed ID for this record</td></tr>
+ * <tr><td>filespublished</td><td>the number of files published on this feed and date</td></tr>
+ * <tr><td>bytespublished</td><td>the number of bytes published on this feed and date</td></tr>
+ * <tr><td>filesdelivered</td><td>the number of files delivered on this feed and date</td></tr>
+ * <tr><td>bytesdelivered</td><td>the number of bytes delivered on this feed and date</td></tr>
+ * <tr><td>filesexpired</td><td>the number of files expired on this feed and date</td></tr>
+ * <tr><td>bytesexpired</td><td>the number of bytes expired on this feed and date</td></tr>
+ * </table>
+ *
+ * @author Robert P. Eby
+ * @version $Id: VolumeReport.java,v 1.3 2014/02/28 15:11:13 eby Exp $
+ */
+public class VolumeReport extends ReportBase {
+ private static final String SELECT_SQL = "select EVENT_TIME, TYPE, FEEDID, CONTENT_LENGTH, RESULT" +
+ " from LOG_RECORDS where EVENT_TIME >= ? and EVENT_TIME <= ? LIMIT ?, ?";
+
+ private class Counters {
+ public int filespublished, filesdelivered, filesexpired;
+ public long bytespublished, bytesdelivered, bytesexpired;
+ @Override
+ public String toString() {
+ return String.format("%d,%d,%d,%d,%d,%d",
+ filespublished, bytespublished, filesdelivered,
+ bytesdelivered, filesexpired, bytesexpired);
+ }
+ }
+
+ @Override
+ public void run() {
+ Map<String, Counters> map = new HashMap<String, Counters>();
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+ long start = System.currentTimeMillis();
+ try {
+ DB db = new DB();
+ @SuppressWarnings("resource")
+ Connection conn = db.getConnection();
+ // We need to run this SELECT in stages, because otherwise we run out of memory!
+ final long stepsize = 6000000L;
+ boolean go_again = true;
+ for (long i = 0; go_again; i += stepsize) {
+ PreparedStatement ps = conn.prepareStatement(SELECT_SQL);
+ ps.setLong(1, from);
+ ps.setLong(2, to);
+ ps.setLong(3, i);
+ ps.setLong(4, stepsize);
+ ResultSet rs = ps.executeQuery();
+ go_again = false;
+ while (rs.next()) {
+ go_again = true;
+ long etime = rs.getLong("EVENT_TIME");
+ String type = rs.getString("TYPE");
+ int feed = rs.getInt("FEEDID");
+ long clen = rs.getLong("CONTENT_LENGTH");
+ String key = sdf.format(new Date(etime)) + ":" + feed;
+ Counters c = map.get(key);
+ if (c == null) {
+ c = new Counters();
+ map.put(key, c);
+ }
+ if (type.equalsIgnoreCase("pub")) {
+ c.filespublished++;
+ c.bytespublished += clen;
+ } else if (type.equalsIgnoreCase("del")) {
+ // Only count successful deliveries
+ int statusCode = rs.getInt("RESULT");
+ if (statusCode >= 200 && statusCode < 300) {
+ c.filesdelivered++;
+ c.bytesdelivered += clen;
+ }
+ } else if (type.equalsIgnoreCase("exp")) {
+ c.filesexpired++;
+ c.bytesexpired += clen;
+ }
+ }
+ rs.close();
+ ps.close();
+ }
+ db.release(conn);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ logger.debug("Query time: " + (System.currentTimeMillis()-start) + " ms");
+ try {
+ PrintWriter os = new PrintWriter(outfile);
+ os.println("date,feedid,filespublished,bytespublished,filesdelivered,bytesdelivered,filesexpired,bytesexpired");
+ for (String key : new TreeSet<String>(map.keySet())) {
+ Counters c = map.get(key);
+ String[] p = key.split(":");
+ os.println(String.format("%s,%s,%s", p[0], p[1], c.toString()));
+ }
+ os.close();
+ } catch (FileNotFoundException e) {
+ System.err.println("File cannot be written: "+outfile);
+ }
+ }
+}
diff --git a/datarouter-prov/src/main/java/com/att/research/datarouter/reports/package.html b/datarouter-prov/src/main/java/com/att/research/datarouter/reports/package.html
new file mode 100644
index 00000000..2c2d26b3
--- /dev/null
+++ b/datarouter-prov/src/main/java/com/att/research/datarouter/reports/package.html
@@ -0,0 +1,43 @@
+#-------------------------------------------------------------------------------
+# ============LICENSE_START==================================================
+# * org.onap.dmaap
+# * ===========================================================================
+# * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+# * ===========================================================================
+# * 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.
+# * ============LICENSE_END====================================================
+# *
+# * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+# *
+#-------------------------------------------------------------------------------
+
+<html>
+<body>
+<p>
+This package provides various classes which are used to generate .CSV files from the logs
+in the database.
+The .CSV files can then be used to generate reports on another web server external from the DR network.
+</p>
+<p>
+The classes in this package, and the reports they generate are:
+</p>
+<table>
+<tr><th>Class</th><th>Report</th></tr>
+<tr><td>{@link com.att.research.datarouter.reports.DailyLatencyReport}</td><td>dailylatency.csv</td></tr>
+<tr><td>{@link com.att.research.datarouter.reports.FeedReport}</td><td>NOT CURRENTLY USED</td></tr>
+<tr><td>{@link com.att.research.datarouter.reports.LatencyReport}</td><td></td></tr>
+<tr><td>{@link com.att.research.datarouter.reports.SubscriberReport}</td><td>subscriber.csv</td></tr>
+<tr><td>{@link com.att.research.datarouter.reports.VolumeReport}</td><td>volumes.csv</td></tr>
+</table>
+</body>
+</html>
diff --git a/datarouter-prov/src/main/java/org/json/CDL.java b/datarouter-prov/src/main/java/org/json/CDL.java
new file mode 100644
index 00000000..7e489a9c
--- /dev/null
+++ b/datarouter-prov/src/main/java/org/json/CDL.java
@@ -0,0 +1,301 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package org.json;
+
+/*
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+/**
+ * This provides static methods to convert comma delimited text into a
+ * JSONArray, and to covert a JSONArray into comma delimited text. Comma
+ * delimited text is a very popular format for data interchange. It is
+ * understood by most database, spreadsheet, and organizer programs.
+ * <p>
+ * Each row of text represents a row in a table or a data record. Each row
+ * ends with a NEWLINE character. Each row contains one or more values.
+ * Values are separated by commas. A value can contain any character except
+ * for comma, unless is is wrapped in single quotes or double quotes.
+ * <p>
+ * The first row usually contains the names of the columns.
+ * <p>
+ * A comma delimited list can be converted into a JSONArray of JSONObjects.
+ * The names for the elements in the JSONObjects can be taken from the names
+ * in the first row.
+ * @author JSON.org
+ * @version 2012-11-13
+ */
+public class CDL {
+
+ /**
+ * Get the next value. The value can be wrapped in quotes. The value can
+ * be empty.
+ * @param x A JSONTokener of the source text.
+ * @return The value string, or null if empty.
+ * @throws JSONException if the quoted string is badly formed.
+ */
+ private static String getValue(JSONTokener x) throws JSONException {
+ char c;
+ char q;
+ StringBuffer sb;
+ do {
+ c = x.next();
+ } while (c == ' ' || c == '\t');
+ switch (c) {
+ case 0:
+ return null;
+ case '"':
+ case '\'':
+ q = c;
+ sb = new StringBuffer();
+ for (;;) {
+ c = x.next();
+ if (c == q) {
+ break;
+ }
+ if (c == 0 || c == '\n' || c == '\r') {
+ throw x.syntaxError("Missing close quote '" + q + "'.");
+ }
+ sb.append(c);
+ }
+ return sb.toString();
+ case ',':
+ x.back();
+ return "";
+ default:
+ x.back();
+ return x.nextTo(',');
+ }
+ }
+
+ /**
+ * Produce a JSONArray of strings from a row of comma delimited values.
+ * @param x A JSONTokener of the source text.
+ * @return A JSONArray of strings.
+ * @throws JSONException
+ */
+ public static JSONArray rowToJSONArray(JSONTokener x) throws JSONException {
+ JSONArray ja = new JSONArray();
+ for (;;) {
+ String value = getValue(x);
+ char c = x.next();
+ if (value == null ||
+ (ja.length() == 0 && value.length() == 0 && c != ',')) {
+ return null;
+ }
+ ja.put(value);
+ for (;;) {
+ if (c == ',') {
+ break;
+ }
+ if (c != ' ') {
+ if (c == '\n' || c == '\r' || c == 0) {
+ return ja;
+ }
+ throw x.syntaxError("Bad character '" + c + "' (" +
+ (int)c + ").");
+ }
+ c = x.next();
+ }
+ }
+ }
+
+ /**
+ * Produce a JSONObject from a row of comma delimited text, using a
+ * parallel JSONArray of strings to provides the names of the elements.
+ * @param names A JSONArray of names. This is commonly obtained from the
+ * first row of a comma delimited text file using the rowToJSONArray
+ * method.
+ * @param x A JSONTokener of the source text.
+ * @return A JSONObject combining the names and values.
+ * @throws JSONException
+ */
+ public static JSONObject rowToJSONObject(JSONArray names, JSONTokener x)
+ throws JSONException {
+ JSONArray ja = rowToJSONArray(x);
+ return ja != null ? ja.toJSONObject(names) : null;
+ }
+
+ /**
+ * Produce a comma delimited text row from a JSONArray. Values containing
+ * the comma character will be quoted. Troublesome characters may be
+ * removed.
+ * @param ja A JSONArray of strings.
+ * @return A string ending in NEWLINE.
+ */
+ public static String rowToString(JSONArray ja) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < ja.length(); i += 1) {
+ if (i > 0) {
+ sb.append(',');
+ }
+ Object object = ja.opt(i);
+ if (object != null) {
+ String string = object.toString();
+ if (string.length() > 0 && (string.indexOf(',') >= 0 ||
+ string.indexOf('\n') >= 0 || string.indexOf('\r') >= 0 ||
+ string.indexOf(0) >= 0 || string.charAt(0) == '"')) {
+ sb.append('"');
+ int length = string.length();
+ for (int j = 0; j < length; j += 1) {
+ char c = string.charAt(j);
+ if (c >= ' ' && c != '"') {
+ sb.append(c);
+ }
+ }
+ sb.append('"');
+ } else {
+ sb.append(string);
+ }
+ }
+ }
+ sb.append('\n');
+ return sb.toString();
+ }
+
+ /**
+ * Produce a JSONArray of JSONObjects from a comma delimited text string,
+ * using the first row as a source of names.
+ * @param string The comma delimited text.
+ * @return A JSONArray of JSONObjects.
+ * @throws JSONException
+ */
+ public static JSONArray toJSONArray(String string) throws JSONException {
+ return toJSONArray(new JSONTokener(string));
+ }
+
+ /**
+ * Produce a JSONArray of JSONObjects from a comma delimited text string,
+ * using the first row as a source of names.
+ * @param x The JSONTokener containing the comma delimited text.
+ * @return A JSONArray of JSONObjects.
+ * @throws JSONException
+ */
+ public static JSONArray toJSONArray(JSONTokener x) throws JSONException {
+ return toJSONArray(rowToJSONArray(x), x);
+ }
+
+ /**
+ * Produce a JSONArray of JSONObjects from a comma delimited text string
+ * using a supplied JSONArray as the source of element names.
+ * @param names A JSONArray of strings.
+ * @param string The comma delimited text.
+ * @return A JSONArray of JSONObjects.
+ * @throws JSONException
+ */
+ public static JSONArray toJSONArray(JSONArray names, String string)
+ throws JSONException {
+ return toJSONArray(names, new JSONTokener(string));
+ }
+
+ /**
+ * Produce a JSONArray of JSONObjects from a comma delimited text string
+ * using a supplied JSONArray as the source of element names.
+ * @param names A JSONArray of strings.
+ * @param x A JSONTokener of the source text.
+ * @return A JSONArray of JSONObjects.
+ * @throws JSONException
+ */
+ public static JSONArray toJSONArray(JSONArray names, JSONTokener x)
+ throws JSONException {
+ if (names == null || names.length() == 0) {
+ return null;
+ }
+ JSONArray ja = new JSONArray();
+ for (;;) {
+ JSONObject jo = rowToJSONObject(names, x);
+ if (jo == null) {
+ break;
+ }
+ ja.put(jo);
+ }
+ if (ja.length() == 0) {
+ return null;
+ }
+ return ja;
+ }
+
+
+ /**
+ * Produce a comma delimited text from a JSONArray of JSONObjects. The
+ * first row will be a list of names obtained by inspecting the first
+ * JSONObject.
+ * @param ja A JSONArray of JSONObjects.
+ * @return A comma delimited text.
+ * @throws JSONException
+ */
+ public static String toString(JSONArray ja) throws JSONException {
+ JSONObject jo = ja.optJSONObject(0);
+ if (jo != null) {
+ JSONArray names = jo.names();
+ if (names != null) {
+ return rowToString(names) + toString(names, ja);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Produce a comma delimited text from a JSONArray of JSONObjects using
+ * a provided list of names. The list of names is not included in the
+ * output.
+ * @param names A JSONArray of strings.
+ * @param ja A JSONArray of JSONObjects.
+ * @return A comma delimited text.
+ * @throws JSONException
+ */
+ public static String toString(JSONArray names, JSONArray ja)
+ throws JSONException {
+ if (names == null || names.length() == 0) {
+ return null;
+ }
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < ja.length(); i += 1) {
+ JSONObject jo = ja.optJSONObject(i);
+ if (jo != null) {
+ sb.append(rowToString(jo.toJSONArray(names)));
+ }
+ }
+ return sb.toString();
+ }
+}
diff --git a/datarouter-prov/src/main/java/org/json/Cookie.java b/datarouter-prov/src/main/java/org/json/Cookie.java
new file mode 100644
index 00000000..67e4f170
--- /dev/null
+++ b/datarouter-prov/src/main/java/org/json/Cookie.java
@@ -0,0 +1,191 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package org.json;
+
+/*
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+/**
+ * Convert a web browser cookie specification to a JSONObject and back.
+ * JSON and Cookies are both notations for name/value pairs.
+ * @author JSON.org
+ * @version 2010-12-24
+ */
+public class Cookie {
+
+ /**
+ * Produce a copy of a string in which the characters '+', '%', '=', ';'
+ * and control characters are replaced with "%hh". This is a gentle form
+ * of URL encoding, attempting to cause as little distortion to the
+ * string as possible. The characters '=' and ';' are meta characters in
+ * cookies. By convention, they are escaped using the URL-encoding. This is
+ * only a convention, not a standard. Often, cookies are expected to have
+ * encoded values. We encode '=' and ';' because we must. We encode '%' and
+ * '+' because they are meta characters in URL encoding.
+ * @param string The source string.
+ * @return The escaped result.
+ */
+ public static String escape(String string) {
+ char c;
+ String s = string.trim();
+ StringBuffer sb = new StringBuffer();
+ int length = s.length();
+ for (int i = 0; i < length; i += 1) {
+ c = s.charAt(i);
+ if (c < ' ' || c == '+' || c == '%' || c == '=' || c == ';') {
+ sb.append('%');
+ sb.append(Character.forDigit((char)((c >>> 4) & 0x0f), 16));
+ sb.append(Character.forDigit((char)(c & 0x0f), 16));
+ } else {
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+
+
+ /**
+ * Convert a cookie specification string into a JSONObject. The string
+ * will contain a name value pair separated by '='. The name and the value
+ * will be unescaped, possibly converting '+' and '%' sequences. The
+ * cookie properties may follow, separated by ';', also represented as
+ * name=value (except the secure property, which does not have a value).
+ * The name will be stored under the key "name", and the value will be
+ * stored under the key "value". This method does not do checking or
+ * validation of the parameters. It only converts the cookie string into
+ * a JSONObject.
+ * @param string The cookie specification string.
+ * @return A JSONObject containing "name", "value", and possibly other
+ * members.
+ * @throws JSONException
+ */
+ public static JSONObject toJSONObject(String string) throws JSONException {
+ String name;
+ JSONObject jo = new JSONObject();
+ Object value;
+ JSONTokener x = new JSONTokener(string);
+ jo.put("name", x.nextTo('='));
+ x.next('=');
+ jo.put("value", x.nextTo(';'));
+ x.next();
+ while (x.more()) {
+ name = unescape(x.nextTo("=;"));
+ if (x.next() != '=') {
+ if (name.equals("secure")) {
+ value = Boolean.TRUE;
+ } else {
+ throw x.syntaxError("Missing '=' in cookie parameter.");
+ }
+ } else {
+ value = unescape(x.nextTo(';'));
+ x.next();
+ }
+ jo.put(name, value);
+ }
+ return jo;
+ }
+
+
+ /**
+ * Convert a JSONObject into a cookie specification string. The JSONObject
+ * must contain "name" and "value" members.
+ * If the JSONObject contains "expires", "domain", "path", or "secure"
+ * members, they will be appended to the cookie specification string.
+ * All other members are ignored.
+ * @param jo A JSONObject
+ * @return A cookie specification string
+ * @throws JSONException
+ */
+ public static String toString(JSONObject jo) throws JSONException {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append(escape(jo.getString("name")));
+ sb.append("=");
+ sb.append(escape(jo.getString("value")));
+ if (jo.has("expires")) {
+ sb.append(";expires=");
+ sb.append(jo.getString("expires"));
+ }
+ if (jo.has("domain")) {
+ sb.append(";domain=");
+ sb.append(escape(jo.getString("domain")));
+ }
+ if (jo.has("path")) {
+ sb.append(";path=");
+ sb.append(escape(jo.getString("path")));
+ }
+ if (jo.optBoolean("secure")) {
+ sb.append(";secure");
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Convert <code>%</code><i>hh</i> sequences to single characters, and
+ * convert plus to space.
+ * @param string A string that may contain
+ * <code>+</code>&nbsp;<small>(plus)</small> and
+ * <code>%</code><i>hh</i> sequences.
+ * @return The unescaped string.
+ */
+ public static String unescape(String string) {
+ int length = string.length();
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < length; ++i) {
+ char c = string.charAt(i);
+ if (c == '+') {
+ c = ' ';
+ } else if (c == '%' && i + 2 < length) {
+ int d = JSONTokener.dehexchar(string.charAt(i + 1));
+ int e = JSONTokener.dehexchar(string.charAt(i + 2));
+ if (d >= 0 && e >= 0) {
+ c = (char)(d * 16 + e);
+ i += 2;
+ }
+ }
+ sb.append(c);
+ }
+ return sb.toString();
+ }
+}
diff --git a/datarouter-prov/src/main/java/org/json/CookieList.java b/datarouter-prov/src/main/java/org/json/CookieList.java
new file mode 100644
index 00000000..89b78167
--- /dev/null
+++ b/datarouter-prov/src/main/java/org/json/CookieList.java
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package org.json;
+
+/*
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+import java.util.Iterator;
+
+/**
+ * Convert a web browser cookie list string to a JSONObject and back.
+ * @author JSON.org
+ * @version 2010-12-24
+ */
+public class CookieList {
+
+ /**
+ * Convert a cookie list into a JSONObject. A cookie list is a sequence
+ * of name/value pairs. The names are separated from the values by '='.
+ * The pairs are separated by ';'. The names and the values
+ * will be unescaped, possibly converting '+' and '%' sequences.
+ *
+ * To add a cookie to a cooklist,
+ * cookielistJSONObject.put(cookieJSONObject.getString("name"),
+ * cookieJSONObject.getString("value"));
+ * @param string A cookie list string
+ * @return A JSONObject
+ * @throws JSONException
+ */
+ public static JSONObject toJSONObject(String string) throws JSONException {
+ JSONObject jo = new JSONObject();
+ JSONTokener x = new JSONTokener(string);
+ while (x.more()) {
+ String name = Cookie.unescape(x.nextTo('='));
+ x.next('=');
+ jo.put(name, Cookie.unescape(x.nextTo(';')));
+ x.next();
+ }
+ return jo;
+ }
+
+
+ /**
+ * Convert a JSONObject into a cookie list. A cookie list is a sequence
+ * of name/value pairs. The names are separated from the values by '='.
+ * The pairs are separated by ';'. The characters '%', '+', '=', and ';'
+ * in the names and values are replaced by "%hh".
+ * @param jo A JSONObject
+ * @return A cookie list string
+ * @throws JSONException
+ */
+ public static String toString(JSONObject jo) throws JSONException {
+ boolean b = false;
+ Iterator<String> keys = jo.keys();
+ String string;
+ StringBuffer sb = new StringBuffer();
+ while (keys.hasNext()) {
+ string = keys.next().toString();
+ if (!jo.isNull(string)) {
+ if (b) {
+ sb.append(';');
+ }
+ sb.append(Cookie.escape(string));
+ sb.append("=");
+ sb.append(Cookie.escape(jo.getString(string)));
+ b = true;
+ }
+ }
+ return sb.toString();
+ }
+}
diff --git a/datarouter-prov/src/main/java/org/json/HTTP.java b/datarouter-prov/src/main/java/org/json/HTTP.java
new file mode 100644
index 00000000..34ad3f55
--- /dev/null
+++ b/datarouter-prov/src/main/java/org/json/HTTP.java
@@ -0,0 +1,185 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package org.json;
+
+/*
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+import java.util.Iterator;
+
+/**
+ * Convert an HTTP header to a JSONObject and back.
+ * @author JSON.org
+ * @version 2010-12-24
+ */
+public class HTTP {
+
+ /** Carriage return/line feed. */
+ public static final String CRLF = "\r\n";
+
+ /**
+ * Convert an HTTP header string into a JSONObject. It can be a request
+ * header or a response header. A request header will contain
+ * <pre>{
+ * Method: "POST" (for example),
+ * "Request-URI": "/" (for example),
+ * "HTTP-Version": "HTTP/1.1" (for example)
+ * }</pre>
+ * A response header will contain
+ * <pre>{
+ * "HTTP-Version": "HTTP/1.1" (for example),
+ * "Status-Code": "200" (for example),
+ * "Reason-Phrase": "OK" (for example)
+ * }</pre>
+ * In addition, the other parameters in the header will be captured, using
+ * the HTTP field names as JSON names, so that <pre>
+ * Date: Sun, 26 May 2002 18:06:04 GMT
+ * Cookie: Q=q2=PPEAsg--; B=677gi6ouf29bn&b=2&f=s
+ * Cache-Control: no-cache</pre>
+ * become
+ * <pre>{...
+ * Date: "Sun, 26 May 2002 18:06:04 GMT",
+ * Cookie: "Q=q2=PPEAsg--; B=677gi6ouf29bn&b=2&f=s",
+ * "Cache-Control": "no-cache",
+ * ...}</pre>
+ * It does no further checking or conversion. It does not parse dates.
+ * It does not do '%' transforms on URLs.
+ * @param string An HTTP header string.
+ * @return A JSONObject containing the elements and attributes
+ * of the XML string.
+ * @throws JSONException
+ */
+ public static JSONObject toJSONObject(String string) throws JSONException {
+ JSONObject jo = new JSONObject();
+ HTTPTokener x = new HTTPTokener(string);
+ String token;
+
+ token = x.nextToken();
+ if (token.toUpperCase().startsWith("HTTP")) {
+
+// Response
+
+ jo.put("HTTP-Version", token);
+ jo.put("Status-Code", x.nextToken());
+ jo.put("Reason-Phrase", x.nextTo('\0'));
+ x.next();
+
+ } else {
+
+// Request
+
+ jo.put("Method", token);
+ jo.put("Request-URI", x.nextToken());
+ jo.put("HTTP-Version", x.nextToken());
+ }
+
+// Fields
+
+ while (x.more()) {
+ String name = x.nextTo(':');
+ x.next(':');
+ jo.put(name, x.nextTo('\0'));
+ x.next();
+ }
+ return jo;
+ }
+
+
+ /**
+ * Convert a JSONObject into an HTTP header. A request header must contain
+ * <pre>{
+ * Method: "POST" (for example),
+ * "Request-URI": "/" (for example),
+ * "HTTP-Version": "HTTP/1.1" (for example)
+ * }</pre>
+ * A response header must contain
+ * <pre>{
+ * "HTTP-Version": "HTTP/1.1" (for example),
+ * "Status-Code": "200" (for example),
+ * "Reason-Phrase": "OK" (for example)
+ * }</pre>
+ * Any other members of the JSONObject will be output as HTTP fields.
+ * The result will end with two CRLF pairs.
+ * @param jo A JSONObject
+ * @return An HTTP header string.
+ * @throws JSONException if the object does not contain enough
+ * information.
+ */
+ public static String toString(JSONObject jo) throws JSONException {
+ Iterator<String> keys = jo.keys();
+ String string;
+ StringBuffer sb = new StringBuffer();
+ if (jo.has("Status-Code") && jo.has("Reason-Phrase")) {
+ sb.append(jo.getString("HTTP-Version"));
+ sb.append(' ');
+ sb.append(jo.getString("Status-Code"));
+ sb.append(' ');
+ sb.append(jo.getString("Reason-Phrase"));
+ } else if (jo.has("Method") && jo.has("Request-URI")) {
+ sb.append(jo.getString("Method"));
+ sb.append(' ');
+ sb.append('"');
+ sb.append(jo.getString("Request-URI"));
+ sb.append('"');
+ sb.append(' ');
+ sb.append(jo.getString("HTTP-Version"));
+ } else {
+ throw new JSONException("Not enough material for an HTTP header.");
+ }
+ sb.append(CRLF);
+ while (keys.hasNext()) {
+ string = keys.next().toString();
+ if (!"HTTP-Version".equals(string) && !"Status-Code".equals(string) &&
+ !"Reason-Phrase".equals(string) && !"Method".equals(string) &&
+ !"Request-URI".equals(string) && !jo.isNull(string)) {
+ sb.append(string);
+ sb.append(": ");
+ sb.append(jo.getString(string));
+ sb.append(CRLF);
+ }
+ }
+ sb.append(CRLF);
+ return sb.toString();
+ }
+}
diff --git a/datarouter-prov/src/main/java/org/json/HTTPTokener.java b/datarouter-prov/src/main/java/org/json/HTTPTokener.java
new file mode 100644
index 00000000..0594e74c
--- /dev/null
+++ b/datarouter-prov/src/main/java/org/json/HTTPTokener.java
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package org.json;
+
+/*
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+/**
+ * The HTTPTokener extends the JSONTokener to provide additional methods
+ * for the parsing of HTTP headers.
+ * @author JSON.org
+ * @version 2012-11-13
+ */
+public class HTTPTokener extends JSONTokener {
+
+ /**
+ * Construct an HTTPTokener from a string.
+ * @param string A source string.
+ */
+ public HTTPTokener(String string) {
+ super(string);
+ }
+
+
+ /**
+ * Get the next token or string. This is used in parsing HTTP headers.
+ * @throws JSONException
+ * @return A String.
+ */
+ public String nextToken() throws JSONException {
+ char c;
+ char q;
+ StringBuffer sb = new StringBuffer();
+ do {
+ c = next();
+ } while (Character.isWhitespace(c));
+ if (c == '"' || c == '\'') {
+ q = c;
+ for (;;) {
+ c = next();
+ if (c < ' ') {
+ throw syntaxError("Unterminated string.");
+ }
+ if (c == q) {
+ return sb.toString();
+ }
+ sb.append(c);
+ }
+ }
+ for (;;) {
+ if (c == 0 || Character.isWhitespace(c)) {
+ return sb.toString();
+ }
+ sb.append(c);
+ c = next();
+ }
+ }
+}
diff --git a/datarouter-prov/src/main/java/org/json/JSONArray.java b/datarouter-prov/src/main/java/org/json/JSONArray.java
new file mode 100644
index 00000000..c9e7c423
--- /dev/null
+++ b/datarouter-prov/src/main/java/org/json/JSONArray.java
@@ -0,0 +1,970 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package org.json;
+
+/*
+ Copyright (c) 2002 JSON.org
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ The Software shall be used for Good, not Evil.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A JSONArray is an ordered sequence of values. Its external text form is a
+ * string wrapped in square brackets with commas separating the values. The
+ * internal form is an object having <code>get</code> and <code>opt</code>
+ * methods for accessing the values by index, and <code>put</code> methods for
+ * adding or replacing values. The values can be any of these types:
+ * <code>Boolean</code>, <code>JSONArray</code>, <code>JSONObject</code>,
+ * <code>Number</code>, <code>String</code>, or the
+ * <code>JSONObject.NULL object</code>.
+ * <p>
+ * The constructor can convert a JSON text into a Java object. The
+ * <code>toString</code> method converts to JSON text.
+ * <p>
+ * A <code>get</code> method returns a value if one can be found, and throws an
+ * exception if one cannot be found. An <code>opt</code> method returns a
+ * default value instead of throwing an exception, and so is useful for
+ * obtaining optional values.
+ * <p>
+ * The generic <code>get()</code> and <code>opt()</code> methods return an
+ * object which you can cast or query for type. There are also typed
+ * <code>get</code> and <code>opt</code> methods that do type checking and type
+ * coercion for you.
+ * <p>
+ * The texts produced by the <code>toString</code> methods strictly conform to
+ * JSON syntax rules. The constructors are more forgiving in the texts they will
+ * accept:
+ * <ul>
+ * <li>An extra <code>,</code>&nbsp;<small>(comma)</small> may appear just
+ * before the closing bracket.</li>
+ * <li>The <code>null</code> value will be inserted when there is <code>,</code>
+ * &nbsp;<small>(comma)</small> elision.</li>
+ * <li>Strings may be quoted with <code>'</code>&nbsp;<small>(single
+ * quote)</small>.</li>
+ * <li>Strings do not need to be quoted at all if they do not begin with a quote
+ * or single quote, and if they do not contain leading or trailing spaces, and
+ * if they do not contain any of these characters:
+ * <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers and
+ * if they are not the reserved words <code>true</code>, <code>false</code>, or
+ * <code>null</code>.</li>
+ * <li>Values can be separated by <code>;</code> <small>(semicolon)</small> as
+ * well as by <code>,</code> <small>(comma)</small>.</li>
+ * </ul>
+ *
+ * @author JSON.org
+ * @version 2012-11-13
+ */
+public class JSONArray {
+
+ /**
+ * The arrayList where the JSONArray's properties are kept.
+ */
+ private final List<Object> myArrayList;
+
+ /**
+ * Construct an empty JSONArray.
+ */
+ public JSONArray() {
+ this.myArrayList = new ArrayList<Object>();
+ }
+
+ /**
+ * Construct a JSONArray from a JSONTokener.
+ *
+ * @param x
+ * A JSONTokener
+ * @throws JSONException
+ * If there is a syntax error.
+ */
+ public JSONArray(JSONTokener x) throws JSONException {
+ this();
+ if (x.nextClean() != '[') {
+ throw x.syntaxError("A JSONArray text must start with '['");
+ }
+ if (x.nextClean() != ']') {
+ x.back();
+ for (;;) {
+ if (x.nextClean() == ',') {
+ x.back();
+ this.myArrayList.add(JSONObject.NULL);
+ } else {
+ x.back();
+ this.myArrayList.add(x.nextValue());
+ }
+ switch (x.nextClean()) {
+ case ';':
+ case ',':
+ if (x.nextClean() == ']') {
+ return;
+ }
+ x.back();
+ break;
+ case ']':
+ return;
+ default:
+ throw x.syntaxError("Expected a ',' or ']'");
+ }
+ }
+ }
+ }
+
+ /**
+ * Construct a JSONArray from a source JSON text.
+ *
+ * @param source
+ * A string that begins with <code>[</code>&nbsp;<small>(left
+ * bracket)</small> and ends with <code>]</code>
+ * &nbsp;<small>(right bracket)</small>.
+ * @throws JSONException
+ * If there is a syntax error.
+ */
+ public JSONArray(String source) throws JSONException {
+ this(new JSONTokener(source));
+ }
+
+ /**
+ * Construct a JSONArray from a Collection.
+ *
+ * @param collection
+ * A Collection.
+ */
+ public JSONArray(Collection<Object> collection) {
+ this.myArrayList = new ArrayList<Object>();
+ if (collection != null) {
+ Iterator<Object> iter = collection.iterator();
+ while (iter.hasNext()) {
+ this.myArrayList.add(JSONObject.wrap(iter.next()));
+ }
+ }
+ }
+
+ /**
+ * Construct a JSONArray from an array
+ *
+ * @throws JSONException
+ * If not an array.
+ */
+ public JSONArray(Object array) throws JSONException {
+ this();
+ if (array.getClass().isArray()) {
+ int length = Array.getLength(array);
+ for (int i = 0; i < length; i += 1) {
+ this.put(JSONObject.wrap(Array.get(array, i)));
+ }
+ } else {
+ throw new JSONException(
+ "JSONArray initial value should be a string or collection or array.");
+ }
+ }
+
+ /**
+ * Get the object value associated with an index.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return An object value.
+ * @throws JSONException
+ * If there is no value for the index.
+ */
+ public Object get(int index) throws JSONException {
+ Object object = this.opt(index);
+ if (object == null) {
+ throw new JSONException("JSONArray[" + index + "] not found.");
+ }
+ return object;
+ }
+
+ /**
+ * Get the boolean value associated with an index. The string values "true"
+ * and "false" are converted to boolean.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return The truth.
+ * @throws JSONException
+ * If there is no value for the index or if the value is not
+ * convertible to boolean.
+ */
+ public boolean getBoolean(int index) throws JSONException {
+ Object object = this.get(index);
+ if (object.equals(Boolean.FALSE)
+ || (object instanceof String && ((String) object)
+ .equalsIgnoreCase("false"))) {
+ return false;
+ } else if (object.equals(Boolean.TRUE)
+ || (object instanceof String && ((String) object)
+ .equalsIgnoreCase("true"))) {
+ return true;
+ }
+ throw new JSONException("JSONArray[" + index + "] is not a boolean.");
+ }
+
+ /**
+ * Get the double value associated with an index.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return The value.
+ * @throws JSONException
+ * If the key is not found or if the value cannot be converted
+ * to a number.
+ */
+ public double getDouble(int index) throws JSONException {
+ Object object = this.get(index);
+ try {
+ return object instanceof Number ? ((Number) object).doubleValue()
+ : Double.parseDouble((String) object);
+ } catch (Exception e) {
+ throw new JSONException("JSONArray[" + index + "] is not a number.");
+ }
+ }
+
+ /**
+ * Get the int value associated with an index.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return The value.
+ * @throws JSONException
+ * If the key is not found or if the value is not a number.
+ */
+ public int getInt(int index) throws JSONException {
+ Object object = this.get(index);
+ try {
+ return object instanceof Number ? ((Number) object).intValue()
+ : Integer.parseInt((String) object);
+ } catch (Exception e) {
+ throw new JSONException("JSONArray[" + index + "] is not a number.");
+ }
+ }
+
+ /**
+ * Get the JSONArray associated with an index.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return A JSONArray value.
+ * @throws JSONException
+ * If there is no value for the index. or if the value is not a
+ * JSONArray
+ */
+ public JSONArray getJSONArray(int index) throws JSONException {
+ Object object = this.get(index);
+ if (object instanceof JSONArray) {
+ return (JSONArray) object;
+ }
+ throw new JSONException("JSONArray[" + index + "] is not a JSONArray.");
+ }
+
+ /**
+ * Get the JSONObject associated with an index.
+ *
+ * @param index
+ * subscript
+ * @return A JSONObject value.
+ * @throws JSONException
+ * If there is no value for the index or if the value is not a
+ * JSONObject
+ */
+ public JSONObject getJSONObject(int index) throws JSONException {
+ Object object = this.get(index);
+ if (object instanceof JSONObject) {
+ return (JSONObject) object;
+ }
+ throw new JSONException("JSONArray[" + index + "] is not a JSONObject.");
+ }
+
+ /**
+ * Get the long value associated with an index.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return The value.
+ * @throws JSONException
+ * If the key is not found or if the value cannot be converted
+ * to a number.
+ */
+ public long getLong(int index) throws JSONException {
+ Object object = this.get(index);
+ try {
+ return object instanceof Number ? ((Number) object).longValue()
+ : Long.parseLong((String) object);
+ } catch (Exception e) {
+ throw new JSONException("JSONArray[" + index + "] is not a number.");
+ }
+ }
+
+ /**
+ * Get the string associated with an index.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return A string value.
+ * @throws JSONException
+ * If there is no string value for the index.
+ */
+ public String getString(int index) throws JSONException {
+ Object object = this.get(index);
+ if (object instanceof String) {
+ return (String) object;
+ }
+ throw new JSONException("JSONArray[" + index + "] not a string.");
+ }
+
+ /**
+ * Determine if the value is null.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return true if the value at the index is null, or if there is no value.
+ */
+ public boolean isNull(int index) {
+ return JSONObject.NULL.equals(this.opt(index));
+ }
+
+ /**
+ * Make a string from the contents of this JSONArray. The
+ * <code>separator</code> string is inserted between each element. Warning:
+ * This method assumes that the data structure is acyclical.
+ *
+ * @param separator
+ * A string that will be inserted between the elements.
+ * @return a string.
+ * @throws JSONException
+ * If the array contains an invalid number.
+ */
+ public String join(String separator) throws JSONException {
+ int len = this.length();
+ StringBuffer sb = new StringBuffer();
+
+ for (int i = 0; i < len; i += 1) {
+ if (i > 0) {
+ sb.append(separator);
+ }
+ sb.append(JSONObject.valueToString(this.myArrayList.get(i)));
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Get the number of elements in the JSONArray, included nulls.
+ *
+ * @return The length (or size).
+ */
+ public int length() {
+ return this.myArrayList.size();
+ }
+
+ /**
+ * Get the optional object value associated with an index.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return An object value, or null if there is no object at that index.
+ */
+ public Object opt(int index) {
+ return (index < 0 || index >= this.length()) ? null : this.myArrayList
+ .get(index);
+ }
+
+ /**
+ * Get the optional boolean value associated with an index. It returns false
+ * if there is no value at that index, or if the value is not Boolean.TRUE
+ * or the String "true".
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return The truth.
+ */
+ public boolean optBoolean(int index) {
+ return this.optBoolean(index, false);
+ }
+
+ /**
+ * Get the optional boolean value associated with an index. It returns the
+ * defaultValue if there is no value at that index or if it is not a Boolean
+ * or the String "true" or "false" (case insensitive).
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @param defaultValue
+ * A boolean default.
+ * @return The truth.
+ */
+ public boolean optBoolean(int index, boolean defaultValue) {
+ try {
+ return this.getBoolean(index);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Get the optional double value associated with an index. NaN is returned
+ * if there is no value for the index, or if the value is not a number and
+ * cannot be converted to a number.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return The value.
+ */
+ public double optDouble(int index) {
+ return this.optDouble(index, Double.NaN);
+ }
+
+ /**
+ * Get the optional double value associated with an index. The defaultValue
+ * is returned if there is no value for the index, or if the value is not a
+ * number and cannot be converted to a number.
+ *
+ * @param index
+ * subscript
+ * @param defaultValue
+ * The default value.
+ * @return The value.
+ */
+ public double optDouble(int index, double defaultValue) {
+ try {
+ return this.getDouble(index);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Get the optional int value associated with an index. Zero is returned if
+ * there is no value for the index, or if the value is not a number and
+ * cannot be converted to a number.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return The value.
+ */
+ public int optInt(int index) {
+ return this.optInt(index, 0);
+ }
+
+ /**
+ * Get the optional int value associated with an index. The defaultValue is
+ * returned if there is no value for the index, or if the value is not a
+ * number and cannot be converted to a number.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @param defaultValue
+ * The default value.
+ * @return The value.
+ */
+ public int optInt(int index, int defaultValue) {
+ try {
+ return this.getInt(index);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Get the optional JSONArray associated with an index.
+ *
+ * @param index
+ * subscript
+ * @return A JSONArray value, or null if the index has no value, or if the
+ * value is not a JSONArray.
+ */
+ public JSONArray optJSONArray(int index) {
+ Object o = this.opt(index);
+ return o instanceof JSONArray ? (JSONArray) o : null;
+ }
+
+ /**
+ * Get the optional JSONObject associated with an index. Null is returned if
+ * the key is not found, or null if the index has no value, or if the value
+ * is not a JSONObject.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return A JSONObject value.
+ */
+ public JSONObject optJSONObject(int index) {
+ Object o = this.opt(index);
+ return o instanceof JSONObject ? (JSONObject) o : null;
+ }
+
+ /**
+ * Get the optional long value associated with an index. Zero is returned if
+ * there is no value for the index, or if the value is not a number and
+ * cannot be converted to a number.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return The value.
+ */
+ public long optLong(int index) {
+ return this.optLong(index, 0);
+ }
+
+ /**
+ * Get the optional long value associated with an index. The defaultValue is
+ * returned if there is no value for the index, or if the value is not a
+ * number and cannot be converted to a number.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @param defaultValue
+ * The default value.
+ * @return The value.
+ */
+ public long optLong(int index, long defaultValue) {
+ try {
+ return this.getLong(index);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Get the optional string value associated with an index. It returns an
+ * empty string if there is no value at that index. If the value is not a
+ * string and is not null, then it is coverted to a string.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @return A String value.
+ */
+ public String optString(int index) {
+ return this.optString(index, "");
+ }
+
+ /**
+ * Get the optional string associated with an index. The defaultValue is
+ * returned if the key is not found.
+ *
+ * @param index
+ * The index must be between 0 and length() - 1.
+ * @param defaultValue
+ * The default value.
+ * @return A String value.
+ */
+ public String optString(int index, String defaultValue) {
+ Object object = this.opt(index);
+ return JSONObject.NULL.equals(object) ? defaultValue : object
+ .toString();
+ }
+
+ /**
+ * Append a boolean value. This increases the array's length by one.
+ *
+ * @param value
+ * A boolean value.
+ * @return this.
+ */
+ public JSONArray put(boolean value) {
+ this.put(value ? Boolean.TRUE : Boolean.FALSE);
+ return this;
+ }
+
+ /**
+ * Put a value in the JSONArray, where the value will be a JSONArray which
+ * is produced from a Collection.
+ *
+ * @param value
+ * A Collection value.
+ * @return this.
+ */
+ public JSONArray put(Collection<Object> value) {
+ this.put(new JSONArray(value));
+ return this;
+ }
+
+ /**
+ * Append a double value. This increases the array's length by one.
+ *
+ * @param value
+ * A double value.
+ * @throws JSONException
+ * if the value is not finite.
+ * @return this.
+ */
+ public JSONArray put(double value) throws JSONException {
+ Double d = new Double(value);
+ JSONObject.testValidity(d);
+ this.put(d);
+ return this;
+ }
+
+ /**
+ * Append an int value. This increases the array's length by one.
+ *
+ * @param value
+ * An int value.
+ * @return this.
+ */
+ public JSONArray put(int value) {
+ this.put(new Integer(value));
+ return this;
+ }
+
+ /**
+ * Append an long value. This increases the array's length by one.
+ *
+ * @param value
+ * A long value.
+ * @return this.
+ */
+ public JSONArray put(long value) {
+ this.put(new Long(value));
+ return this;
+ }
+
+ /**
+ * Put a value in the JSONArray, where the value will be a JSONObject which
+ * is produced from a Map.
+ *
+ * @param value
+ * A Map value.
+ * @return this.
+ */
+ public JSONArray put(Map<String,Object> value) {
+ this.put(new JSONObject(value));
+ return this;
+ }
+
+ /**
+ * Append an object value. This increases the array's length by one.
+ *
+ * @param value
+ * An object value. The value should be a Boolean, Double,
+ * Integer, JSONArray, JSONObject, Long, or String, or the
+ * JSONObject.NULL object.
+ * @return this.
+ */
+ public JSONArray put(Object value) {
+ this.myArrayList.add(value);
+ return this;
+ }
+
+ /**
+ * Put or replace a boolean value in the JSONArray. If the index is greater
+ * than the length of the JSONArray, then null elements will be added as
+ * necessary to pad it out.
+ *
+ * @param index
+ * The subscript.
+ * @param value
+ * A boolean value.
+ * @return this.
+ * @throws JSONException
+ * If the index is negative.
+ */
+ public JSONArray put(int index, boolean value) throws JSONException {
+ this.put(index, value ? Boolean.TRUE : Boolean.FALSE);
+ return this;
+ }
+
+ /**
+ * Put a value in the JSONArray, where the value will be a JSONArray which
+ * is produced from a Collection.
+ *
+ * @param index
+ * The subscript.
+ * @param value
+ * A Collection value.
+ * @return this.
+ * @throws JSONException
+ * If the index is negative or if the value is not finite.
+ */
+ public JSONArray put(int index, Collection<Object> value) throws JSONException {
+ this.put(index, new JSONArray(value));
+ return this;
+ }
+
+ /**
+ * Put or replace a double value. If the index is greater than the length of
+ * the JSONArray, then null elements will be added as necessary to pad it
+ * out.
+ *
+ * @param index
+ * The subscript.
+ * @param value
+ * A double value.
+ * @return this.
+ * @throws JSONException
+ * If the index is negative or if the value is not finite.
+ */
+ public JSONArray put(int index, double value) throws JSONException {
+ this.put(index, new Double(value));
+ return this;
+ }
+
+ /**
+ * Put or replace an int value. If the index is greater than the length of
+ * the JSONArray, then null elements will be added as necessary to pad it
+ * out.
+ *
+ * @param index
+ * The subscript.
+ * @param value
+ * An int value.
+ * @return this.
+ * @throws JSONException
+ * If the index is negative.
+ */
+ public JSONArray put(int index, int value) throws JSONException {
+ this.put(index, new Integer(value));
+ return this;
+ }
+
+ /**
+ * Put or replace a long value. If the index is greater than the length of
+ * the JSONArray, then null elements will be added as necessary to pad it
+ * out.
+ *
+ * @param index
+ * The subscript.
+ * @param value
+ * A long value.
+ * @return this.
+ * @throws JSONException
+ * If the index is negative.
+ */
+ public JSONArray put(int index, long value) throws JSONException {
+ this.put(index, new Long(value));
+ return this;
+ }
+
+ /**
+ * Put a value in the JSONArray, where the value will be a JSONObject that
+ * is produced from a Map.
+ *
+ * @param index
+ * The subscript.
+ * @param value
+ * The Map value.
+ * @return this.
+ * @throws JSONException
+ * If the index is negative or if the the value is an invalid
+ * number.
+ */
+ public JSONArray put(int index, Map<String,Object> value) throws JSONException {
+ this.put(index, new JSONObject(value));
+ return this;
+ }
+
+ /**
+ * Put or replace an object value in the JSONArray. If the index is greater
+ * than the length of the JSONArray, then null elements will be added as
+ * necessary to pad it out.
+ *
+ * @param index
+ * The subscript.
+ * @param value
+ * The value to put into the array. The value should be a
+ * Boolean, Double, Integer, JSONArray, JSONObject, Long, or
+ * String, or the JSONObject.NULL object.
+ * @return this.
+ * @throws JSONException
+ * If the index is negative or if the the value is an invalid
+ * number.
+ */
+ public JSONArray put(int index, Object value) throws JSONException {
+ JSONObject.testValidity(value);
+ if (index < 0) {
+ throw new JSONException("JSONArray[" + index + "] not found.");
+ }
+ if (index < this.length()) {
+ this.myArrayList.set(index, value);
+ } else {
+ while (index != this.length()) {
+ this.put(JSONObject.NULL);
+ }
+ this.put(value);
+ }
+ return this;
+ }
+
+ /**
+ * Remove an index and close the hole.
+ *
+ * @param index
+ * The index of the element to be removed.
+ * @return The value that was associated with the index, or null if there
+ * was no value.
+ */
+ public Object remove(int index) {
+ Object o = this.opt(index);
+ this.myArrayList.remove(index);
+ return o;
+ }
+
+ /**
+ * Produce a JSONObject by combining a JSONArray of names with the values of
+ * this JSONArray.
+ *
+ * @param names
+ * A JSONArray containing a list of key strings. These will be
+ * paired with the values.
+ * @return A JSONObject, or null if there are no names or if this JSONArray
+ * has no values.
+ * @throws JSONException
+ * If any of the names are null.
+ */
+ public JSONObject toJSONObject(JSONArray names) throws JSONException {
+ if (names == null || names.length() == 0 || this.length() == 0) {
+ return null;
+ }
+ JSONObject jo = new JSONObject();
+ for (int i = 0; i < names.length(); i += 1) {
+ jo.put(names.getString(i), this.opt(i));
+ }
+ return jo;
+ }
+
+ /**
+ * Make a JSON text of this JSONArray. For compactness, no unnecessary
+ * whitespace is added. If it is not possible to produce a syntactically
+ * correct JSON text then null will be returned instead. This could occur if
+ * the array contains an invalid number.
+ * <p>
+ * Warning: This method assumes that the data structure is acyclical.
+ *
+ * @return a printable, displayable, transmittable representation of the
+ * array.
+ */
+ public String toString() {
+ try {
+ return this.toString(0);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * Make a prettyprinted JSON text of this JSONArray. Warning: This method
+ * assumes that the data structure is acyclical.
+ *
+ * @param indentFactor
+ * The number of spaces to add to each level of indentation.
+ * @return a printable, displayable, transmittable representation of the
+ * object, beginning with <code>[</code>&nbsp;<small>(left
+ * bracket)</small> and ending with <code>]</code>
+ * &nbsp;<small>(right bracket)</small>.
+ * @throws JSONException
+ */
+ public String toString(int indentFactor) throws JSONException {
+ StringWriter sw = new StringWriter();
+ synchronized (sw.getBuffer()) {
+ return this.write(sw, indentFactor, 0).toString();
+ }
+ }
+
+ /**
+ * Write the contents of the JSONArray as JSON text to a writer. For
+ * compactness, no whitespace is added.
+ * <p>
+ * Warning: This method assumes that the data structure is acyclical.
+ *
+ * @return The writer.
+ * @throws JSONException
+ */
+ public Writer write(Writer writer) throws JSONException {
+ return this.write(writer, 0, 0);
+ }
+
+ /**
+ * Write the contents of the JSONArray as JSON text to a writer. For
+ * compactness, no whitespace is added.
+ * <p>
+ * Warning: This method assumes that the data structure is acyclical.
+ *
+ * @param indentFactor
+ * The number of spaces to add to each level of indentation.
+ * @param indent
+ * The indention of the top level.
+ * @return The writer.
+ * @throws JSONException
+ */
+ Writer write(Writer writer, int indentFactor, int indent)
+ throws JSONException {
+ try {
+ boolean commanate = false;
+ int length = this.length();
+ writer.write('[');
+
+ if (length == 1) {
+ JSONObject.writeValue(writer, this.myArrayList.get(0),
+ indentFactor, indent);
+ } else if (length != 0) {
+ final int newindent = indent + indentFactor;
+
+ for (int i = 0; i < length; i += 1) {
+ if (commanate) {
+ writer.write(',');
+ }
+ if (indentFactor > 0) {
+ writer.write('\n');
+ }
+ JSONObject.indent(writer, newindent);
+ JSONObject.writeValue(writer, this.myArrayList.get(i),
+ indentFactor, newindent);
+ commanate = true;
+ }
+ if (indentFactor > 0) {
+ writer.write('\n');
+ }
+ JSONObject.indent(writer, indent);
+ }
+ writer.write(']');
+ return writer;
+ } catch (IOException e) {
+ throw new JSONException(e);
+ }
+ }
+}
diff --git a/datarouter-prov/src/main/java/org/json/JSONException.java b/datarouter-prov/src/main/java/org/json/JSONException.java
new file mode 100644
index 00000000..2308eb25
--- /dev/null
+++ b/datarouter-prov/src/main/java/org/json/JSONException.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package org.json;
+
+/**
+ * The JSONException is thrown by the JSON.org classes when things are amiss.
+ *
+ * @author JSON.org
+ * @version 2013-02-10
+ */
+public class JSONException extends RuntimeException {
+ private static final long serialVersionUID = 0;
+ private Throwable cause;
+
+ /**
+ * Constructs a JSONException with an explanatory message.
+ *
+ * @param message
+ * Detail about the reason for the exception.
+ */
+ public JSONException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new JSONException with the specified cause.
+ */
+ public JSONException(Throwable cause) {
+ super(cause.getMessage());
+ this.cause = cause;
+ }
+
+ /**
+ * Returns the cause of this exception or null if the cause is nonexistent
+ * or unknown.
+ *
+ * @return the cause of this exception or null if the cause is nonexistent
+ * or unknown.
+ */
+ public Throwable getCause() {
+ return this.cause;
+ }
+}
diff --git a/datarouter-prov/src/main/java/org/json/JSONML.java b/datarouter-prov/src/main/java/org/json/JSONML.java
new file mode 100644
index 00000000..5afb599e
--- /dev/null
+++ b/datarouter-prov/src/main/java/org/json/JSONML.java
@@ -0,0 +1,489 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package org.json;
+
+/*
+Copyright (c) 2008 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+import java.util.Iterator;
+
+
+/**
+ * This provides static methods to convert an XML text into a JSONArray or
+ * JSONObject, and to covert a JSONArray or JSONObject into an XML text using
+ * the JsonML transform.
+ *
+ * @author JSON.org
+ * @version 2012-03-28
+ */
+public class JSONML {
+
+ /**
+ * Parse XML values and store them in a JSONArray.
+ * @param x The XMLTokener containing the source string.
+ * @param arrayForm true if array form, false if object form.
+ * @param ja The JSONArray that is containing the current tag or null
+ * if we are at the outermost level.
+ * @return A JSONArray if the value is the outermost tag, otherwise null.
+ * @throws JSONException
+ */
+ private static Object parse(
+ XMLTokener x,
+ boolean arrayForm,
+ JSONArray ja
+ ) throws JSONException {
+ String attribute;
+ char c;
+ String closeTag = null;
+ int i;
+ JSONArray newja = null;
+ JSONObject newjo = null;
+ Object token;
+ String tagName = null;
+
+// Test for and skip past these forms:
+// <!-- ... -->
+// <![ ... ]]>
+// <! ... >
+// <? ... ?>
+
+ while (true) {
+ if (!x.more()) {
+ throw x.syntaxError("Bad XML");
+ }
+ token = x.nextContent();
+ if (token == XML.LT) {
+ token = x.nextToken();
+ if (token instanceof Character) {
+ if (token == XML.SLASH) {
+
+// Close tag </
+
+ token = x.nextToken();
+ if (!(token instanceof String)) {
+ throw new JSONException(
+ "Expected a closing name instead of '" +
+ token + "'.");
+ }
+ if (x.nextToken() != XML.GT) {
+ throw x.syntaxError("Misshaped close tag");
+ }
+ return token;
+ } else if (token == XML.BANG) {
+
+// <!
+
+ c = x.next();
+ if (c == '-') {
+ if (x.next() == '-') {
+ x.skipPast("-->");
+ } else {
+ x.back();
+ }
+ } else if (c == '[') {
+ token = x.nextToken();
+ if (token.equals("CDATA") && x.next() == '[') {
+ if (ja != null) {
+ ja.put(x.nextCDATA());
+ }
+ } else {
+ throw x.syntaxError("Expected 'CDATA['");
+ }
+ } else {
+ i = 1;
+ do {
+ token = x.nextMeta();
+ if (token == null) {
+ throw x.syntaxError("Missing '>' after '<!'.");
+ } else if (token == XML.LT) {
+ i += 1;
+ } else if (token == XML.GT) {
+ i -= 1;
+ }
+ } while (i > 0);
+ }
+ } else if (token == XML.QUEST) {
+
+// <?
+
+ x.skipPast("?>");
+ } else {
+ throw x.syntaxError("Misshaped tag");
+ }
+
+// Open tag <
+
+ } else {
+ if (!(token instanceof String)) {
+ throw x.syntaxError("Bad tagName '" + token + "'.");
+ }
+ tagName = (String)token;
+ newja = new JSONArray();
+ newjo = new JSONObject();
+ if (arrayForm) {
+ newja.put(tagName);
+ if (ja != null) {
+ ja.put(newja);
+ }
+ } else {
+ newjo.put("tagName", tagName);
+ if (ja != null) {
+ ja.put(newjo);
+ }
+ }
+ token = null;
+ for (;;) {
+ if (token == null) {
+ token = x.nextToken();
+ }
+ if (token == null) {
+ throw x.syntaxError("Misshaped tag");
+ }
+ if (!(token instanceof String)) {
+ break;
+ }
+
+// attribute = value
+
+ attribute = (String)token;
+ if (!arrayForm && ("tagName".equals(attribute) || "childNode".equals(attribute))) {
+ throw x.syntaxError("Reserved attribute.");
+ }
+ token = x.nextToken();
+ if (token == XML.EQ) {
+ token = x.nextToken();
+ if (!(token instanceof String)) {
+ throw x.syntaxError("Missing value");
+ }
+ newjo.accumulate(attribute, XML.stringToValue((String)token));
+ token = null;
+ } else {
+ newjo.accumulate(attribute, "");
+ }
+ }
+ if (arrayForm && newjo.length() > 0) {
+ newja.put(newjo);
+ }
+
+// Empty tag <.../>
+
+ if (token == XML.SLASH) {
+ if (x.nextToken() != XML.GT) {
+ throw x.syntaxError("Misshaped tag");
+ }
+ if (ja == null) {
+ if (arrayForm) {
+ return newja;
+ } else {
+ return newjo;
+ }
+ }
+
+// Content, between <...> and </...>
+
+ } else {
+ if (token != XML.GT) {
+ throw x.syntaxError("Misshaped tag");
+ }
+ closeTag = (String)parse(x, arrayForm, newja);
+ if (closeTag != null) {
+ if (!closeTag.equals(tagName)) {
+ throw x.syntaxError("Mismatched '" + tagName +
+ "' and '" + closeTag + "'");
+ }
+ tagName = null;
+ if (!arrayForm && newja.length() > 0) {
+ newjo.put("childNodes", newja);
+ }
+ if (ja == null) {
+ if (arrayForm) {
+ return newja;
+ } else {
+ return newjo;
+ }
+ }
+ }
+ }
+ }
+ } else {
+ if (ja != null) {
+ ja.put(token instanceof String
+ ? XML.stringToValue((String)token)
+ : token);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Convert a well-formed (but not necessarily valid) XML string into a
+ * JSONArray using the JsonML transform. Each XML tag is represented as
+ * a JSONArray in which the first element is the tag name. If the tag has
+ * attributes, then the second element will be JSONObject containing the
+ * name/value pairs. If the tag contains children, then strings and
+ * JSONArrays will represent the child tags.
+ * Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code> are ignored.
+ * @param string The source string.
+ * @return A JSONArray containing the structured data from the XML string.
+ * @throws JSONException
+ */
+ public static JSONArray toJSONArray(String string) throws JSONException {
+ return toJSONArray(new XMLTokener(string));
+ }
+
+
+ /**
+ * Convert a well-formed (but not necessarily valid) XML string into a
+ * JSONArray using the JsonML transform. Each XML tag is represented as
+ * a JSONArray in which the first element is the tag name. If the tag has
+ * attributes, then the second element will be JSONObject containing the
+ * name/value pairs. If the tag contains children, then strings and
+ * JSONArrays will represent the child content and tags.
+ * Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code> are ignored.
+ * @param x An XMLTokener.
+ * @return A JSONArray containing the structured data from the XML string.
+ * @throws JSONException
+ */
+ public static JSONArray toJSONArray(XMLTokener x) throws JSONException {
+ return (JSONArray)parse(x, true, null);
+ }
+
+
+ /**
+ * Convert a well-formed (but not necessarily valid) XML string into a
+ * JSONObject using the JsonML transform. Each XML tag is represented as
+ * a JSONObject with a "tagName" property. If the tag has attributes, then
+ * the attributes will be in the JSONObject as properties. If the tag
+ * contains children, the object will have a "childNodes" property which
+ * will be an array of strings and JsonML JSONObjects.
+
+ * Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code> are ignored.
+ * @param x An XMLTokener of the XML source text.
+ * @return A JSONObject containing the structured data from the XML string.
+ * @throws JSONException
+ */
+ public static JSONObject toJSONObject(XMLTokener x) throws JSONException {
+ return (JSONObject)parse(x, false, null);
+ }
+
+
+ /**
+ * Convert a well-formed (but not necessarily valid) XML string into a
+ * JSONObject using the JsonML transform. Each XML tag is represented as
+ * a JSONObject with a "tagName" property. If the tag has attributes, then
+ * the attributes will be in the JSONObject as properties. If the tag
+ * contains children, the object will have a "childNodes" property which
+ * will be an array of strings and JsonML JSONObjects.
+
+ * Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code> are ignored.
+ * @param string The XML source text.
+ * @return A JSONObject containing the structured data from the XML string.
+ * @throws JSONException
+ */
+ public static JSONObject toJSONObject(String string) throws JSONException {
+ return toJSONObject(new XMLTokener(string));
+ }
+
+
+ /**
+ * Reverse the JSONML transformation, making an XML text from a JSONArray.
+ * @param ja A JSONArray.
+ * @return An XML string.
+ * @throws JSONException
+ */
+ public static String toString(JSONArray ja) throws JSONException {
+ int i;
+ JSONObject jo;
+ String key;
+ Iterator<String> keys;
+ int length;
+ Object object;
+ StringBuffer sb = new StringBuffer();
+ String tagName;
+ String value;
+
+// Emit <tagName
+
+ tagName = ja.getString(0);
+ XML.noSpace(tagName);
+ tagName = XML.escape(tagName);
+ sb.append('<');
+ sb.append(tagName);
+
+ object = ja.opt(1);
+ if (object instanceof JSONObject) {
+ i = 2;
+ jo = (JSONObject)object;
+
+// Emit the attributes
+
+ keys = jo.keys();
+ while (keys.hasNext()) {
+ key = keys.next().toString();
+ XML.noSpace(key);
+ value = jo.optString(key);
+ if (value != null) {
+ sb.append(' ');
+ sb.append(XML.escape(key));
+ sb.append('=');
+ sb.append('"');
+ sb.append(XML.escape(value));
+ sb.append('"');
+ }
+ }
+ } else {
+ i = 1;
+ }
+
+//Emit content in body
+
+ length = ja.length();
+ if (i >= length) {
+ sb.append('/');
+ sb.append('>');
+ } else {
+ sb.append('>');
+ do {
+ object = ja.get(i);
+ i += 1;
+ if (object != null) {
+ if (object instanceof String) {
+ sb.append(XML.escape(object.toString()));
+ } else if (object instanceof JSONObject) {
+ sb.append(toString((JSONObject)object));
+ } else if (object instanceof JSONArray) {
+ sb.append(toString((JSONArray)object));
+ }
+ }
+ } while (i < length);
+ sb.append('<');
+ sb.append('/');
+ sb.append(tagName);
+ sb.append('>');
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Reverse the JSONML transformation, making an XML text from a JSONObject.
+ * The JSONObject must contain a "tagName" property. If it has children,
+ * then it must have a "childNodes" property containing an array of objects.
+ * The other properties are attributes with string values.
+ * @param jo A JSONObject.
+ * @return An XML string.
+ * @throws JSONException
+ */
+ public static String toString(JSONObject jo) throws JSONException {
+ StringBuffer sb = new StringBuffer();
+ int i;
+ JSONArray ja;
+ String key;
+ Iterator<String> keys;
+ int length;
+ Object object;
+ String tagName;
+ String value;
+
+//Emit <tagName
+
+ tagName = jo.optString("tagName");
+ if (tagName == null) {
+ return XML.escape(jo.toString());
+ }
+ XML.noSpace(tagName);
+ tagName = XML.escape(tagName);
+ sb.append('<');
+ sb.append(tagName);
+
+//Emit the attributes
+
+ keys = jo.keys();
+ while (keys.hasNext()) {
+ key = keys.next().toString();
+ if (!"tagName".equals(key) && !"childNodes".equals(key)) {
+ XML.noSpace(key);
+ value = jo.optString(key);
+ if (value != null) {
+ sb.append(' ');
+ sb.append(XML.escape(key));
+ sb.append('=');
+ sb.append('"');
+ sb.append(XML.escape(value));
+ sb.append('"');
+ }
+ }
+ }
+
+//Emit content in body
+
+ ja = jo.optJSONArray("childNodes");
+ if (ja == null) {
+ sb.append('/');
+ sb.append('>');
+ } else {
+ sb.append('>');
+ length = ja.length();
+ for (i = 0; i < length; i += 1) {
+ object = ja.get(i);
+ if (object != null) {
+ if (object instanceof String) {
+ sb.append(XML.escape(object.toString()));
+ } else if (object instanceof JSONObject) {
+ sb.append(toString((JSONObject)object));
+ } else if (object instanceof JSONArray) {
+ sb.append(toString((JSONArray)object));
+ } else {
+ sb.append(object.toString());
+ }
+ }
+ }
+ sb.append('<');
+ sb.append('/');
+ sb.append(tagName);
+ sb.append('>');
+ }
+ return sb.toString();
+ }
+}
diff --git a/datarouter-prov/src/main/java/org/json/JSONObject.java b/datarouter-prov/src/main/java/org/json/JSONObject.java
new file mode 100644
index 00000000..b4b0fe5f
--- /dev/null
+++ b/datarouter-prov/src/main/java/org/json/JSONObject.java
@@ -0,0 +1,1653 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package org.json;
+
+/*
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.Set;
+
+/**
+ * A JSONObject is an unordered collection of name/value pairs. Its external
+ * form is a string wrapped in curly braces with colons between the names and
+ * values, and commas between the values and names. The internal form is an
+ * object having <code>get</code> and <code>opt</code> methods for accessing the
+ * values by name, and <code>put</code> methods for adding or replacing values
+ * by name. The values can be any of these types: <code>Boolean</code>,
+ * <code>JSONArray</code>, <code>JSONObject</code>, <code>Number</code>,
+ * <code>String</code>, or the <code>JSONObject.NULL</code> object. A JSONObject
+ * constructor can be used to convert an external form JSON text into an
+ * internal form whose values can be retrieved with the <code>get</code> and
+ * <code>opt</code> methods, or to convert values into a JSON text using the
+ * <code>put</code> and <code>toString</code> methods. A <code>get</code> method
+ * returns a value if one can be found, and throws an exception if one cannot be
+ * found. An <code>opt</code> method returns a default value instead of throwing
+ * an exception, and so is useful for obtaining optional values.
+ * <p>
+ * The generic <code>get()</code> and <code>opt()</code> methods return an
+ * object, which you can cast or query for type. There are also typed
+ * <code>get</code> and <code>opt</code> methods that do type checking and type
+ * coercion for you. The opt methods differ from the get methods in that they do
+ * not throw. Instead, they return a specified value, such as null.
+ * <p>
+ * The <code>put</code> methods add or replace values in an object. For example,
+ *
+ * <pre>
+ * myString = new JSONObject().put(&quot;JSON&quot;, &quot;Hello, World!&quot;).toString();
+ * </pre>
+ *
+ * produces the string <code>{"JSON": "Hello, World"}</code>.
+ * <p>
+ * The texts produced by the <code>toString</code> methods strictly conform to
+ * the JSON syntax rules. The constructors are more forgiving in the texts they
+ * will accept:
+ * <ul>
+ * <li>An extra <code>,</code>&nbsp;<small>(comma)</small> may appear just
+ * before the closing brace.</li>
+ * <li>Strings may be quoted with <code>'</code>&nbsp;<small>(single
+ * quote)</small>.</li>
+ * <li>Strings do not need to be quoted at all if they do not begin with a quote
+ * or single quote, and if they do not contain leading or trailing spaces, and
+ * if they do not contain any of these characters:
+ * <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers and
+ * if they are not the reserved words <code>true</code>, <code>false</code>, or
+ * <code>null</code>.</li>
+ * <li>Keys can be followed by <code>=</code> or <code>=></code> as well as by
+ * <code>:</code>.</li>
+ * <li>Values can be followed by <code>;</code> <small>(semicolon)</small> as
+ * well as by <code>,</code> <small>(comma)</small>.</li>
+ * </ul>
+ *
+ * @author JSON.org
+ * @version 2012-12-01
+ */
+public class JSONObject {
+ /**
+ * The maximum number of keys in the key pool.
+ */
+ private static final int keyPoolSize = 100;
+
+ /**
+ * Key pooling is like string interning, but without permanently tying up
+ * memory. To help conserve memory, storage of duplicated key strings in
+ * JSONObjects will be avoided by using a key pool to manage unique key
+ * string objects. This is used by JSONObject.put(string, object).
+ */
+ private static Map<String,Object> keyPool = new HashMap<String,Object>(keyPoolSize);
+
+ /**
+ * JSONObject.NULL is equivalent to the value that JavaScript calls null,
+ * whilst Java's null is equivalent to the value that JavaScript calls
+ * undefined.
+ */
+ private static final class Null {
+
+ /**
+ * There is only intended to be a single instance of the NULL object,
+ * so the clone method returns itself.
+ * @return NULL.
+ */
+ protected final Object clone() {
+ return this;
+ }
+
+ /**
+ * A Null object is equal to the null value and to itself.
+ * @param object An object to test for nullness.
+ * @return true if the object parameter is the JSONObject.NULL object
+ * or null.
+ */
+ public boolean equals(Object object) {
+ return object == null || object == this;
+ }
+
+ /**
+ * Get the "null" string value.
+ * @return The string "null".
+ */
+ public String toString() {
+ return "null";
+ }
+ }
+
+
+ /**
+ * The map where the JSONObject's properties are kept.
+ */
+ private final Map<String,Object> map;
+
+
+ /**
+ * It is sometimes more convenient and less ambiguous to have a
+ * <code>NULL</code> object than to use Java's <code>null</code> value.
+ * <code>JSONObject.NULL.equals(null)</code> returns <code>true</code>.
+ * <code>JSONObject.NULL.toString()</code> returns <code>"null"</code>.
+ */
+ public static final Object NULL = new Null();
+
+
+ /**
+ * Construct an empty JSONObject.
+ */
+ public JSONObject() {
+ this.map = new HashMap<String,Object>();
+ }
+
+
+ /**
+ * Construct a JSONObject from a subset of another JSONObject.
+ * An array of strings is used to identify the keys that should be copied.
+ * Missing keys are ignored.
+ * @param jo A JSONObject.
+ * @param names An array of strings.
+ * @throws JSONException
+ * @exception JSONException If a value is a non-finite number or if a name is duplicated.
+ */
+ public JSONObject(JSONObject jo, String[] names) {
+ this();
+ for (int i = 0; i < names.length; i += 1) {
+ try {
+ this.putOnce(names[i], jo.opt(names[i]));
+ } catch (Exception ignore) {
+ }
+ }
+ }
+
+
+ /**
+ * Construct a JSONObject from a JSONTokener.
+ * @param x A JSONTokener object containing the source string.
+ * @throws JSONException If there is a syntax error in the source string
+ * or a duplicated key.
+ */
+ public JSONObject(JSONTokener x) throws JSONException {
+ this();
+ char c;
+ String key;
+
+ if (x.nextClean() != '{') {
+ throw x.syntaxError("A JSONObject text must begin with '{'");
+ }
+ for (;;) {
+ c = x.nextClean();
+ switch (c) {
+ case 0:
+ throw x.syntaxError("A JSONObject text must end with '}'");
+ case '}':
+ return;
+ default:
+ x.back();
+ key = x.nextValue().toString();
+ }
+
+// The key is followed by ':'. We will also tolerate '=' or '=>'.
+
+ c = x.nextClean();
+ if (c == '=') {
+ if (x.next() != '>') {
+ x.back();
+ }
+ } else if (c != ':') {
+ throw x.syntaxError("Expected a ':' after a key");
+ }
+ this.putOnce(key, x.nextValue());
+
+// Pairs are separated by ','. We will also tolerate ';'.
+
+ switch (x.nextClean()) {
+ case ';':
+ case ',':
+ if (x.nextClean() == '}') {
+ return;
+ }
+ x.back();
+ break;
+ case '}':
+ return;
+ default:
+ throw x.syntaxError("Expected a ',' or '}'");
+ }
+ }
+ }
+
+
+ /**
+ * Construct a JSONObject from a Map.
+ *
+ * @param map A map object that can be used to initialize the contents of
+ * the JSONObject.
+ * @throws JSONException
+ */
+ public JSONObject(Map<String,Object> map) {
+ this.map = new HashMap<String,Object>();
+ if (map != null) {
+ Iterator<Map.Entry<String,Object>> i = map.entrySet().iterator();
+ while (i.hasNext()) {
+ Map.Entry<String,Object> e = i.next();
+ Object value = e.getValue();
+ if (value != null) {
+ this.map.put(e.getKey(), wrap(value));
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Construct a JSONObject from an Object using bean getters.
+ * It reflects on all of the public methods of the object.
+ * For each of the methods with no parameters and a name starting
+ * with <code>"get"</code> or <code>"is"</code> followed by an uppercase letter,
+ * the method is invoked, and a key and the value returned from the getter method
+ * are put into the new JSONObject.
+ *
+ * The key is formed by removing the <code>"get"</code> or <code>"is"</code> prefix.
+ * If the second remaining character is not upper case, then the first
+ * character is converted to lower case.
+ *
+ * For example, if an object has a method named <code>"getName"</code>, and
+ * if the result of calling <code>object.getName()</code> is <code>"Larry Fine"</code>,
+ * then the JSONObject will contain <code>"name": "Larry Fine"</code>.
+ *
+ * @param bean An object that has getter methods that should be used
+ * to make a JSONObject.
+ */
+ public JSONObject(Object bean) {
+ this();
+ this.populateMap(bean);
+ }
+
+
+ /**
+ * Construct a JSONObject from an Object, using reflection to find the
+ * public members. The resulting JSONObject's keys will be the strings
+ * from the names array, and the values will be the field values associated
+ * with those keys in the object. If a key is not found or not visible,
+ * then it will not be copied into the new JSONObject.
+ * @param object An object that has fields that should be used to make a
+ * JSONObject.
+ * @param names An array of strings, the names of the fields to be obtained
+ * from the object.
+ */
+ public JSONObject(Object object, String names[]) {
+ this();
+ Class<? extends Object> c = object.getClass();
+ for (int i = 0; i < names.length; i += 1) {
+ String name = names[i];
+ try {
+ this.putOpt(name, c.getField(name).get(object));
+ } catch (Exception ignore) {
+ }
+ }
+ }
+
+
+ /**
+ * Construct a JSONObject from a source JSON text string.
+ * This is the most commonly used JSONObject constructor.
+ * @param source A string beginning
+ * with <code>{</code>&nbsp;<small>(left brace)</small> and ending
+ * with <code>}</code>&nbsp;<small>(right brace)</small>.
+ * @exception JSONException If there is a syntax error in the source
+ * string or a duplicated key.
+ */
+ public JSONObject(String source) throws JSONException {
+ this(new JSONTokener(source));
+ }
+
+
+ /**
+ * Construct a JSONObject from a ResourceBundle.
+ * @param baseName The ResourceBundle base name.
+ * @param locale The Locale to load the ResourceBundle for.
+ * @throws JSONException If any JSONExceptions are detected.
+ */
+ public JSONObject(String baseName, Locale locale) throws JSONException {
+ this();
+ ResourceBundle bundle = ResourceBundle.getBundle(baseName, locale,
+ Thread.currentThread().getContextClassLoader());
+
+// Iterate through the keys in the bundle.
+
+ Enumeration<?> keys = bundle.getKeys();
+ while (keys.hasMoreElements()) {
+ Object key = keys.nextElement();
+ if (key instanceof String) {
+
+// Go through the path, ensuring that there is a nested JSONObject for each
+// segment except the last. Add the value using the last segment's name into
+// the deepest nested JSONObject.
+
+ String[] path = ((String)key).split("\\.");
+ int last = path.length - 1;
+ JSONObject target = this;
+ for (int i = 0; i < last; i += 1) {
+ String segment = path[i];
+ JSONObject nextTarget = target.optJSONObject(segment);
+ if (nextTarget == null) {
+ nextTarget = new JSONObject();
+ target.put(segment, nextTarget);
+ }
+ target = nextTarget;
+ }
+ target.put(path[last], bundle.getString((String)key));
+ }
+ }
+ }
+
+
+ /**
+ * Accumulate values under a key. It is similar to the put method except
+ * that if there is already an object stored under the key then a
+ * JSONArray is stored under the key to hold all of the accumulated values.
+ * If there is already a JSONArray, then the new value is appended to it.
+ * In contrast, the put method replaces the previous value.
+ *
+ * If only one value is accumulated that is not a JSONArray, then the
+ * result will be the same as using put. But if multiple values are
+ * accumulated, then the result will be like append.
+ * @param key A key string.
+ * @param value An object to be accumulated under the key.
+ * @return this.
+ * @throws JSONException If the value is an invalid number
+ * or if the key is null.
+ */
+ public JSONObject accumulate(
+ String key,
+ Object value
+ ) throws JSONException {
+ testValidity(value);
+ Object object = this.opt(key);
+ if (object == null) {
+ this.put(key, value instanceof JSONArray
+ ? new JSONArray().put(value)
+ : value);
+ } else if (object instanceof JSONArray) {
+ ((JSONArray)object).put(value);
+ } else {
+ this.put(key, new JSONArray().put(object).put(value));
+ }
+ return this;
+ }
+
+
+ /**
+ * Append values to the array under a key. If the key does not exist in the
+ * JSONObject, then the key is put in the JSONObject with its value being a
+ * JSONArray containing the value parameter. If the key was already
+ * associated with a JSONArray, then the value parameter is appended to it.
+ * @param key A key string.
+ * @param value An object to be accumulated under the key.
+ * @return this.
+ * @throws JSONException If the key is null or if the current value
+ * associated with the key is not a JSONArray.
+ */
+ public JSONObject append(String key, Object value) throws JSONException {
+ testValidity(value);
+ Object object = this.opt(key);
+ if (object == null) {
+ this.put(key, new JSONArray().put(value));
+ } else if (object instanceof JSONArray) {
+ this.put(key, ((JSONArray)object).put(value));
+ } else {
+ throw new JSONException("JSONObject[" + key +
+ "] is not a JSONArray.");
+ }
+ return this;
+ }
+
+
+ /**
+ * Produce a string from a double. The string "null" will be returned if
+ * the number is not finite.
+ * @param d A double.
+ * @return A String.
+ */
+ public static String doubleToString(double d) {
+ if (Double.isInfinite(d) || Double.isNaN(d)) {
+ return "null";
+ }
+
+// Shave off trailing zeros and decimal point, if possible.
+
+ String string = Double.toString(d);
+ if (string.indexOf('.') > 0 && string.indexOf('e') < 0 &&
+ string.indexOf('E') < 0) {
+ while (string.endsWith("0")) {
+ string = string.substring(0, string.length() - 1);
+ }
+ if (string.endsWith(".")) {
+ string = string.substring(0, string.length() - 1);
+ }
+ }
+ return string;
+ }
+
+
+ /**
+ * Get the value object associated with a key.
+ *
+ * @param key A key string.
+ * @return The object associated with the key.
+ * @throws JSONException if the key is not found.
+ */
+ public Object get(String key) throws JSONException {
+ if (key == null) {
+ throw new JSONException("Null key.");
+ }
+ Object object = this.opt(key);
+ if (object == null) {
+ throw new JSONException("JSONObject[" + quote(key) +
+ "] not found.");
+ }
+ return object;
+ }
+
+
+ /**
+ * Get the boolean value associated with a key.
+ *
+ * @param key A key string.
+ * @return The truth.
+ * @throws JSONException
+ * if the value is not a Boolean or the String "true" or "false".
+ */
+ public boolean getBoolean(String key) throws JSONException {
+ Object object = this.get(key);
+ if (object.equals(Boolean.FALSE) ||
+ (object instanceof String &&
+ ((String)object).equalsIgnoreCase("false"))) {
+ return false;
+ } else if (object.equals(Boolean.TRUE) ||
+ (object instanceof String &&
+ ((String)object).equalsIgnoreCase("true"))) {
+ return true;
+ }
+ throw new JSONException("JSONObject[" + quote(key) +
+ "] is not a Boolean.");
+ }
+
+
+ /**
+ * Get the double value associated with a key.
+ * @param key A key string.
+ * @return The numeric value.
+ * @throws JSONException if the key is not found or
+ * if the value is not a Number object and cannot be converted to a number.
+ */
+ public double getDouble(String key) throws JSONException {
+ Object object = this.get(key);
+ try {
+ return object instanceof Number
+ ? ((Number)object).doubleValue()
+ : Double.parseDouble((String)object);
+ } catch (Exception e) {
+ throw new JSONException("JSONObject[" + quote(key) +
+ "] is not a number.");
+ }
+ }
+
+
+ /**
+ * Get the int value associated with a key.
+ *
+ * @param key A key string.
+ * @return The integer value.
+ * @throws JSONException if the key is not found or if the value cannot
+ * be converted to an integer.
+ */
+ public int getInt(String key) throws JSONException {
+ Object object = this.get(key);
+ try {
+ return object instanceof Number
+ ? ((Number)object).intValue()
+ : Integer.parseInt((String)object);
+ } catch (Exception e) {
+ throw new JSONException("JSONObject[" + quote(key) +
+ "] is not an int.");
+ }
+ }
+
+
+ /**
+ * Get the JSONArray value associated with a key.
+ *
+ * @param key A key string.
+ * @return A JSONArray which is the value.
+ * @throws JSONException if the key is not found or
+ * if the value is not a JSONArray.
+ */
+ public JSONArray getJSONArray(String key) throws JSONException {
+ Object object = this.get(key);
+ if (object instanceof JSONArray) {
+ return (JSONArray)object;
+ }
+ throw new JSONException("JSONObject[" + quote(key) +
+ "] is not a JSONArray.");
+ }
+
+
+ /**
+ * Get the JSONObject value associated with a key.
+ *
+ * @param key A key string.
+ * @return A JSONObject which is the value.
+ * @throws JSONException if the key is not found or
+ * if the value is not a JSONObject.
+ */
+ public JSONObject getJSONObject(String key) throws JSONException {
+ Object object = this.get(key);
+ if (object instanceof JSONObject) {
+ return (JSONObject)object;
+ }
+ throw new JSONException("JSONObject[" + quote(key) +
+ "] is not a JSONObject.");
+ }
+
+
+ /**
+ * Get the long value associated with a key.
+ *
+ * @param key A key string.
+ * @return The long value.
+ * @throws JSONException if the key is not found or if the value cannot
+ * be converted to a long.
+ */
+ public long getLong(String key) throws JSONException {
+ Object object = this.get(key);
+ try {
+ return object instanceof Number
+ ? ((Number)object).longValue()
+ : Long.parseLong((String)object);
+ } catch (Exception e) {
+ throw new JSONException("JSONObject[" + quote(key) +
+ "] is not a long.");
+ }
+ }
+
+
+ /**
+ * Get an array of field names from a JSONObject.
+ *
+ * @return An array of field names, or null if there are no names.
+ */
+ public static String[] getNames(JSONObject jo) {
+ int length = jo.length();
+ if (length == 0) {
+ return null;
+ }
+ Iterator<String> iterator = jo.keys();
+ String[] names = new String[length];
+ int i = 0;
+ while (iterator.hasNext()) {
+ names[i] = iterator.next();
+ i += 1;
+ }
+ return names;
+ }
+
+
+ /**
+ * Get an array of field names from an Object.
+ *
+ * @return An array of field names, or null if there are no names.
+ */
+ public static String[] getNames(Object object) {
+ if (object == null) {
+ return null;
+ }
+ Class<? extends Object> klass = object.getClass();
+ Field[] fields = klass.getFields();
+ int length = fields.length;
+ if (length == 0) {
+ return null;
+ }
+ String[] names = new String[length];
+ for (int i = 0; i < length; i += 1) {
+ names[i] = fields[i].getName();
+ }
+ return names;
+ }
+
+
+ /**
+ * Get the string associated with a key.
+ *
+ * @param key A key string.
+ * @return A string which is the value.
+ * @throws JSONException if there is no string value for the key.
+ */
+ public String getString(String key) throws JSONException {
+ Object object = this.get(key);
+ if (object instanceof String) {
+ return (String)object;
+ }
+ throw new JSONException("JSONObject[" + quote(key) +
+ "] not a string.");
+ }
+
+
+ /**
+ * Determine if the JSONObject contains a specific key.
+ * @param key A key string.
+ * @return true if the key exists in the JSONObject.
+ */
+ public boolean has(String key) {
+ return this.map.containsKey(key);
+ }
+
+
+ /**
+ * Increment a property of a JSONObject. If there is no such property,
+ * create one with a value of 1. If there is such a property, and if
+ * it is an Integer, Long, Double, or Float, then add one to it.
+ * @param key A key string.
+ * @return this.
+ * @throws JSONException If there is already a property with this name
+ * that is not an Integer, Long, Double, or Float.
+ */
+ public JSONObject increment(String key) throws JSONException {
+ Object value = this.opt(key);
+ if (value == null) {
+ this.put(key, 1);
+ } else if (value instanceof Integer) {
+ this.put(key, ((Integer)value).intValue() + 1);
+ } else if (value instanceof Long) {
+ this.put(key, ((Long)value).longValue() + 1);
+ } else if (value instanceof Double) {
+ this.put(key, ((Double)value).doubleValue() + 1);
+ } else if (value instanceof Float) {
+ this.put(key, ((Float)value).floatValue() + 1);
+ } else {
+ throw new JSONException("Unable to increment [" + quote(key) + "].");
+ }
+ return this;
+ }
+
+
+ /**
+ * Determine if the value associated with the key is null or if there is
+ * no value.
+ * @param key A key string.
+ * @return true if there is no value associated with the key or if
+ * the value is the JSONObject.NULL object.
+ */
+ public boolean isNull(String key) {
+ return JSONObject.NULL.equals(this.opt(key));
+ }
+
+
+ /**
+ * Get an enumeration of the keys of the JSONObject.
+ *
+ * @return An iterator of the keys.
+ */
+ public Iterator<String> keys() {
+ return this.keySet().iterator();
+ }
+
+
+ /**
+ * Get a set of keys of the JSONObject.
+ *
+ * @return A keySet.
+ */
+ public Set<String> keySet() {
+ return this.map.keySet();
+ }
+
+
+ /**
+ * Get the number of keys stored in the JSONObject.
+ *
+ * @return The number of keys in the JSONObject.
+ */
+ public int length() {
+ return this.map.size();
+ }
+
+
+ /**
+ * Produce a JSONArray containing the names of the elements of this
+ * JSONObject.
+ * @return A JSONArray containing the key strings, or null if the JSONObject
+ * is empty.
+ */
+ public JSONArray names() {
+ JSONArray ja = new JSONArray();
+ Iterator<String> keys = this.keys();
+ while (keys.hasNext()) {
+ ja.put(keys.next());
+ }
+ return ja.length() == 0 ? null : ja;
+ }
+
+ /**
+ * Produce a string from a Number.
+ * @param number A Number
+ * @return A String.
+ * @throws JSONException If n is a non-finite number.
+ */
+ public static String numberToString(Number number)
+ throws JSONException {
+ if (number == null) {
+ throw new JSONException("Null pointer");
+ }
+ testValidity(number);
+
+// Shave off trailing zeros and decimal point, if possible.
+
+ String string = number.toString();
+ if (string.indexOf('.') > 0 && string.indexOf('e') < 0 &&
+ string.indexOf('E') < 0) {
+ while (string.endsWith("0")) {
+ string = string.substring(0, string.length() - 1);
+ }
+ if (string.endsWith(".")) {
+ string = string.substring(0, string.length() - 1);
+ }
+ }
+ return string;
+ }
+
+
+ /**
+ * Get an optional value associated with a key.
+ * @param key A key string.
+ * @return An object which is the value, or null if there is no value.
+ */
+ public Object opt(String key) {
+ return key == null ? null : this.map.get(key);
+ }
+
+
+ /**
+ * Get an optional boolean associated with a key.
+ * It returns false if there is no such key, or if the value is not
+ * Boolean.TRUE or the String "true".
+ *
+ * @param key A key string.
+ * @return The truth.
+ */
+ public boolean optBoolean(String key) {
+ return this.optBoolean(key, false);
+ }
+
+
+ /**
+ * Get an optional boolean associated with a key.
+ * It returns the defaultValue if there is no such key, or if it is not
+ * a Boolean or the String "true" or "false" (case insensitive).
+ *
+ * @param key A key string.
+ * @param defaultValue The default.
+ * @return The truth.
+ */
+ public boolean optBoolean(String key, boolean defaultValue) {
+ try {
+ return this.getBoolean(key);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+
+ /**
+ * Get an optional double associated with a key,
+ * or NaN if there is no such key or if its value is not a number.
+ * If the value is a string, an attempt will be made to evaluate it as
+ * a number.
+ *
+ * @param key A string which is the key.
+ * @return An object which is the value.
+ */
+ public double optDouble(String key) {
+ return this.optDouble(key, Double.NaN);
+ }
+
+
+ /**
+ * Get an optional double associated with a key, or the
+ * defaultValue if there is no such key or if its value is not a number.
+ * If the value is a string, an attempt will be made to evaluate it as
+ * a number.
+ *
+ * @param key A key string.
+ * @param defaultValue The default.
+ * @return An object which is the value.
+ */
+ public double optDouble(String key, double defaultValue) {
+ try {
+ return this.getDouble(key);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+
+ /**
+ * Get an optional int value associated with a key,
+ * or zero if there is no such key or if the value is not a number.
+ * If the value is a string, an attempt will be made to evaluate it as
+ * a number.
+ *
+ * @param key A key string.
+ * @return An object which is the value.
+ */
+ public int optInt(String key) {
+ return this.optInt(key, 0);
+ }
+
+
+ /**
+ * Get an optional int value associated with a key,
+ * or the default if there is no such key or if the value is not a number.
+ * If the value is a string, an attempt will be made to evaluate it as
+ * a number.
+ *
+ * @param key A key string.
+ * @param defaultValue The default.
+ * @return An object which is the value.
+ */
+ public int optInt(String key, int defaultValue) {
+ try {
+ return this.getInt(key);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+
+ /**
+ * Get an optional JSONArray associated with a key.
+ * It returns null if there is no such key, or if its value is not a
+ * JSONArray.
+ *
+ * @param key A key string.
+ * @return A JSONArray which is the value.
+ */
+ public JSONArray optJSONArray(String key) {
+ Object o = this.opt(key);
+ return o instanceof JSONArray ? (JSONArray)o : null;
+ }
+
+
+ /**
+ * Get an optional JSONObject associated with a key.
+ * It returns null if there is no such key, or if its value is not a
+ * JSONObject.
+ *
+ * @param key A key string.
+ * @return A JSONObject which is the value.
+ */
+ public JSONObject optJSONObject(String key) {
+ Object object = this.opt(key);
+ return object instanceof JSONObject ? (JSONObject)object : null;
+ }
+
+
+ /**
+ * Get an optional long value associated with a key,
+ * or zero if there is no such key or if the value is not a number.
+ * If the value is a string, an attempt will be made to evaluate it as
+ * a number.
+ *
+ * @param key A key string.
+ * @return An object which is the value.
+ */
+ public long optLong(String key) {
+ return this.optLong(key, 0);
+ }
+
+
+ /**
+ * Get an optional long value associated with a key,
+ * or the default if there is no such key or if the value is not a number.
+ * If the value is a string, an attempt will be made to evaluate it as
+ * a number.
+ *
+ * @param key A key string.
+ * @param defaultValue The default.
+ * @return An object which is the value.
+ */
+ public long optLong(String key, long defaultValue) {
+ try {
+ return this.getLong(key);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+
+ /**
+ * Get an optional string associated with a key.
+ * It returns an empty string if there is no such key. If the value is not
+ * a string and is not null, then it is converted to a string.
+ *
+ * @param key A key string.
+ * @return A string which is the value.
+ */
+ public String optString(String key) {
+ return this.optString(key, "");
+ }
+
+
+ /**
+ * Get an optional string associated with a key.
+ * It returns the defaultValue if there is no such key.
+ *
+ * @param key A key string.
+ * @param defaultValue The default.
+ * @return A string which is the value.
+ */
+ public String optString(String key, String defaultValue) {
+ Object object = this.opt(key);
+ return NULL.equals(object) ? defaultValue : object.toString();
+ }
+
+
+ private void populateMap(Object bean) {
+ Class<? extends Object> klass = bean.getClass();
+
+// If klass is a System class then set includeSuperClass to false.
+
+ boolean includeSuperClass = klass.getClassLoader() != null;
+
+ Method[] methods = includeSuperClass
+ ? klass.getMethods()
+ : klass.getDeclaredMethods();
+ for (int i = 0; i < methods.length; i += 1) {
+ try {
+ Method method = methods[i];
+ if (Modifier.isPublic(method.getModifiers())) {
+ String name = method.getName();
+ String key = "";
+ if (name.startsWith("get")) {
+ if ("getClass".equals(name) ||
+ "getDeclaringClass".equals(name)) {
+ key = "";
+ } else {
+ key = name.substring(3);
+ }
+ } else if (name.startsWith("is")) {
+ key = name.substring(2);
+ }
+ if (key.length() > 0 &&
+ Character.isUpperCase(key.charAt(0)) &&
+ method.getParameterTypes().length == 0) {
+ if (key.length() == 1) {
+ key = key.toLowerCase();
+ } else if (!Character.isUpperCase(key.charAt(1))) {
+ key = key.substring(0, 1).toLowerCase() +
+ key.substring(1);
+ }
+
+ Object result = method.invoke(bean, (Object[])null);
+ if (result != null) {
+ this.map.put(key, wrap(result));
+ }
+ }
+ }
+ } catch (Exception ignore) {
+ }
+ }
+ }
+
+
+ /**
+ * Put a key/boolean pair in the JSONObject.
+ *
+ * @param key A key string.
+ * @param value A boolean which is the value.
+ * @return this.
+ * @throws JSONException If the key is null.
+ */
+ public JSONObject put(String key, boolean value) throws JSONException {
+ this.put(key, value ? Boolean.TRUE : Boolean.FALSE);
+ return this;
+ }
+
+
+ /**
+ * Put a key/value pair in the JSONObject, where the value will be a
+ * JSONArray which is produced from a Collection.
+ * @param key A key string.
+ * @param value A Collection value.
+ * @return this.
+ * @throws JSONException
+ */
+ public JSONObject put(String key, Collection<Object> value) throws JSONException {
+ this.put(key, new JSONArray(value));
+ return this;
+ }
+
+
+ /**
+ * Put a key/double pair in the JSONObject.
+ *
+ * @param key A key string.
+ * @param value A double which is the value.
+ * @return this.
+ * @throws JSONException If the key is null or if the number is invalid.
+ */
+ public JSONObject put(String key, double value) throws JSONException {
+ this.put(key, new Double(value));
+ return this;
+ }
+
+
+ /**
+ * Put a key/int pair in the JSONObject.
+ *
+ * @param key A key string.
+ * @param value An int which is the value.
+ * @return this.
+ * @throws JSONException If the key is null.
+ */
+ public JSONObject put(String key, int value) throws JSONException {
+ this.put(key, new Integer(value));
+ return this;
+ }
+
+
+ /**
+ * Put a key/long pair in the JSONObject.
+ *
+ * @param key A key string.
+ * @param value A long which is the value.
+ * @return this.
+ * @throws JSONException If the key is null.
+ */
+ public JSONObject put(String key, long value) throws JSONException {
+ this.put(key, new Long(value));
+ return this;
+ }
+
+
+ /**
+ * Put a key/value pair in the JSONObject, where the value will be a
+ * JSONObject which is produced from a Map.
+ * @param key A key string.
+ * @param value A Map value.
+ * @return this.
+ * @throws JSONException
+ */
+ public JSONObject put(String key, Map<String, Object> value) throws JSONException {
+ this.put(key, new JSONObject(value));
+ return this;
+ }
+
+
+ /**
+ * Put a key/value pair in the JSONObject. If the value is null,
+ * then the key will be removed from the JSONObject if it is present.
+ * @param key A key string.
+ * @param value An object which is the value. It should be of one of these
+ * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String,
+ * or the JSONObject.NULL object.
+ * @return this.
+ * @throws JSONException If the value is non-finite number
+ * or if the key is null.
+ */
+ public JSONObject put(String key, Object value) throws JSONException {
+ String pooled;
+ if (key == null) {
+ throw new JSONException("Null key.");
+ }
+ if (value != null) {
+ testValidity(value);
+ pooled = (String)keyPool.get(key);
+ if (pooled == null) {
+ if (keyPool.size() >= keyPoolSize) {
+ keyPool = new HashMap<String, Object>(keyPoolSize);
+ }
+ keyPool.put(key, key);
+ } else {
+ key = pooled;
+ }
+ this.map.put(key, value);
+ } else {
+ this.remove(key);
+ }
+ return this;
+ }
+
+
+ /**
+ * Put a key/value pair in the JSONObject, but only if the key and the
+ * value are both non-null, and only if there is not already a member
+ * with that name.
+ * @param key
+ * @param value
+ * @return his.
+ * @throws JSONException if the key is a duplicate
+ */
+ public JSONObject putOnce(String key, Object value) throws JSONException {
+ if (key != null && value != null) {
+ if (this.opt(key) != null) {
+ throw new JSONException("Duplicate key \"" + key + "\"");
+ }
+ this.put(key, value);
+ }
+ return this;
+ }
+
+
+ /**
+ * Put a key/value pair in the JSONObject, but only if the
+ * key and the value are both non-null.
+ * @param key A key string.
+ * @param value An object which is the value. It should be of one of these
+ * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String,
+ * or the JSONObject.NULL object.
+ * @return this.
+ * @throws JSONException If the value is a non-finite number.
+ */
+ public JSONObject putOpt(String key, Object value) throws JSONException {
+ if (key != null && value != null) {
+ this.put(key, value);
+ }
+ return this;
+ }
+
+
+ /**
+ * Produce a string in double quotes with backslash sequences in all the
+ * right places. A backslash will be inserted within </, producing <\/,
+ * allowing JSON text to be delivered in HTML. In JSON text, a string
+ * cannot contain a control character or an unescaped quote or backslash.
+ * @param string A String
+ * @return A String correctly formatted for insertion in a JSON text.
+ */
+ public static String quote(String string) {
+ StringWriter sw = new StringWriter();
+ synchronized (sw.getBuffer()) {
+ try {
+ return quote(string, sw).toString();
+ } catch (IOException ignored) {
+ // will never happen - we are writing to a string writer
+ return "";
+ }
+ }
+ }
+
+ public static Writer quote(String string, Writer w) throws IOException {
+ if (string == null || string.length() == 0) {
+ w.write("\"\"");
+ return w;
+ }
+
+ char b;
+ char c = 0;
+ String hhhh;
+ int i;
+ int len = string.length();
+
+ w.write('"');
+ for (i = 0; i < len; i += 1) {
+ b = c;
+ c = string.charAt(i);
+ switch (c) {
+ case '\\':
+ case '"':
+ w.write('\\');
+ w.write(c);
+ break;
+ case '/':
+ if (b == '<') {
+ w.write('\\');
+ }
+ w.write(c);
+ break;
+ case '\b':
+ w.write("\\b");
+ break;
+ case '\t':
+ w.write("\\t");
+ break;
+ case '\n':
+ w.write("\\n");
+ break;
+ case '\f':
+ w.write("\\f");
+ break;
+ case '\r':
+ w.write("\\r");
+ break;
+ default:
+ if (c < ' ' || (c >= '\u0080' && c < '\u00a0')
+ || (c >= '\u2000' && c < '\u2100')) {
+ w.write("\\u");
+ hhhh = Integer.toHexString(c);
+ w.write("0000", 0, 4 - hhhh.length());
+ w.write(hhhh);
+ } else {
+ w.write(c);
+ }
+ }
+ }
+ w.write('"');
+ return w;
+ }
+
+ /**
+ * Remove a name and its value, if present.
+ * @param key The name to be removed.
+ * @return The value that was associated with the name,
+ * or null if there was no value.
+ */
+ public Object remove(String key) {
+ return this.map.remove(key);
+ }
+
+ /**
+ * Try to convert a string into a number, boolean, or null. If the string
+ * can't be converted, return the string.
+ * @param string A String.
+ * @return A simple JSON value.
+ */
+ public static Object stringToValue(String string) {
+ Double d;
+ if (string.equals("")) {
+ return string;
+ }
+ if (string.equalsIgnoreCase("true")) {
+ return Boolean.TRUE;
+ }
+ if (string.equalsIgnoreCase("false")) {
+ return Boolean.FALSE;
+ }
+ if (string.equalsIgnoreCase("null")) {
+ return JSONObject.NULL;
+ }
+
+ /*
+ * If it might be a number, try converting it.
+ * If a number cannot be produced, then the value will just
+ * be a string. Note that the plus and implied string
+ * conventions are non-standard. A JSON parser may accept
+ * non-JSON forms as long as it accepts all correct JSON forms.
+ */
+
+ char b = string.charAt(0);
+ if ((b >= '0' && b <= '9') || b == '.' || b == '-' || b == '+') {
+ try {
+ if (string.indexOf('.') > -1 ||
+ string.indexOf('e') > -1 || string.indexOf('E') > -1) {
+ d = Double.valueOf(string);
+ if (!d.isInfinite() && !d.isNaN()) {
+ return d;
+ }
+ } else {
+ Long myLong = new Long(string);
+ if (myLong.longValue() == myLong.intValue()) {
+ return new Integer(myLong.intValue());
+ } else {
+ return myLong;
+ }
+ }
+ } catch (Exception ignore) {
+ }
+ }
+ return string;
+ }
+
+
+ /**
+ * Throw an exception if the object is a NaN or infinite number.
+ * @param o The object to test.
+ * @throws JSONException If o is a non-finite number.
+ */
+ public static void testValidity(Object o) throws JSONException {
+ if (o != null) {
+ if (o instanceof Double) {
+ if (((Double)o).isInfinite() || ((Double)o).isNaN()) {
+ throw new JSONException(
+ "JSON does not allow non-finite numbers.");
+ }
+ } else if (o instanceof Float) {
+ if (((Float)o).isInfinite() || ((Float)o).isNaN()) {
+ throw new JSONException(
+ "JSON does not allow non-finite numbers.");
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Produce a JSONArray containing the values of the members of this
+ * JSONObject.
+ * @param names A JSONArray containing a list of key strings. This
+ * determines the sequence of the values in the result.
+ * @return A JSONArray of values.
+ * @throws JSONException If any of the values are non-finite numbers.
+ */
+ public JSONArray toJSONArray(JSONArray names) throws JSONException {
+ if (names == null || names.length() == 0) {
+ return null;
+ }
+ JSONArray ja = new JSONArray();
+ for (int i = 0; i < names.length(); i += 1) {
+ ja.put(this.opt(names.getString(i)));
+ }
+ return ja;
+ }
+
+ /**
+ * Make a JSON text of this JSONObject. For compactness, no whitespace
+ * is added. If this would not result in a syntactically correct JSON text,
+ * then null will be returned instead.
+ * <p>
+ * Warning: This method assumes that the data structure is acyclical.
+ *
+ * @return a printable, displayable, portable, transmittable
+ * representation of the object, beginning
+ * with <code>{</code>&nbsp;<small>(left brace)</small> and ending
+ * with <code>}</code>&nbsp;<small>(right brace)</small>.
+ */
+ public String toString() {
+ try {
+ return this.toString(0);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+
+ /**
+ * Make a prettyprinted JSON text of this JSONObject.
+ * <p>
+ * Warning: This method assumes that the data structure is acyclical.
+ * @param indentFactor The number of spaces to add to each level of
+ * indentation.
+ * @return a printable, displayable, portable, transmittable
+ * representation of the object, beginning
+ * with <code>{</code>&nbsp;<small>(left brace)</small> and ending
+ * with <code>}</code>&nbsp;<small>(right brace)</small>.
+ * @throws JSONException If the object contains an invalid number.
+ */
+ public String toString(int indentFactor) throws JSONException {
+ StringWriter w = new StringWriter();
+ synchronized (w.getBuffer()) {
+ return this.write(w, indentFactor, 0).toString();
+ }
+ }
+
+ /**
+ * Make a JSON text of an Object value. If the object has an
+ * value.toJSONString() method, then that method will be used to produce
+ * the JSON text. The method is required to produce a strictly
+ * conforming text. If the object does not contain a toJSONString
+ * method (which is the most common case), then a text will be
+ * produced by other means. If the value is an array or Collection,
+ * then a JSONArray will be made from it and its toJSONString method
+ * will be called. If the value is a MAP, then a JSONObject will be made
+ * from it and its toJSONString method will be called. Otherwise, the
+ * value's toString method will be called, and the result will be quoted.
+ *
+ * <p>
+ * Warning: This method assumes that the data structure is acyclical.
+ * @param value The value to be serialized.
+ * @return a printable, displayable, transmittable
+ * representation of the object, beginning
+ * with <code>{</code>&nbsp;<small>(left brace)</small> and ending
+ * with <code>}</code>&nbsp;<small>(right brace)</small>.
+ * @throws JSONException If the value is or contains an invalid number.
+ */
+ @SuppressWarnings("unchecked")
+ public static String valueToString(Object value) throws JSONException {
+ if (value == null || value.equals(null)) {
+ return "null";
+ }
+ if (value instanceof JSONString) {
+ Object object;
+ try {
+ object = ((JSONString)value).toJSONString();
+ } catch (Exception e) {
+ throw new JSONException(e);
+ }
+ if (object instanceof String) {
+ return (String)object;
+ }
+ throw new JSONException("Bad value from toJSONString: " + object);
+ }
+ if (value instanceof Number) {
+ return numberToString((Number) value);
+ }
+ if (value instanceof Boolean || value instanceof JSONObject ||
+ value instanceof JSONArray) {
+ return value.toString();
+ }
+ if (value instanceof Map) {
+ return new JSONObject((Map<String, Object>)value).toString();
+ }
+ if (value instanceof Collection) {
+ return new JSONArray((Collection<Object>)value).toString();
+ }
+ if (value.getClass().isArray()) {
+ return new JSONArray(value).toString();
+ }
+ return quote(value.toString());
+ }
+
+ /**
+ * Wrap an object, if necessary. If the object is null, return the NULL
+ * object. If it is an array or collection, wrap it in a JSONArray. If
+ * it is a map, wrap it in a JSONObject. If it is a standard property
+ * (Double, String, et al) then it is already wrapped. Otherwise, if it
+ * comes from one of the java packages, turn it into a string. And if
+ * it doesn't, try to wrap it in a JSONObject. If the wrapping fails,
+ * then null is returned.
+ *
+ * @param object The object to wrap
+ * @return The wrapped value
+ */
+ @SuppressWarnings("unchecked")
+ public static Object wrap(Object object) {
+ try {
+ if (object == null) {
+ return NULL;
+ }
+ if (object instanceof JSONObject || object instanceof JSONArray ||
+ NULL.equals(object) || object instanceof JSONString ||
+ object instanceof Byte || object instanceof Character ||
+ object instanceof Short || object instanceof Integer ||
+ object instanceof Long || object instanceof Boolean ||
+ object instanceof Float || object instanceof Double ||
+ object instanceof String) {
+ return object;
+ }
+
+ if (object instanceof Collection) {
+ return new JSONArray((Collection<Object>)object);
+ }
+ if (object.getClass().isArray()) {
+ return new JSONArray(object);
+ }
+ if (object instanceof Map) {
+ return new JSONObject((Map<String, Object>)object);
+ }
+ Package objectPackage = object.getClass().getPackage();
+ String objectPackageName = objectPackage != null
+ ? objectPackage.getName()
+ : "";
+ if (
+ objectPackageName.startsWith("java.") ||
+ objectPackageName.startsWith("javax.") ||
+ object.getClass().getClassLoader() == null
+ ) {
+ return object.toString();
+ }
+ return new JSONObject(object);
+ } catch(Exception exception) {
+ return null;
+ }
+ }
+
+
+ /**
+ * Write the contents of the JSONObject as JSON text to a writer.
+ * For compactness, no whitespace is added.
+ * <p>
+ * Warning: This method assumes that the data structure is acyclical.
+ *
+ * @return The writer.
+ * @throws JSONException
+ */
+ public Writer write(Writer writer) throws JSONException {
+ return this.write(writer, 0, 0);
+ }
+
+
+ @SuppressWarnings("unchecked")
+ static final Writer writeValue(Writer writer, Object value,
+ int indentFactor, int indent) throws JSONException, IOException {
+ if (value == null || value.equals(null)) {
+ writer.write("null");
+ } else if (value instanceof JSONObject) {
+ ((JSONObject) value).write(writer, indentFactor, indent);
+ } else if (value instanceof JSONArray) {
+ ((JSONArray) value).write(writer, indentFactor, indent);
+ } else if (value instanceof Map) {
+ new JSONObject((Map<String, Object>) value).write(writer, indentFactor, indent);
+ } else if (value instanceof Collection) {
+ new JSONArray((Collection<Object>) value).write(writer, indentFactor,
+ indent);
+ } else if (value.getClass().isArray()) {
+ new JSONArray(value).write(writer, indentFactor, indent);
+ } else if (value instanceof Number) {
+ writer.write(numberToString((Number) value));
+ } else if (value instanceof Boolean) {
+ writer.write(value.toString());
+ } else if (value instanceof JSONString) {
+ Object o;
+ try {
+ o = ((JSONString) value).toJSONString();
+ } catch (Exception e) {
+ throw new JSONException(e);
+ }
+ writer.write(o != null ? o.toString() : quote(value.toString()));
+ } else {
+ quote(value.toString(), writer);
+ }
+ return writer;
+ }
+
+ static final void indent(Writer writer, int indent) throws IOException {
+ for (int i = 0; i < indent; i += 1) {
+ writer.write(' ');
+ }
+ }
+
+ /**
+ * Write the contents of the JSONObject as JSON text to a writer. For
+ * compactness, no whitespace is added.
+ * <p>
+ * Warning: This method assumes that the data structure is acyclical.
+ *
+ * @return The writer.
+ * @throws JSONException
+ */
+ Writer write(Writer writer, int indentFactor, int indent)
+ throws JSONException {
+ try {
+ boolean commanate = false;
+ final int length = this.length();
+ Iterator<String> keys = this.keys();
+ writer.write('{');
+
+ if (length == 1) {
+ Object key = keys.next();
+ writer.write(quote(key.toString()));
+ writer.write(':');
+ if (indentFactor > 0) {
+ writer.write(' ');
+ }
+ writeValue(writer, this.map.get(key), indentFactor, indent);
+ } else if (length != 0) {
+ final int newindent = indent + indentFactor;
+ while (keys.hasNext()) {
+ Object key = keys.next();
+ if (commanate) {
+ writer.write(',');
+ }
+ if (indentFactor > 0) {
+ writer.write('\n');
+ }
+ indent(writer, newindent);
+ writer.write(quote(key.toString()));
+ writer.write(':');
+ if (indentFactor > 0) {
+ writer.write(' ');
+ }
+ writeValue(writer, this.map.get(key), indentFactor,
+ newindent);
+ commanate = true;
+ }
+ if (indentFactor > 0) {
+ writer.write('\n');
+ }
+ indent(writer, indent);
+ }
+ writer.write('}');
+ return writer;
+ } catch (IOException exception) {
+ throw new JSONException(exception);
+ }
+ }
+}
diff --git a/datarouter-prov/src/main/java/org/json/JSONString.java b/datarouter-prov/src/main/java/org/json/JSONString.java
new file mode 100644
index 00000000..d01ae339
--- /dev/null
+++ b/datarouter-prov/src/main/java/org/json/JSONString.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package org.json;
+/**
+ * The <code>JSONString</code> interface allows a <code>toJSONString()</code>
+ * method so that a class can change the behavior of
+ * <code>JSONObject.toString()</code>, <code>JSONArray.toString()</code>,
+ * and <code>JSONWriter.value(</code>Object<code>)</code>. The
+ * <code>toJSONString</code> method will be used instead of the default behavior
+ * of using the Object's <code>toString()</code> method and quoting the result.
+ */
+public interface JSONString {
+ /**
+ * The <code>toJSONString</code> method allows a class to produce its own JSON
+ * serialization.
+ *
+ * @return A strictly syntactically correct JSON text.
+ */
+ public String toJSONString();
+}
diff --git a/datarouter-prov/src/main/java/org/json/JSONStringer.java b/datarouter-prov/src/main/java/org/json/JSONStringer.java
new file mode 100644
index 00000000..91b58772
--- /dev/null
+++ b/datarouter-prov/src/main/java/org/json/JSONStringer.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package org.json;
+
+/*
+Copyright (c) 2006 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+import java.io.StringWriter;
+
+/**
+ * JSONStringer provides a quick and convenient way of producing JSON text.
+ * The texts produced strictly conform to JSON syntax rules. No whitespace is
+ * added, so the results are ready for transmission or storage. Each instance of
+ * JSONStringer can produce one JSON text.
+ * <p>
+ * A JSONStringer instance provides a <code>value</code> method for appending
+ * values to the
+ * text, and a <code>key</code>
+ * method for adding keys before values in objects. There are <code>array</code>
+ * and <code>endArray</code> methods that make and bound array values, and
+ * <code>object</code> and <code>endObject</code> methods which make and bound
+ * object values. All of these methods return the JSONWriter instance,
+ * permitting cascade style. For example, <pre>
+ * myString = new JSONStringer()
+ * .object()
+ * .key("JSON")
+ * .value("Hello, World!")
+ * .endObject()
+ * .toString();</pre> which produces the string <pre>
+ * {"JSON":"Hello, World!"}</pre>
+ * <p>
+ * The first method called must be <code>array</code> or <code>object</code>.
+ * There are no methods for adding commas or colons. JSONStringer adds them for
+ * you. Objects and arrays can be nested up to 20 levels deep.
+ * <p>
+ * This can sometimes be easier than using a JSONObject to build a string.
+ * @author JSON.org
+ * @version 2008-09-18
+ */
+public class JSONStringer extends JSONWriter {
+ /**
+ * Make a fresh JSONStringer. It can be used to build one JSON text.
+ */
+ public JSONStringer() {
+ super(new StringWriter());
+ }
+
+ /**
+ * Return the JSON text. This method is used to obtain the product of the
+ * JSONStringer instance. It will return <code>null</code> if there was a
+ * problem in the construction of the JSON text (such as the calls to
+ * <code>array</code> were not properly balanced with calls to
+ * <code>endArray</code>).
+ * @return The JSON text.
+ */
+ public String toString() {
+ return this.mode == 'd' ? this.writer.toString() : null;
+ }
+}
diff --git a/datarouter-prov/src/main/java/org/json/JSONTokener.java b/datarouter-prov/src/main/java/org/json/JSONTokener.java
new file mode 100644
index 00000000..816f52e4
--- /dev/null
+++ b/datarouter-prov/src/main/java/org/json/JSONTokener.java
@@ -0,0 +1,468 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package org.json;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+
+/*
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+/**
+ * A JSONTokener takes a source string and extracts characters and tokens from
+ * it. It is used by the JSONObject and JSONArray constructors to parse
+ * JSON source strings.
+ * @author JSON.org
+ * @version 2012-02-16
+ */
+public class JSONTokener {
+
+ private long character;
+ private boolean eof;
+ private long index;
+ private long line;
+ private char previous;
+ private Reader reader;
+ private boolean usePrevious;
+
+
+ /**
+ * Construct a JSONTokener from a Reader.
+ *
+ * @param reader A reader.
+ */
+ public JSONTokener(Reader reader) {
+ this.reader = reader.markSupported()
+ ? reader
+ : new BufferedReader(reader);
+ this.eof = false;
+ this.usePrevious = false;
+ this.previous = 0;
+ this.index = 0;
+ this.character = 1;
+ this.line = 1;
+ }
+
+
+ /**
+ * Construct a JSONTokener from an InputStream.
+ */
+ public JSONTokener(InputStream inputStream) throws JSONException {
+ this(new InputStreamReader(inputStream));
+ }
+
+
+ /**
+ * Construct a JSONTokener from a string.
+ *
+ * @param s A source string.
+ */
+ public JSONTokener(String s) {
+ this(new StringReader(s));
+ }
+
+
+ /**
+ * Back up one character. This provides a sort of lookahead capability,
+ * so that you can test for a digit or letter before attempting to parse
+ * the next number or identifier.
+ */
+ public void back() throws JSONException {
+ if (this.usePrevious || this.index <= 0) {
+ throw new JSONException("Stepping back two steps is not supported");
+ }
+ this.index -= 1;
+ this.character -= 1;
+ this.usePrevious = true;
+ this.eof = false;
+ }
+
+
+ /**
+ * Get the hex value of a character (base16).
+ * @param c A character between '0' and '9' or between 'A' and 'F' or
+ * between 'a' and 'f'.
+ * @return An int between 0 and 15, or -1 if c was not a hex digit.
+ */
+ public static int dehexchar(char c) {
+ if (c >= '0' && c <= '9') {
+ return c - '0';
+ }
+ if (c >= 'A' && c <= 'F') {
+ return c - ('A' - 10);
+ }
+ if (c >= 'a' && c <= 'f') {
+ return c - ('a' - 10);
+ }
+ return -1;
+ }
+
+ public boolean end() {
+ return this.eof && !this.usePrevious;
+ }
+
+
+ /**
+ * Determine if the source string still contains characters that next()
+ * can consume.
+ * @return true if not yet at the end of the source.
+ */
+ public boolean more() throws JSONException {
+ this.next();
+ if (this.end()) {
+ return false;
+ }
+ this.back();
+ return true;
+ }
+
+
+ /**
+ * Get the next character in the source string.
+ *
+ * @return The next character, or 0 if past the end of the source string.
+ */
+ public char next() throws JSONException {
+ int c;
+ if (this.usePrevious) {
+ this.usePrevious = false;
+ c = this.previous;
+ } else {
+ try {
+ c = this.reader.read();
+ } catch (IOException exception) {
+ throw new JSONException(exception);
+ }
+
+ if (c <= 0) { // End of stream
+ this.eof = true;
+ c = 0;
+ }
+ }
+ this.index += 1;
+ if (this.previous == '\r') {
+ this.line += 1;
+ this.character = c == '\n' ? 0 : 1;
+ } else if (c == '\n') {
+ this.line += 1;
+ this.character = 0;
+ } else {
+ this.character += 1;
+ }
+ this.previous = (char) c;
+ return this.previous;
+ }
+
+
+ /**
+ * Consume the next character, and check that it matches a specified
+ * character.
+ * @param c The character to match.
+ * @return The character.
+ * @throws JSONException if the character does not match.
+ */
+ public char next(char c) throws JSONException {
+ char n = this.next();
+ if (n != c) {
+ throw this.syntaxError("Expected '" + c + "' and instead saw '" +
+ n + "'");
+ }
+ return n;
+ }
+
+
+ /**
+ * Get the next n characters.
+ *
+ * @param n The number of characters to take.
+ * @return A string of n characters.
+ * @throws JSONException
+ * Substring bounds error if there are not
+ * n characters remaining in the source string.
+ */
+ public String next(int n) throws JSONException {
+ if (n == 0) {
+ return "";
+ }
+
+ char[] chars = new char[n];
+ int pos = 0;
+
+ while (pos < n) {
+ chars[pos] = this.next();
+ if (this.end()) {
+ throw this.syntaxError("Substring bounds error");
+ }
+ pos += 1;
+ }
+ return new String(chars);
+ }
+
+
+ /**
+ * Get the next char in the string, skipping whitespace.
+ * @throws JSONException
+ * @return A character, or 0 if there are no more characters.
+ */
+ public char nextClean() throws JSONException {
+ for (;;) {
+ char c = this.next();
+ if (c == 0 || c > ' ') {
+ return c;
+ }
+ }
+ }
+
+
+ /**
+ * Return the characters up to the next close quote character.
+ * Backslash processing is done. The formal JSON format does not
+ * allow strings in single quotes, but an implementation is allowed to
+ * accept them.
+ * @param quote The quoting character, either
+ * <code>"</code>&nbsp;<small>(double quote)</small> or
+ * <code>'</code>&nbsp;<small>(single quote)</small>.
+ * @return A String.
+ * @throws JSONException Unterminated string.
+ */
+ public String nextString(char quote) throws JSONException {
+ char c;
+ StringBuffer sb = new StringBuffer();
+ for (;;) {
+ c = this.next();
+ switch (c) {
+ case 0:
+ case '\n':
+ case '\r':
+ throw this.syntaxError("Unterminated string");
+ case '\\':
+ c = this.next();
+ switch (c) {
+ case 'b':
+ sb.append('\b');
+ break;
+ case 't':
+ sb.append('\t');
+ break;
+ case 'n':
+ sb.append('\n');
+ break;
+ case 'f':
+ sb.append('\f');
+ break;
+ case 'r':
+ sb.append('\r');
+ break;
+ case 'u':
+ sb.append((char)Integer.parseInt(this.next(4), 16));
+ break;
+ case '"':
+ case '\'':
+ case '\\':
+ case '/':
+ sb.append(c);
+ break;
+ default:
+ throw this.syntaxError("Illegal escape.");
+ }
+ break;
+ default:
+ if (c == quote) {
+ return sb.toString();
+ }
+ sb.append(c);
+ }
+ }
+ }
+
+
+ /**
+ * Get the text up but not including the specified character or the
+ * end of line, whichever comes first.
+ * @param delimiter A delimiter character.
+ * @return A string.
+ */
+ public String nextTo(char delimiter) throws JSONException {
+ StringBuffer sb = new StringBuffer();
+ for (;;) {
+ char c = this.next();
+ if (c == delimiter || c == 0 || c == '\n' || c == '\r') {
+ if (c != 0) {
+ this.back();
+ }
+ return sb.toString().trim();
+ }
+ sb.append(c);
+ }
+ }
+
+
+ /**
+ * Get the text up but not including one of the specified delimiter
+ * characters or the end of line, whichever comes first.
+ * @param delimiters A set of delimiter characters.
+ * @return A string, trimmed.
+ */
+ public String nextTo(String delimiters) throws JSONException {
+ char c;
+ StringBuffer sb = new StringBuffer();
+ for (;;) {
+ c = this.next();
+ if (delimiters.indexOf(c) >= 0 || c == 0 ||
+ c == '\n' || c == '\r') {
+ if (c != 0) {
+ this.back();
+ }
+ return sb.toString().trim();
+ }
+ sb.append(c);
+ }
+ }
+
+
+ /**
+ * Get the next value. The value can be a Boolean, Double, Integer,
+ * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object.
+ * @throws JSONException If syntax error.
+ *
+ * @return An object.
+ */
+ public Object nextValue() throws JSONException {
+ char c = this.nextClean();
+ String string;
+
+ switch (c) {
+ case '"':
+ case '\'':
+ return this.nextString(c);
+ case '{':
+ this.back();
+ return new JSONObject(this);
+ case '[':
+ this.back();
+ return new JSONArray(this);
+ }
+
+ /*
+ * Handle unquoted text. This could be the values true, false, or
+ * null, or it can be a number. An implementation (such as this one)
+ * is allowed to also accept non-standard forms.
+ *
+ * Accumulate characters until we reach the end of the text or a
+ * formatting character.
+ */
+
+ StringBuffer sb = new StringBuffer();
+ while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
+ sb.append(c);
+ c = this.next();
+ }
+ this.back();
+
+ string = sb.toString().trim();
+ if ("".equals(string)) {
+ throw this.syntaxError("Missing value");
+ }
+ return JSONObject.stringToValue(string);
+ }
+
+
+ /**
+ * Skip characters until the next character is the requested character.
+ * If the requested character is not found, no characters are skipped.
+ * @param to A character to skip to.
+ * @return The requested character, or zero if the requested character
+ * is not found.
+ */
+ public char skipTo(char to) throws JSONException {
+ char c;
+ try {
+ long startIndex = this.index;
+ long startCharacter = this.character;
+ long startLine = this.line;
+ this.reader.mark(1000000);
+ do {
+ c = this.next();
+ if (c == 0) {
+ this.reader.reset();
+ this.index = startIndex;
+ this.character = startCharacter;
+ this.line = startLine;
+ return c;
+ }
+ } while (c != to);
+ } catch (IOException exc) {
+ throw new JSONException(exc);
+ }
+
+ this.back();
+ return c;
+ }
+
+
+ /**
+ * Make a JSONException to signal a syntax error.
+ *
+ * @param message The error message.
+ * @return A JSONException object, suitable for throwing
+ */
+ public JSONException syntaxError(String message) {
+ return new JSONException(message + this.toString());
+ }
+
+
+ /**
+ * Make a printable string of this JSONTokener.
+ *
+ * @return " at {index} [character {character} line {line}]"
+ */
+ public String toString() {
+ return " at " + this.index + " [character " + this.character + " line " +
+ this.line + "]";
+ }
+}
diff --git a/datarouter-prov/src/main/java/org/json/JSONWriter.java b/datarouter-prov/src/main/java/org/json/JSONWriter.java
new file mode 100644
index 00000000..a9b0babc
--- /dev/null
+++ b/datarouter-prov/src/main/java/org/json/JSONWriter.java
@@ -0,0 +1,349 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package org.json;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/*
+Copyright (c) 2006 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+/**
+ * JSONWriter provides a quick and convenient way of producing JSON text.
+ * The texts produced strictly conform to JSON syntax rules. No whitespace is
+ * added, so the results are ready for transmission or storage. Each instance of
+ * JSONWriter can produce one JSON text.
+ * <p>
+ * A JSONWriter instance provides a <code>value</code> method for appending
+ * values to the
+ * text, and a <code>key</code>
+ * method for adding keys before values in objects. There are <code>array</code>
+ * and <code>endArray</code> methods that make and bound array values, and
+ * <code>object</code> and <code>endObject</code> methods which make and bound
+ * object values. All of these methods return the JSONWriter instance,
+ * permitting a cascade style. For example, <pre>
+ * new JSONWriter(myWriter)
+ * .object()
+ * .key("JSON")
+ * .value("Hello, World!")
+ * .endObject();</pre> which writes <pre>
+ * {"JSON":"Hello, World!"}</pre>
+ * <p>
+ * The first method called must be <code>array</code> or <code>object</code>.
+ * There are no methods for adding commas or colons. JSONWriter adds them for
+ * you. Objects and arrays can be nested up to 20 levels deep.
+ * <p>
+ * This can sometimes be easier than using a JSONObject to build a string.
+ * @author JSON.org
+ * @version 2011-11-24
+ */
+public class JSONWriter {
+ private static final int maxdepth = 200;
+
+ /**
+ * The comma flag determines if a comma should be output before the next
+ * value.
+ */
+ private boolean comma;
+
+ /**
+ * The current mode. Values:
+ * 'a' (array),
+ * 'd' (done),
+ * 'i' (initial),
+ * 'k' (key),
+ * 'o' (object).
+ */
+ protected char mode;
+
+ /**
+ * The object/array stack.
+ */
+ private final JSONObject stack[];
+
+ /**
+ * The stack top index. A value of 0 indicates that the stack is empty.
+ */
+ private int top;
+
+ /**
+ * The writer that will receive the output.
+ */
+ protected Writer writer;
+
+ /**
+ * Make a fresh JSONWriter. It can be used to build one JSON text.
+ */
+ public JSONWriter(Writer w) {
+ this.comma = false;
+ this.mode = 'i';
+ this.stack = new JSONObject[maxdepth];
+ this.top = 0;
+ this.writer = w;
+ }
+
+ /**
+ * Append a value.
+ * @param string A string value.
+ * @return this
+ * @throws JSONException If the value is out of sequence.
+ */
+ private JSONWriter append(String string) throws JSONException {
+ if (string == null) {
+ throw new JSONException("Null pointer");
+ }
+ if (this.mode == 'o' || this.mode == 'a') {
+ try {
+ if (this.comma && this.mode == 'a') {
+ this.writer.write(',');
+ }
+ this.writer.write(string);
+ } catch (IOException e) {
+ throw new JSONException(e);
+ }
+ if (this.mode == 'o') {
+ this.mode = 'k';
+ }
+ this.comma = true;
+ return this;
+ }
+ throw new JSONException("Value out of sequence.");
+ }
+
+ /**
+ * Begin appending a new array. All values until the balancing
+ * <code>endArray</code> will be appended to this array. The
+ * <code>endArray</code> method must be called to mark the array's end.
+ * @return this
+ * @throws JSONException If the nesting is too deep, or if the object is
+ * started in the wrong place (for example as a key or after the end of the
+ * outermost array or object).
+ */
+ public JSONWriter array() throws JSONException {
+ if (this.mode == 'i' || this.mode == 'o' || this.mode == 'a') {
+ this.push(null);
+ this.append("[");
+ this.comma = false;
+ return this;
+ }
+ throw new JSONException("Misplaced array.");
+ }
+
+ /**
+ * End something.
+ * @param mode Mode
+ * @param c Closing character
+ * @return this
+ * @throws JSONException If unbalanced.
+ */
+ private JSONWriter end(char mode, char c) throws JSONException {
+ if (this.mode != mode) {
+ throw new JSONException(mode == 'a'
+ ? "Misplaced endArray."
+ : "Misplaced endObject.");
+ }
+ this.pop(mode);
+ try {
+ this.writer.write(c);
+ } catch (IOException e) {
+ throw new JSONException(e);
+ }
+ this.comma = true;
+ return this;
+ }
+
+ /**
+ * End an array. This method most be called to balance calls to
+ * <code>array</code>.
+ * @return this
+ * @throws JSONException If incorrectly nested.
+ */
+ public JSONWriter endArray() throws JSONException {
+ return this.end('a', ']');
+ }
+
+ /**
+ * End an object. This method most be called to balance calls to
+ * <code>object</code>.
+ * @return this
+ * @throws JSONException If incorrectly nested.
+ */
+ public JSONWriter endObject() throws JSONException {
+ return this.end('k', '}');
+ }
+
+ /**
+ * Append a key. The key will be associated with the next value. In an
+ * object, every value must be preceded by a key.
+ * @param string A key string.
+ * @return this
+ * @throws JSONException If the key is out of place. For example, keys
+ * do not belong in arrays or if the key is null.
+ */
+ public JSONWriter key(String string) throws JSONException {
+ if (string == null) {
+ throw new JSONException("Null key.");
+ }
+ if (this.mode == 'k') {
+ try {
+ this.stack[this.top - 1].putOnce(string, Boolean.TRUE);
+ if (this.comma) {
+ this.writer.write(',');
+ }
+ this.writer.write(JSONObject.quote(string));
+ this.writer.write(':');
+ this.comma = false;
+ this.mode = 'o';
+ return this;
+ } catch (IOException e) {
+ throw new JSONException(e);
+ }
+ }
+ throw new JSONException("Misplaced key.");
+ }
+
+
+ /**
+ * Begin appending a new object. All keys and values until the balancing
+ * <code>endObject</code> will be appended to this object. The
+ * <code>endObject</code> method must be called to mark the object's end.
+ * @return this
+ * @throws JSONException If the nesting is too deep, or if the object is
+ * started in the wrong place (for example as a key or after the end of the
+ * outermost array or object).
+ */
+ public JSONWriter object() throws JSONException {
+ if (this.mode == 'i') {
+ this.mode = 'o';
+ }
+ if (this.mode == 'o' || this.mode == 'a') {
+ this.append("{");
+ this.push(new JSONObject());
+ this.comma = false;
+ return this;
+ }
+ throw new JSONException("Misplaced object.");
+
+ }
+
+
+ /**
+ * Pop an array or object scope.
+ * @param c The scope to close.
+ * @throws JSONException If nesting is wrong.
+ */
+ private void pop(char c) throws JSONException {
+ if (this.top <= 0) {
+ throw new JSONException("Nesting error.");
+ }
+ char m = this.stack[this.top - 1] == null ? 'a' : 'k';
+ if (m != c) {
+ throw new JSONException("Nesting error.");
+ }
+ this.top -= 1;
+ this.mode = this.top == 0
+ ? 'd'
+ : this.stack[this.top - 1] == null
+ ? 'a'
+ : 'k';
+ }
+
+ /**
+ * Push an array or object scope.
+ * @param jo The scope to open.
+ * @throws JSONException If nesting is too deep.
+ */
+ private void push(JSONObject jo) throws JSONException {
+ if (this.top >= maxdepth) {
+ throw new JSONException("Nesting too deep.");
+ }
+ this.stack[this.top] = jo;
+ this.mode = jo == null ? 'a' : 'k';
+ this.top += 1;
+ }
+
+
+ /**
+ * Append either the value <code>true</code> or the value
+ * <code>false</code>.
+ * @param b A boolean.
+ * @return this
+ * @throws JSONException
+ */
+ public JSONWriter value(boolean b) throws JSONException {
+ return this.append(b ? "true" : "false");
+ }
+
+ /**
+ * Append a double value.
+ * @param d A double.
+ * @return this
+ * @throws JSONException If the number is not finite.
+ */
+ public JSONWriter value(double d) throws JSONException {
+ return this.value(new Double(d));
+ }
+
+ /**
+ * Append a long value.
+ * @param l A long.
+ * @return this
+ * @throws JSONException
+ */
+ public JSONWriter value(long l) throws JSONException {
+ return this.append(Long.toString(l));
+ }
+
+
+ /**
+ * Append an object value.
+ * @param object The object to append. It can be null, or a Boolean, Number,
+ * String, JSONObject, or JSONArray, or an object that implements JSONString.
+ * @return this
+ * @throws JSONException If the value is out of sequence.
+ */
+ public JSONWriter value(Object object) throws JSONException {
+ return this.append(JSONObject.valueToString(object));
+ }
+}
diff --git a/datarouter-prov/src/main/java/org/json/LOGJSONObject.java b/datarouter-prov/src/main/java/org/json/LOGJSONObject.java
new file mode 100644
index 00000000..2f18c540
--- /dev/null
+++ b/datarouter-prov/src/main/java/org/json/LOGJSONObject.java
@@ -0,0 +1,1653 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package org.json;
+
+/*
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.LinkedHashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.Set;
+
+/**
+ * A JSONObject is an unordered collection of name/value pairs. Its external
+ * form is a string wrapped in curly braces with colons between the names and
+ * values, and commas between the values and names. The internal form is an
+ * object having <code>get</code> and <code>opt</code> methods for accessing the
+ * values by name, and <code>put</code> methods for adding or replacing values
+ * by name. The values can be any of these types: <code>Boolean</code>,
+ * <code>JSONArray</code>, <code>JSONObject</code>, <code>Number</code>,
+ * <code>String</code>, or the <code>JSONObject.NULL</code> object. A JSONObject
+ * constructor can be used to convert an external form JSON text into an
+ * internal form whose values can be retrieved with the <code>get</code> and
+ * <code>opt</code> methods, or to convert values into a JSON text using the
+ * <code>put</code> and <code>toString</code> methods. A <code>get</code> method
+ * returns a value if one can be found, and throws an exception if one cannot be
+ * found. An <code>opt</code> method returns a default value instead of throwing
+ * an exception, and so is useful for obtaining optional values.
+ * <p>
+ * The generic <code>get()</code> and <code>opt()</code> methods return an
+ * object, which you can cast or query for type. There are also typed
+ * <code>get</code> and <code>opt</code> methods that do type checking and type
+ * coercion for you. The opt methods differ from the get methods in that they do
+ * not throw. Instead, they return a specified value, such as null.
+ * <p>
+ * The <code>put</code> methods add or replace values in an object. For example,
+ *
+ * <pre>
+ * myString = new JSONObject().put(&quot;JSON&quot;, &quot;Hello, World!&quot;).toString();
+ * </pre>
+ *
+ * produces the string <code>{"JSON": "Hello, World"}</code>.
+ * <p>
+ * The texts produced by the <code>toString</code> methods strictly conform to
+ * the JSON syntax rules. The constructors are more forgiving in the texts they
+ * will accept:
+ * <ul>
+ * <li>An extra <code>,</code>&nbsp;<small>(comma)</small> may appear just
+ * before the closing brace.</li>
+ * <li>Strings may be quoted with <code>'</code>&nbsp;<small>(single
+ * quote)</small>.</li>
+ * <li>Strings do not need to be quoted at all if they do not begin with a quote
+ * or single quote, and if they do not contain leading or trailing spaces, and
+ * if they do not contain any of these characters:
+ * <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers and
+ * if they are not the reserved words <code>true</code>, <code>false</code>, or
+ * <code>null</code>.</li>
+ * <li>Keys can be followed by <code>=</code> or <code>=></code> as well as by
+ * <code>:</code>.</li>
+ * <li>Values can be followed by <code>;</code> <small>(semicolon)</small> as
+ * well as by <code>,</code> <small>(comma)</small>.</li>
+ * </ul>
+ *
+ * @author JSON.org
+ * @version 2012-12-01
+ */
+public class LOGJSONObject {
+ /**
+ * The maximum number of keys in the key pool.
+ */
+ private static final int keyPoolSize = 100;
+
+ /**
+ * Key pooling is like string interning, but without permanently tying up
+ * memory. To help conserve memory, storage of duplicated key strings in
+ * JSONObjects will be avoided by using a key pool to manage unique key
+ * string objects. This is used by JSONObject.put(string, object).
+ */
+ private static Map<String,Object> keyPool = new LinkedHashMap<String,Object>(keyPoolSize);
+
+ /**
+ * JSONObject.NULL is equivalent to the value that JavaScript calls null,
+ * whilst Java's null is equivalent to the value that JavaScript calls
+ * undefined.
+ */
+ private static final class Null {
+
+ /**
+ * There is only intended to be a single instance of the NULL object,
+ * so the clone method returns itself.
+ * @return NULL.
+ */
+ protected final Object clone() {
+ return this;
+ }
+
+ /**
+ * A Null object is equal to the null value and to itself.
+ * @param object An object to test for nullness.
+ * @return true if the object parameter is the JSONObject.NULL object
+ * or null.
+ */
+ public boolean equals(Object object) {
+ return object == null || object == this;
+ }
+
+ /**
+ * Get the "null" string value.
+ * @return The string "null".
+ */
+ public String toString() {
+ return "null";
+ }
+ }
+
+
+ /**
+ * The map where the JSONObject's properties are kept.
+ */
+ private final Map<String,Object> map;
+
+
+ /**
+ * It is sometimes more convenient and less ambiguous to have a
+ * <code>NULL</code> object than to use Java's <code>null</code> value.
+ * <code>JSONObject.NULL.equals(null)</code> returns <code>true</code>.
+ * <code>JSONObject.NULL.toString()</code> returns <code>"null"</code>.
+ */
+ public static final Object NULL = new Null();
+
+
+ /**
+ * Construct an empty JSONObject.
+ */
+ public LOGJSONObject() {
+ this.map = new LinkedHashMap<String,Object>();
+ }
+
+
+ /**
+ * Construct a JSONObject from a subset of another JSONObject.
+ * An array of strings is used to identify the keys that should be copied.
+ * Missing keys are ignored.
+ * @param jo A JSONObject.
+ * @param names An array of strings.
+ * @throws JSONException
+ * @exception JSONException If a value is a non-finite number or if a name is duplicated.
+ */
+ public LOGJSONObject(LOGJSONObject jo, String[] names) {
+ this();
+ for (int i = 0; i < names.length; i += 1) {
+ try {
+ this.putOnce(names[i], jo.opt(names[i]));
+ } catch (Exception ignore) {
+ }
+ }
+ }
+
+
+ /**
+ * Construct a JSONObject from a JSONTokener.
+ * @param x A JSONTokener object containing the source string.
+ * @throws JSONException If there is a syntax error in the source string
+ * or a duplicated key.
+ */
+ public LOGJSONObject(JSONTokener x) throws JSONException {
+ this();
+ char c;
+ String key;
+
+ if (x.nextClean() != '{') {
+ throw x.syntaxError("A JSONObject text must begin with '{'");
+ }
+ for (;;) {
+ c = x.nextClean();
+ switch (c) {
+ case 0:
+ throw x.syntaxError("A JSONObject text must end with '}'");
+ case '}':
+ return;
+ default:
+ x.back();
+ key = x.nextValue().toString();
+ }
+
+// The key is followed by ':'. We will also tolerate '=' or '=>'.
+
+ c = x.nextClean();
+ if (c == '=') {
+ if (x.next() != '>') {
+ x.back();
+ }
+ } else if (c != ':') {
+ throw x.syntaxError("Expected a ':' after a key");
+ }
+ this.putOnce(key, x.nextValue());
+
+// Pairs are separated by ','. We will also tolerate ';'.
+
+ switch (x.nextClean()) {
+ case ';':
+ case ',':
+ if (x.nextClean() == '}') {
+ return;
+ }
+ x.back();
+ break;
+ case '}':
+ return;
+ default:
+ throw x.syntaxError("Expected a ',' or '}'");
+ }
+ }
+ }
+
+
+ /**
+ * Construct a JSONObject from a Map.
+ *
+ * @param map A map object that can be used to initialize the contents of
+ * the JSONObject.
+ * @throws JSONException
+ */
+ public LOGJSONObject(Map<String,Object> map) {
+ this.map = new LinkedHashMap<String,Object>();
+ if (map != null) {
+ Iterator<Map.Entry<String,Object>> i = map.entrySet().iterator();
+ while (i.hasNext()) {
+ Map.Entry<String,Object> e = i.next();
+ Object value = e.getValue();
+ if (value != null) {
+ this.map.put(e.getKey(), wrap(value));
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Construct a JSONObject from an Object using bean getters.
+ * It reflects on all of the public methods of the object.
+ * For each of the methods with no parameters and a name starting
+ * with <code>"get"</code> or <code>"is"</code> followed by an uppercase letter,
+ * the method is invoked, and a key and the value returned from the getter method
+ * are put into the new JSONObject.
+ *
+ * The key is formed by removing the <code>"get"</code> or <code>"is"</code> prefix.
+ * If the second remaining character is not upper case, then the first
+ * character is converted to lower case.
+ *
+ * For example, if an object has a method named <code>"getName"</code>, and
+ * if the result of calling <code>object.getName()</code> is <code>"Larry Fine"</code>,
+ * then the JSONObject will contain <code>"name": "Larry Fine"</code>.
+ *
+ * @param bean An object that has getter methods that should be used
+ * to make a JSONObject.
+ */
+ public LOGJSONObject(Object bean) {
+ this();
+ this.populateMap(bean);
+ }
+
+
+ /**
+ * Construct a JSONObject from an Object, using reflection to find the
+ * public members. The resulting JSONObject's keys will be the strings
+ * from the names array, and the values will be the field values associated
+ * with those keys in the object. If a key is not found or not visible,
+ * then it will not be copied into the new JSONObject.
+ * @param object An object that has fields that should be used to make a
+ * JSONObject.
+ * @param names An array of strings, the names of the fields to be obtained
+ * from the object.
+ */
+ public LOGJSONObject(Object object, String names[]) {
+ this();
+ Class<? extends Object> c = object.getClass();
+ for (int i = 0; i < names.length; i += 1) {
+ String name = names[i];
+ try {
+ this.putOpt(name, c.getField(name).get(object));
+ } catch (Exception ignore) {
+ }
+ }
+ }
+
+
+ /**
+ * Construct a JSONObject from a source JSON text string.
+ * This is the most commonly used JSONObject constructor.
+ * @param source A string beginning
+ * with <code>{</code>&nbsp;<small>(left brace)</small> and ending
+ * with <code>}</code>&nbsp;<small>(right brace)</small>.
+ * @exception JSONException If there is a syntax error in the source
+ * string or a duplicated key.
+ */
+ public LOGJSONObject(String source) throws JSONException {
+ this(new JSONTokener(source));
+ }
+
+
+ /**
+ * Construct a JSONObject from a ResourceBundle.
+ * @param baseName The ResourceBundle base name.
+ * @param locale The Locale to load the ResourceBundle for.
+ * @throws JSONException If any JSONExceptions are detected.
+ */
+ public LOGJSONObject(String baseName, Locale locale) throws JSONException {
+ this();
+ ResourceBundle bundle = ResourceBundle.getBundle(baseName, locale,
+ Thread.currentThread().getContextClassLoader());
+
+// Iterate through the keys in the bundle.
+
+ Enumeration<?> keys = bundle.getKeys();
+ while (keys.hasMoreElements()) {
+ Object key = keys.nextElement();
+ if (key instanceof String) {
+
+// Go through the path, ensuring that there is a nested JSONObject for each
+// segment except the last. Add the value using the last segment's name into
+// the deepest nested JSONObject.
+
+ String[] path = ((String)key).split("\\.");
+ int last = path.length - 1;
+ LOGJSONObject target = this;
+ for (int i = 0; i < last; i += 1) {
+ String segment = path[i];
+ LOGJSONObject nextTarget = target.optJSONObject(segment);
+ if (nextTarget == null) {
+ nextTarget = new LOGJSONObject();
+ target.put(segment, nextTarget);
+ }
+ target = nextTarget;
+ }
+ target.put(path[last], bundle.getString((String)key));
+ }
+ }
+ }
+
+
+ /**
+ * Accumulate values under a key. It is similar to the put method except
+ * that if there is already an object stored under the key then a
+ * JSONArray is stored under the key to hold all of the accumulated values.
+ * If there is already a JSONArray, then the new value is appended to it.
+ * In contrast, the put method replaces the previous value.
+ *
+ * If only one value is accumulated that is not a JSONArray, then the
+ * result will be the same as using put. But if multiple values are
+ * accumulated, then the result will be like append.
+ * @param key A key string.
+ * @param value An object to be accumulated under the key.
+ * @return this.
+ * @throws JSONException If the value is an invalid number
+ * or if the key is null.
+ */
+ public LOGJSONObject accumulate(
+ String key,
+ Object value
+ ) throws JSONException {
+ testValidity(value);
+ Object object = this.opt(key);
+ if (object == null) {
+ this.put(key, value instanceof JSONArray
+ ? new JSONArray().put(value)
+ : value);
+ } else if (object instanceof JSONArray) {
+ ((JSONArray)object).put(value);
+ } else {
+ this.put(key, new JSONArray().put(object).put(value));
+ }
+ return this;
+ }
+
+
+ /**
+ * Append values to the array under a key. If the key does not exist in the
+ * JSONObject, then the key is put in the JSONObject with its value being a
+ * JSONArray containing the value parameter. If the key was already
+ * associated with a JSONArray, then the value parameter is appended to it.
+ * @param key A key string.
+ * @param value An object to be accumulated under the key.
+ * @return this.
+ * @throws JSONException If the key is null or if the current value
+ * associated with the key is not a JSONArray.
+ */
+ public LOGJSONObject append(String key, Object value) throws JSONException {
+ testValidity(value);
+ Object object = this.opt(key);
+ if (object == null) {
+ this.put(key, new JSONArray().put(value));
+ } else if (object instanceof JSONArray) {
+ this.put(key, ((JSONArray)object).put(value));
+ } else {
+ throw new JSONException("JSONObject[" + key +
+ "] is not a JSONArray.");
+ }
+ return this;
+ }
+
+
+ /**
+ * Produce a string from a double. The string "null" will be returned if
+ * the number is not finite.
+ * @param d A double.
+ * @return A String.
+ */
+ public static String doubleToString(double d) {
+ if (Double.isInfinite(d) || Double.isNaN(d)) {
+ return "null";
+ }
+
+// Shave off trailing zeros and decimal point, if possible.
+
+ String string = Double.toString(d);
+ if (string.indexOf('.') > 0 && string.indexOf('e') < 0 &&
+ string.indexOf('E') < 0) {
+ while (string.endsWith("0")) {
+ string = string.substring(0, string.length() - 1);
+ }
+ if (string.endsWith(".")) {
+ string = string.substring(0, string.length() - 1);
+ }
+ }
+ return string;
+ }
+
+
+ /**
+ * Get the value object associated with a key.
+ *
+ * @param key A key string.
+ * @return The object associated with the key.
+ * @throws JSONException if the key is not found.
+ */
+ public Object get(String key) throws JSONException {
+ if (key == null) {
+ throw new JSONException("Null key.");
+ }
+ Object object = this.opt(key);
+ if (object == null) {
+ throw new JSONException("JSONObject[" + quote(key) +
+ "] not found.");
+ }
+ return object;
+ }
+
+
+ /**
+ * Get the boolean value associated with a key.
+ *
+ * @param key A key string.
+ * @return The truth.
+ * @throws JSONException
+ * if the value is not a Boolean or the String "true" or "false".
+ */
+ public boolean getBoolean(String key) throws JSONException {
+ Object object = this.get(key);
+ if (object.equals(Boolean.FALSE) ||
+ (object instanceof String &&
+ ((String)object).equalsIgnoreCase("false"))) {
+ return false;
+ } else if (object.equals(Boolean.TRUE) ||
+ (object instanceof String &&
+ ((String)object).equalsIgnoreCase("true"))) {
+ return true;
+ }
+ throw new JSONException("JSONObject[" + quote(key) +
+ "] is not a Boolean.");
+ }
+
+
+ /**
+ * Get the double value associated with a key.
+ * @param key A key string.
+ * @return The numeric value.
+ * @throws JSONException if the key is not found or
+ * if the value is not a Number object and cannot be converted to a number.
+ */
+ public double getDouble(String key) throws JSONException {
+ Object object = this.get(key);
+ try {
+ return object instanceof Number
+ ? ((Number)object).doubleValue()
+ : Double.parseDouble((String)object);
+ } catch (Exception e) {
+ throw new JSONException("JSONObject[" + quote(key) +
+ "] is not a number.");
+ }
+ }
+
+
+ /**
+ * Get the int value associated with a key.
+ *
+ * @param key A key string.
+ * @return The integer value.
+ * @throws JSONException if the key is not found or if the value cannot
+ * be converted to an integer.
+ */
+ public int getInt(String key) throws JSONException {
+ Object object = this.get(key);
+ try {
+ return object instanceof Number
+ ? ((Number)object).intValue()
+ : Integer.parseInt((String)object);
+ } catch (Exception e) {
+ throw new JSONException("JSONObject[" + quote(key) +
+ "] is not an int.");
+ }
+ }
+
+
+ /**
+ * Get the JSONArray value associated with a key.
+ *
+ * @param key A key string.
+ * @return A JSONArray which is the value.
+ * @throws JSONException if the key is not found or
+ * if the value is not a JSONArray.
+ */
+ public JSONArray getJSONArray(String key) throws JSONException {
+ Object object = this.get(key);
+ if (object instanceof JSONArray) {
+ return (JSONArray)object;
+ }
+ throw new JSONException("JSONObject[" + quote(key) +
+ "] is not a JSONArray.");
+ }
+
+
+ /**
+ * Get the JSONObject value associated with a key.
+ *
+ * @param key A key string.
+ * @return A JSONObject which is the value.
+ * @throws JSONException if the key is not found or
+ * if the value is not a JSONObject.
+ */
+ public LOGJSONObject getJSONObject(String key) throws JSONException {
+ Object object = this.get(key);
+ if (object instanceof LOGJSONObject) {
+ return (LOGJSONObject)object;
+ }
+ throw new JSONException("JSONObject[" + quote(key) +
+ "] is not a JSONObject.");
+ }
+
+
+ /**
+ * Get the long value associated with a key.
+ *
+ * @param key A key string.
+ * @return The long value.
+ * @throws JSONException if the key is not found or if the value cannot
+ * be converted to a long.
+ */
+ public long getLong(String key) throws JSONException {
+ Object object = this.get(key);
+ try {
+ return object instanceof Number
+ ? ((Number)object).longValue()
+ : Long.parseLong((String)object);
+ } catch (Exception e) {
+ throw new JSONException("JSONObject[" + quote(key) +
+ "] is not a long.");
+ }
+ }
+
+
+ /**
+ * Get an array of field names from a JSONObject.
+ *
+ * @return An array of field names, or null if there are no names.
+ */
+ public static String[] getNames(LOGJSONObject jo) {
+ int length = jo.length();
+ if (length == 0) {
+ return null;
+ }
+ Iterator<String> iterator = jo.keys();
+ String[] names = new String[length];
+ int i = 0;
+ while (iterator.hasNext()) {
+ names[i] = iterator.next();
+ i += 1;
+ }
+ return names;
+ }
+
+
+ /**
+ * Get an array of field names from an Object.
+ *
+ * @return An array of field names, or null if there are no names.
+ */
+ public static String[] getNames(Object object) {
+ if (object == null) {
+ return null;
+ }
+ Class<? extends Object> klass = object.getClass();
+ Field[] fields = klass.getFields();
+ int length = fields.length;
+ if (length == 0) {
+ return null;
+ }
+ String[] names = new String[length];
+ for (int i = 0; i < length; i += 1) {
+ names[i] = fields[i].getName();
+ }
+ return names;
+ }
+
+
+ /**
+ * Get the string associated with a key.
+ *
+ * @param key A key string.
+ * @return A string which is the value.
+ * @throws JSONException if there is no string value for the key.
+ */
+ public String getString(String key) throws JSONException {
+ Object object = this.get(key);
+ if (object instanceof String) {
+ return (String)object;
+ }
+ throw new JSONException("JSONObject[" + quote(key) +
+ "] not a string.");
+ }
+
+
+ /**
+ * Determine if the JSONObject contains a specific key.
+ * @param key A key string.
+ * @return true if the key exists in the JSONObject.
+ */
+ public boolean has(String key) {
+ return this.map.containsKey(key);
+ }
+
+
+ /**
+ * Increment a property of a JSONObject. If there is no such property,
+ * create one with a value of 1. If there is such a property, and if
+ * it is an Integer, Long, Double, or Float, then add one to it.
+ * @param key A key string.
+ * @return this.
+ * @throws JSONException If there is already a property with this name
+ * that is not an Integer, Long, Double, or Float.
+ */
+ public LOGJSONObject increment(String key) throws JSONException {
+ Object value = this.opt(key);
+ if (value == null) {
+ this.put(key, 1);
+ } else if (value instanceof Integer) {
+ this.put(key, ((Integer)value).intValue() + 1);
+ } else if (value instanceof Long) {
+ this.put(key, ((Long)value).longValue() + 1);
+ } else if (value instanceof Double) {
+ this.put(key, ((Double)value).doubleValue() + 1);
+ } else if (value instanceof Float) {
+ this.put(key, ((Float)value).floatValue() + 1);
+ } else {
+ throw new JSONException("Unable to increment [" + quote(key) + "].");
+ }
+ return this;
+ }
+
+
+ /**
+ * Determine if the value associated with the key is null or if there is
+ * no value.
+ * @param key A key string.
+ * @return true if there is no value associated with the key or if
+ * the value is the JSONObject.NULL object.
+ */
+ public boolean isNull(String key) {
+ return LOGJSONObject.NULL.equals(this.opt(key));
+ }
+
+
+ /**
+ * Get an enumeration of the keys of the JSONObject.
+ *
+ * @return An iterator of the keys.
+ */
+ public Iterator<String> keys() {
+ return this.keySet().iterator();
+ }
+
+
+ /**
+ * Get a set of keys of the JSONObject.
+ *
+ * @return A keySet.
+ */
+ public Set<String> keySet() {
+ return this.map.keySet();
+ }
+
+
+ /**
+ * Get the number of keys stored in the JSONObject.
+ *
+ * @return The number of keys in the JSONObject.
+ */
+ public int length() {
+ return this.map.size();
+ }
+
+
+ /**
+ * Produce a JSONArray containing the names of the elements of this
+ * JSONObject.
+ * @return A JSONArray containing the key strings, or null if the JSONObject
+ * is empty.
+ */
+ public JSONArray names() {
+ JSONArray ja = new JSONArray();
+ Iterator<String> keys = this.keys();
+ while (keys.hasNext()) {
+ ja.put(keys.next());
+ }
+ return ja.length() == 0 ? null : ja;
+ }
+
+ /**
+ * Produce a string from a Number.
+ * @param number A Number
+ * @return A String.
+ * @throws JSONException If n is a non-finite number.
+ */
+ public static String numberToString(Number number)
+ throws JSONException {
+ if (number == null) {
+ throw new JSONException("Null pointer");
+ }
+ testValidity(number);
+
+// Shave off trailing zeros and decimal point, if possible.
+
+ String string = number.toString();
+ if (string.indexOf('.') > 0 && string.indexOf('e') < 0 &&
+ string.indexOf('E') < 0) {
+ while (string.endsWith("0")) {
+ string = string.substring(0, string.length() - 1);
+ }
+ if (string.endsWith(".")) {
+ string = string.substring(0, string.length() - 1);
+ }
+ }
+ return string;
+ }
+
+
+ /**
+ * Get an optional value associated with a key.
+ * @param key A key string.
+ * @return An object which is the value, or null if there is no value.
+ */
+ public Object opt(String key) {
+ return key == null ? null : this.map.get(key);
+ }
+
+
+ /**
+ * Get an optional boolean associated with a key.
+ * It returns false if there is no such key, or if the value is not
+ * Boolean.TRUE or the String "true".
+ *
+ * @param key A key string.
+ * @return The truth.
+ */
+ public boolean optBoolean(String key) {
+ return this.optBoolean(key, false);
+ }
+
+
+ /**
+ * Get an optional boolean associated with a key.
+ * It returns the defaultValue if there is no such key, or if it is not
+ * a Boolean or the String "true" or "false" (case insensitive).
+ *
+ * @param key A key string.
+ * @param defaultValue The default.
+ * @return The truth.
+ */
+ public boolean optBoolean(String key, boolean defaultValue) {
+ try {
+ return this.getBoolean(key);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+
+ /**
+ * Get an optional double associated with a key,
+ * or NaN if there is no such key or if its value is not a number.
+ * If the value is a string, an attempt will be made to evaluate it as
+ * a number.
+ *
+ * @param key A string which is the key.
+ * @return An object which is the value.
+ */
+ public double optDouble(String key) {
+ return this.optDouble(key, Double.NaN);
+ }
+
+
+ /**
+ * Get an optional double associated with a key, or the
+ * defaultValue if there is no such key or if its value is not a number.
+ * If the value is a string, an attempt will be made to evaluate it as
+ * a number.
+ *
+ * @param key A key string.
+ * @param defaultValue The default.
+ * @return An object which is the value.
+ */
+ public double optDouble(String key, double defaultValue) {
+ try {
+ return this.getDouble(key);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+
+ /**
+ * Get an optional int value associated with a key,
+ * or zero if there is no such key or if the value is not a number.
+ * If the value is a string, an attempt will be made to evaluate it as
+ * a number.
+ *
+ * @param key A key string.
+ * @return An object which is the value.
+ */
+ public int optInt(String key) {
+ return this.optInt(key, 0);
+ }
+
+
+ /**
+ * Get an optional int value associated with a key,
+ * or the default if there is no such key or if the value is not a number.
+ * If the value is a string, an attempt will be made to evaluate it as
+ * a number.
+ *
+ * @param key A key string.
+ * @param defaultValue The default.
+ * @return An object which is the value.
+ */
+ public int optInt(String key, int defaultValue) {
+ try {
+ return this.getInt(key);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+
+ /**
+ * Get an optional JSONArray associated with a key.
+ * It returns null if there is no such key, or if its value is not a
+ * JSONArray.
+ *
+ * @param key A key string.
+ * @return A JSONArray which is the value.
+ */
+ public JSONArray optJSONArray(String key) {
+ Object o = this.opt(key);
+ return o instanceof JSONArray ? (JSONArray)o : null;
+ }
+
+
+ /**
+ * Get an optional JSONObject associated with a key.
+ * It returns null if there is no such key, or if its value is not a
+ * JSONObject.
+ *
+ * @param key A key string.
+ * @return A JSONObject which is the value.
+ */
+ public LOGJSONObject optJSONObject(String key) {
+ Object object = this.opt(key);
+ return object instanceof LOGJSONObject ? (LOGJSONObject)object : null;
+ }
+
+
+ /**
+ * Get an optional long value associated with a key,
+ * or zero if there is no such key or if the value is not a number.
+ * If the value is a string, an attempt will be made to evaluate it as
+ * a number.
+ *
+ * @param key A key string.
+ * @return An object which is the value.
+ */
+ public long optLong(String key) {
+ return this.optLong(key, 0);
+ }
+
+
+ /**
+ * Get an optional long value associated with a key,
+ * or the default if there is no such key or if the value is not a number.
+ * If the value is a string, an attempt will be made to evaluate it as
+ * a number.
+ *
+ * @param key A key string.
+ * @param defaultValue The default.
+ * @return An object which is the value.
+ */
+ public long optLong(String key, long defaultValue) {
+ try {
+ return this.getLong(key);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+
+ /**
+ * Get an optional string associated with a key.
+ * It returns an empty string if there is no such key. If the value is not
+ * a string and is not null, then it is converted to a string.
+ *
+ * @param key A key string.
+ * @return A string which is the value.
+ */
+ public String optString(String key) {
+ return this.optString(key, "");
+ }
+
+
+ /**
+ * Get an optional string associated with a key.
+ * It returns the defaultValue if there is no such key.
+ *
+ * @param key A key string.
+ * @param defaultValue The default.
+ * @return A string which is the value.
+ */
+ public String optString(String key, String defaultValue) {
+ Object object = this.opt(key);
+ return NULL.equals(object) ? defaultValue : object.toString();
+ }
+
+
+ private void populateMap(Object bean) {
+ Class<? extends Object> klass = bean.getClass();
+
+// If klass is a System class then set includeSuperClass to false.
+
+ boolean includeSuperClass = klass.getClassLoader() != null;
+
+ Method[] methods = includeSuperClass
+ ? klass.getMethods()
+ : klass.getDeclaredMethods();
+ for (int i = 0; i < methods.length; i += 1) {
+ try {
+ Method method = methods[i];
+ if (Modifier.isPublic(method.getModifiers())) {
+ String name = method.getName();
+ String key = "";
+ if (name.startsWith("get")) {
+ if ("getClass".equals(name) ||
+ "getDeclaringClass".equals(name)) {
+ key = "";
+ } else {
+ key = name.substring(3);
+ }
+ } else if (name.startsWith("is")) {
+ key = name.substring(2);
+ }
+ if (key.length() > 0 &&
+ Character.isUpperCase(key.charAt(0)) &&
+ method.getParameterTypes().length == 0) {
+ if (key.length() == 1) {
+ key = key.toLowerCase();
+ } else if (!Character.isUpperCase(key.charAt(1))) {
+ key = key.substring(0, 1).toLowerCase() +
+ key.substring(1);
+ }
+
+ Object result = method.invoke(bean, (Object[])null);
+ if (result != null) {
+ this.map.put(key, wrap(result));
+ }
+ }
+ }
+ } catch (Exception ignore) {
+ }
+ }
+ }
+
+
+ /**
+ * Put a key/boolean pair in the JSONObject.
+ *
+ * @param key A key string.
+ * @param value A boolean which is the value.
+ * @return this.
+ * @throws JSONException If the key is null.
+ */
+ public LOGJSONObject put(String key, boolean value) throws JSONException {
+ this.put(key, value ? Boolean.TRUE : Boolean.FALSE);
+ return this;
+ }
+
+
+ /**
+ * Put a key/value pair in the JSONObject, where the value will be a
+ * JSONArray which is produced from a Collection.
+ * @param key A key string.
+ * @param value A Collection value.
+ * @return this.
+ * @throws JSONException
+ */
+ public LOGJSONObject put(String key, Collection<Object> value) throws JSONException {
+ this.put(key, new JSONArray(value));
+ return this;
+ }
+
+
+ /**
+ * Put a key/double pair in the JSONObject.
+ *
+ * @param key A key string.
+ * @param value A double which is the value.
+ * @return this.
+ * @throws JSONException If the key is null or if the number is invalid.
+ */
+ public LOGJSONObject put(String key, double value) throws JSONException {
+ this.put(key, new Double(value));
+ return this;
+ }
+
+
+ /**
+ * Put a key/int pair in the JSONObject.
+ *
+ * @param key A key string.
+ * @param value An int which is the value.
+ * @return this.
+ * @throws JSONException If the key is null.
+ */
+ public LOGJSONObject put(String key, int value) throws JSONException {
+ this.put(key, new Integer(value));
+ return this;
+ }
+
+
+ /**
+ * Put a key/long pair in the JSONObject.
+ *
+ * @param key A key string.
+ * @param value A long which is the value.
+ * @return this.
+ * @throws JSONException If the key is null.
+ */
+ public LOGJSONObject put(String key, long value) throws JSONException {
+ this.put(key, new Long(value));
+ return this;
+ }
+
+
+ /**
+ * Put a key/value pair in the JSONObject, where the value will be a
+ * JSONObject which is produced from a Map.
+ * @param key A key string.
+ * @param value A Map value.
+ * @return this.
+ * @throws JSONException
+ */
+ public LOGJSONObject put(String key, Map<String, Object> value) throws JSONException {
+ this.put(key, new LOGJSONObject(value));
+ return this;
+ }
+
+
+ /**
+ * Put a key/value pair in the JSONObject. If the value is null,
+ * then the key will be removed from the JSONObject if it is present.
+ * @param key A key string.
+ * @param value An object which is the value. It should be of one of these
+ * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String,
+ * or the JSONObject.NULL object.
+ * @return this.
+ * @throws JSONException If the value is non-finite number
+ * or if the key is null.
+ */
+ public LOGJSONObject put(String key, Object value) throws JSONException {
+ String pooled;
+ if (key == null) {
+ throw new JSONException("Null key.");
+ }
+ if (value != null) {
+ testValidity(value);
+ pooled = (String)keyPool.get(key);
+ if (pooled == null) {
+ if (keyPool.size() >= keyPoolSize) {
+ keyPool = new LinkedHashMap<String, Object>(keyPoolSize);
+ }
+ keyPool.put(key, key);
+ } else {
+ key = pooled;
+ }
+ this.map.put(key, value);
+ } else {
+ this.remove(key);
+ }
+ return this;
+ }
+
+
+ /**
+ * Put a key/value pair in the JSONObject, but only if the key and the
+ * value are both non-null, and only if there is not already a member
+ * with that name.
+ * @param key
+ * @param value
+ * @return his.
+ * @throws JSONException if the key is a duplicate
+ */
+ public LOGJSONObject putOnce(String key, Object value) throws JSONException {
+ if (key != null && value != null) {
+ if (this.opt(key) != null) {
+ throw new JSONException("Duplicate key \"" + key + "\"");
+ }
+ this.put(key, value);
+ }
+ return this;
+ }
+
+
+ /**
+ * Put a key/value pair in the JSONObject, but only if the
+ * key and the value are both non-null.
+ * @param key A key string.
+ * @param value An object which is the value. It should be of one of these
+ * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String,
+ * or the JSONObject.NULL object.
+ * @return this.
+ * @throws JSONException If the value is a non-finite number.
+ */
+ public LOGJSONObject putOpt(String key, Object value) throws JSONException {
+ if (key != null && value != null) {
+ this.put(key, value);
+ }
+ return this;
+ }
+
+
+ /**
+ * Produce a string in double quotes with backslash sequences in all the
+ * right places. A backslash will be inserted within </, producing <\/,
+ * allowing JSON text to be delivered in HTML. In JSON text, a string
+ * cannot contain a control character or an unescaped quote or backslash.
+ * @param string A String
+ * @return A String correctly formatted for insertion in a JSON text.
+ */
+ public static String quote(String string) {
+ StringWriter sw = new StringWriter();
+ synchronized (sw.getBuffer()) {
+ try {
+ return quote(string, sw).toString();
+ } catch (IOException ignored) {
+ // will never happen - we are writing to a string writer
+ return "";
+ }
+ }
+ }
+
+ public static Writer quote(String string, Writer w) throws IOException {
+ if (string == null || string.length() == 0) {
+ w.write("\"\"");
+ return w;
+ }
+
+ char b;
+ char c = 0;
+ String hhhh;
+ int i;
+ int len = string.length();
+
+ w.write('"');
+ for (i = 0; i < len; i += 1) {
+ b = c;
+ c = string.charAt(i);
+ switch (c) {
+ case '\\':
+ case '"':
+ w.write('\\');
+ w.write(c);
+ break;
+ case '/':
+ if (b == '<') {
+ w.write('\\');
+ }
+ w.write(c);
+ break;
+ case '\b':
+ w.write("\\b");
+ break;
+ case '\t':
+ w.write("\\t");
+ break;
+ case '\n':
+ w.write("\\n");
+ break;
+ case '\f':
+ w.write("\\f");
+ break;
+ case '\r':
+ w.write("\\r");
+ break;
+ default:
+ if (c < ' ' || (c >= '\u0080' && c < '\u00a0')
+ || (c >= '\u2000' && c < '\u2100')) {
+ w.write("\\u");
+ hhhh = Integer.toHexString(c);
+ w.write("0000", 0, 4 - hhhh.length());
+ w.write(hhhh);
+ } else {
+ w.write(c);
+ }
+ }
+ }
+ w.write('"');
+ return w;
+ }
+
+ /**
+ * Remove a name and its value, if present.
+ * @param key The name to be removed.
+ * @return The value that was associated with the name,
+ * or null if there was no value.
+ */
+ public Object remove(String key) {
+ return this.map.remove(key);
+ }
+
+ /**
+ * Try to convert a string into a number, boolean, or null. If the string
+ * can't be converted, return the string.
+ * @param string A String.
+ * @return A simple JSON value.
+ */
+ public static Object stringToValue(String string) {
+ Double d;
+ if (string.equals("")) {
+ return string;
+ }
+ if (string.equalsIgnoreCase("true")) {
+ return Boolean.TRUE;
+ }
+ if (string.equalsIgnoreCase("false")) {
+ return Boolean.FALSE;
+ }
+ if (string.equalsIgnoreCase("null")) {
+ return LOGJSONObject.NULL;
+ }
+
+ /*
+ * If it might be a number, try converting it.
+ * If a number cannot be produced, then the value will just
+ * be a string. Note that the plus and implied string
+ * conventions are non-standard. A JSON parser may accept
+ * non-JSON forms as long as it accepts all correct JSON forms.
+ */
+
+ char b = string.charAt(0);
+ if ((b >= '0' && b <= '9') || b == '.' || b == '-' || b == '+') {
+ try {
+ if (string.indexOf('.') > -1 ||
+ string.indexOf('e') > -1 || string.indexOf('E') > -1) {
+ d = Double.valueOf(string);
+ if (!d.isInfinite() && !d.isNaN()) {
+ return d;
+ }
+ } else {
+ Long myLong = new Long(string);
+ if (myLong.longValue() == myLong.intValue()) {
+ return new Integer(myLong.intValue());
+ } else {
+ return myLong;
+ }
+ }
+ } catch (Exception ignore) {
+ }
+ }
+ return string;
+ }
+
+
+ /**
+ * Throw an exception if the object is a NaN or infinite number.
+ * @param o The object to test.
+ * @throws JSONException If o is a non-finite number.
+ */
+ public static void testValidity(Object o) throws JSONException {
+ if (o != null) {
+ if (o instanceof Double) {
+ if (((Double)o).isInfinite() || ((Double)o).isNaN()) {
+ throw new JSONException(
+ "JSON does not allow non-finite numbers.");
+ }
+ } else if (o instanceof Float) {
+ if (((Float)o).isInfinite() || ((Float)o).isNaN()) {
+ throw new JSONException(
+ "JSON does not allow non-finite numbers.");
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Produce a JSONArray containing the values of the members of this
+ * JSONObject.
+ * @param names A JSONArray containing a list of key strings. This
+ * determines the sequence of the values in the result.
+ * @return A JSONArray of values.
+ * @throws JSONException If any of the values are non-finite numbers.
+ */
+ public JSONArray toJSONArray(JSONArray names) throws JSONException {
+ if (names == null || names.length() == 0) {
+ return null;
+ }
+ JSONArray ja = new JSONArray();
+ for (int i = 0; i < names.length(); i += 1) {
+ ja.put(this.opt(names.getString(i)));
+ }
+ return ja;
+ }
+
+ /**
+ * Make a JSON text of this JSONObject. For compactness, no whitespace
+ * is added. If this would not result in a syntactically correct JSON text,
+ * then null will be returned instead.
+ * <p>
+ * Warning: This method assumes that the data structure is acyclical.
+ *
+ * @return a printable, displayable, portable, transmittable
+ * representation of the object, beginning
+ * with <code>{</code>&nbsp;<small>(left brace)</small> and ending
+ * with <code>}</code>&nbsp;<small>(right brace)</small>.
+ */
+ public String toString() {
+ try {
+ return this.toString(0);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+
+ /**
+ * Make a prettyprinted JSON text of this JSONObject.
+ * <p>
+ * Warning: This method assumes that the data structure is acyclical.
+ * @param indentFactor The number of spaces to add to each level of
+ * indentation.
+ * @return a printable, displayable, portable, transmittable
+ * representation of the object, beginning
+ * with <code>{</code>&nbsp;<small>(left brace)</small> and ending
+ * with <code>}</code>&nbsp;<small>(right brace)</small>.
+ * @throws JSONException If the object contains an invalid number.
+ */
+ public String toString(int indentFactor) throws JSONException {
+ StringWriter w = new StringWriter();
+ synchronized (w.getBuffer()) {
+ return this.write(w, indentFactor, 0).toString();
+ }
+ }
+
+ /**
+ * Make a JSON text of an Object value. If the object has an
+ * value.toJSONString() method, then that method will be used to produce
+ * the JSON text. The method is required to produce a strictly
+ * conforming text. If the object does not contain a toJSONString
+ * method (which is the most common case), then a text will be
+ * produced by other means. If the value is an array or Collection,
+ * then a JSONArray will be made from it and its toJSONString method
+ * will be called. If the value is a MAP, then a JSONObject will be made
+ * from it and its toJSONString method will be called. Otherwise, the
+ * value's toString method will be called, and the result will be quoted.
+ *
+ * <p>
+ * Warning: This method assumes that the data structure is acyclical.
+ * @param value The value to be serialized.
+ * @return a printable, displayable, transmittable
+ * representation of the object, beginning
+ * with <code>{</code>&nbsp;<small>(left brace)</small> and ending
+ * with <code>}</code>&nbsp;<small>(right brace)</small>.
+ * @throws JSONException If the value is or contains an invalid number.
+ */
+ @SuppressWarnings("unchecked")
+ public static String valueToString(Object value) throws JSONException {
+ if (value == null || value.equals(null)) {
+ return "null";
+ }
+ if (value instanceof JSONString) {
+ Object object;
+ try {
+ object = ((JSONString)value).toJSONString();
+ } catch (Exception e) {
+ throw new JSONException(e);
+ }
+ if (object instanceof String) {
+ return (String)object;
+ }
+ throw new JSONException("Bad value from toJSONString: " + object);
+ }
+ if (value instanceof Number) {
+ return numberToString((Number) value);
+ }
+ if (value instanceof Boolean || value instanceof LOGJSONObject ||
+ value instanceof JSONArray) {
+ return value.toString();
+ }
+ if (value instanceof Map) {
+ return new LOGJSONObject((Map<String, Object>)value).toString();
+ }
+ if (value instanceof Collection) {
+ return new JSONArray((Collection<Object>)value).toString();
+ }
+ if (value.getClass().isArray()) {
+ return new JSONArray(value).toString();
+ }
+ return quote(value.toString());
+ }
+
+ /**
+ * Wrap an object, if necessary. If the object is null, return the NULL
+ * object. If it is an array or collection, wrap it in a JSONArray. If
+ * it is a map, wrap it in a JSONObject. If it is a standard property
+ * (Double, String, et al) then it is already wrapped. Otherwise, if it
+ * comes from one of the java packages, turn it into a string. And if
+ * it doesn't, try to wrap it in a JSONObject. If the wrapping fails,
+ * then null is returned.
+ *
+ * @param object The object to wrap
+ * @return The wrapped value
+ */
+ @SuppressWarnings("unchecked")
+ public static Object wrap(Object object) {
+ try {
+ if (object == null) {
+ return NULL;
+ }
+ if (object instanceof LOGJSONObject || object instanceof JSONArray ||
+ NULL.equals(object) || object instanceof JSONString ||
+ object instanceof Byte || object instanceof Character ||
+ object instanceof Short || object instanceof Integer ||
+ object instanceof Long || object instanceof Boolean ||
+ object instanceof Float || object instanceof Double ||
+ object instanceof String) {
+ return object;
+ }
+
+ if (object instanceof Collection) {
+ return new JSONArray((Collection<Object>)object);
+ }
+ if (object.getClass().isArray()) {
+ return new JSONArray(object);
+ }
+ if (object instanceof Map) {
+ return new LOGJSONObject((Map<String, Object>)object);
+ }
+ Package objectPackage = object.getClass().getPackage();
+ String objectPackageName = objectPackage != null
+ ? objectPackage.getName()
+ : "";
+ if (
+ objectPackageName.startsWith("java.") ||
+ objectPackageName.startsWith("javax.") ||
+ object.getClass().getClassLoader() == null
+ ) {
+ return object.toString();
+ }
+ return new LOGJSONObject(object);
+ } catch(Exception exception) {
+ return null;
+ }
+ }
+
+
+ /**
+ * Write the contents of the JSONObject as JSON text to a writer.
+ * For compactness, no whitespace is added.
+ * <p>
+ * Warning: This method assumes that the data structure is acyclical.
+ *
+ * @return The writer.
+ * @throws JSONException
+ */
+ public Writer write(Writer writer) throws JSONException {
+ return this.write(writer, 0, 0);
+ }
+
+
+ @SuppressWarnings("unchecked")
+ static final Writer writeValue(Writer writer, Object value,
+ int indentFactor, int indent) throws JSONException, IOException {
+ if (value == null || value.equals(null)) {
+ writer.write("null");
+ } else if (value instanceof LOGJSONObject) {
+ ((LOGJSONObject) value).write(writer, indentFactor, indent);
+ } else if (value instanceof JSONArray) {
+ ((JSONArray) value).write(writer, indentFactor, indent);
+ } else if (value instanceof Map) {
+ new LOGJSONObject((Map<String, Object>) value).write(writer, indentFactor, indent);
+ } else if (value instanceof Collection) {
+ new JSONArray((Collection<Object>) value).write(writer, indentFactor,
+ indent);
+ } else if (value.getClass().isArray()) {
+ new JSONArray(value).write(writer, indentFactor, indent);
+ } else if (value instanceof Number) {
+ writer.write(numberToString((Number) value));
+ } else if (value instanceof Boolean) {
+ writer.write(value.toString());
+ } else if (value instanceof JSONString) {
+ Object o;
+ try {
+ o = ((JSONString) value).toJSONString();
+ } catch (Exception e) {
+ throw new JSONException(e);
+ }
+ writer.write(o != null ? o.toString() : quote(value.toString()));
+ } else {
+ quote(value.toString(), writer);
+ }
+ return writer;
+ }
+
+ static final void indent(Writer writer, int indent) throws IOException {
+ for (int i = 0; i < indent; i += 1) {
+ writer.write(' ');
+ }
+ }
+
+ /**
+ * Write the contents of the JSONObject as JSON text to a writer. For
+ * compactness, no whitespace is added.
+ * <p>
+ * Warning: This method assumes that the data structure is acyclical.
+ *
+ * @return The writer.
+ * @throws JSONException
+ */
+ Writer write(Writer writer, int indentFactor, int indent)
+ throws JSONException {
+ try {
+ boolean commanate = false;
+ final int length = this.length();
+ Iterator<String> keys = this.keys();
+ writer.write('{');
+
+ if (length == 1) {
+ Object key = keys.next();
+ writer.write(quote(key.toString()));
+ writer.write(':');
+ if (indentFactor > 0) {
+ writer.write(' ');
+ }
+ writeValue(writer, this.map.get(key), indentFactor, indent);
+ } else if (length != 0) {
+ final int newindent = indent + indentFactor;
+ while (keys.hasNext()) {
+ Object key = keys.next();
+ if (commanate) {
+ writer.write(',');
+ }
+ if (indentFactor > 0) {
+ writer.write('\n');
+ }
+ indent(writer, newindent);
+ writer.write(quote(key.toString()));
+ writer.write(':');
+ if (indentFactor > 0) {
+ writer.write(' ');
+ }
+ writeValue(writer, this.map.get(key), indentFactor,
+ newindent);
+ commanate = true;
+ }
+ if (indentFactor > 0) {
+ writer.write('\n');
+ }
+ indent(writer, indent);
+ }
+ writer.write('}');
+ return writer;
+ } catch (IOException exception) {
+ throw new JSONException(exception);
+ }
+ }
+}
diff --git a/datarouter-prov/src/main/java/org/json/None.java b/datarouter-prov/src/main/java/org/json/None.java
new file mode 100644
index 00000000..5b9a47d3
--- /dev/null
+++ b/datarouter-prov/src/main/java/org/json/None.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package org.json;
+
+public interface None {
+ /**
+ * Negative One
+ */
+ public static final int none = -1;
+
+}
diff --git a/datarouter-prov/src/main/java/org/json/XML.java b/datarouter-prov/src/main/java/org/json/XML.java
new file mode 100644
index 00000000..33f43e51
--- /dev/null
+++ b/datarouter-prov/src/main/java/org/json/XML.java
@@ -0,0 +1,530 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package org.json;
+
+/*
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+import java.util.Iterator;
+
+
+/**
+ * This provides static methods to convert an XML text into a JSONObject,
+ * and to covert a JSONObject into an XML text.
+ * @author JSON.org
+ * @version 2012-10-26
+ */
+public class XML {
+
+ /** The Character '&amp;'. */
+ public static final Character AMP = new Character('&');
+
+ /** The Character '''. */
+ public static final Character APOS = new Character('\'');
+
+ /** The Character '!'. */
+ public static final Character BANG = new Character('!');
+
+ /** The Character '='. */
+ public static final Character EQ = new Character('=');
+
+ /** The Character '>'. */
+ public static final Character GT = new Character('>');
+
+ /** The Character '&lt;'. */
+ public static final Character LT = new Character('<');
+
+ /** The Character '?'. */
+ public static final Character QUEST = new Character('?');
+
+ /** The Character '"'. */
+ public static final Character QUOT = new Character('"');
+
+ /** The Character '/'. */
+ public static final Character SLASH = new Character('/');
+
+ /**
+ * Replace special characters with XML escapes:
+ * <pre>
+ * &amp; <small>(ampersand)</small> is replaced by &amp;amp;
+ * &lt; <small>(less than)</small> is replaced by &amp;lt;
+ * &gt; <small>(greater than)</small> is replaced by &amp;gt;
+ * &quot; <small>(double quote)</small> is replaced by &amp;quot;
+ * </pre>
+ * @param string The string to be escaped.
+ * @return The escaped string.
+ */
+ public static String escape(String string) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0, length = string.length(); i < length; i++) {
+ char c = string.charAt(i);
+ switch (c) {
+ case '&':
+ sb.append("&amp;");
+ break;
+ case '<':
+ sb.append("&lt;");
+ break;
+ case '>':
+ sb.append("&gt;");
+ break;
+ case '"':
+ sb.append("&quot;");
+ break;
+ case '\'':
+ sb.append("&apos;");
+ break;
+ default:
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Throw an exception if the string contains whitespace.
+ * Whitespace is not allowed in tagNames and attributes.
+ * @param string
+ * @throws JSONException
+ */
+ public static void noSpace(String string) throws JSONException {
+ int i, length = string.length();
+ if (length == 0) {
+ throw new JSONException("Empty string.");
+ }
+ for (i = 0; i < length; i += 1) {
+ if (Character.isWhitespace(string.charAt(i))) {
+ throw new JSONException("'" + string +
+ "' contains a space character.");
+ }
+ }
+ }
+
+ /**
+ * Scan the content following the named tag, attaching it to the context.
+ * @param x The XMLTokener containing the source string.
+ * @param context The JSONObject that will include the new material.
+ * @param name The tag name.
+ * @return true if the close tag is processed.
+ * @throws JSONException
+ */
+ private static boolean parse(XMLTokener x, JSONObject context,
+ String name) throws JSONException {
+ char c;
+ int i;
+ JSONObject jsonobject = null;
+ String string;
+ String tagName;
+ Object token;
+
+// Test for and skip past these forms:
+// <!-- ... -->
+// <! ... >
+// <![ ... ]]>
+// <? ... ?>
+// Report errors for these forms:
+// <>
+// <=
+// <<
+
+ token = x.nextToken();
+
+// <!
+
+ if (token == BANG) {
+ c = x.next();
+ if (c == '-') {
+ if (x.next() == '-') {
+ x.skipPast("-->");
+ return false;
+ }
+ x.back();
+ } else if (c == '[') {
+ token = x.nextToken();
+ if ("CDATA".equals(token)) {
+ if (x.next() == '[') {
+ string = x.nextCDATA();
+ if (string.length() > 0) {
+ context.accumulate("content", string);
+ }
+ return false;
+ }
+ }
+ throw x.syntaxError("Expected 'CDATA['");
+ }
+ i = 1;
+ do {
+ token = x.nextMeta();
+ if (token == null) {
+ throw x.syntaxError("Missing '>' after '<!'.");
+ } else if (token == LT) {
+ i += 1;
+ } else if (token == GT) {
+ i -= 1;
+ }
+ } while (i > 0);
+ return false;
+ } else if (token == QUEST) {
+
+// <?
+
+ x.skipPast("?>");
+ return false;
+ } else if (token == SLASH) {
+
+// Close tag </
+
+ token = x.nextToken();
+ if (name == null) {
+ throw x.syntaxError("Mismatched close tag " + token);
+ }
+ if (!token.equals(name)) {
+ throw x.syntaxError("Mismatched " + name + " and " + token);
+ }
+ if (x.nextToken() != GT) {
+ throw x.syntaxError("Misshaped close tag");
+ }
+ return true;
+
+ } else if (token instanceof Character) {
+ throw x.syntaxError("Misshaped tag");
+
+// Open tag <
+
+ } else {
+ tagName = (String)token;
+ token = null;
+ jsonobject = new JSONObject();
+ for (;;) {
+ if (token == null) {
+ token = x.nextToken();
+ }
+
+// attribute = value
+
+ if (token instanceof String) {
+ string = (String)token;
+ token = x.nextToken();
+ if (token == EQ) {
+ token = x.nextToken();
+ if (!(token instanceof String)) {
+ throw x.syntaxError("Missing value");
+ }
+ jsonobject.accumulate(string,
+ XML.stringToValue((String)token));
+ token = null;
+ } else {
+ jsonobject.accumulate(string, "");
+ }
+
+// Empty tag <.../>
+
+ } else if (token == SLASH) {
+ if (x.nextToken() != GT) {
+ throw x.syntaxError("Misshaped tag");
+ }
+ if (jsonobject.length() > 0) {
+ context.accumulate(tagName, jsonobject);
+ } else {
+ context.accumulate(tagName, "");
+ }
+ return false;
+
+// Content, between <...> and </...>
+
+ } else if (token == GT) {
+ for (;;) {
+ token = x.nextContent();
+ if (token == null) {
+ if (tagName != null) {
+ throw x.syntaxError("Unclosed tag " + tagName);
+ }
+ return false;
+ } else if (token instanceof String) {
+ string = (String)token;
+ if (string.length() > 0) {
+ jsonobject.accumulate("content",
+ XML.stringToValue(string));
+ }
+
+// Nested element
+
+ } else if (token == LT) {
+ if (parse(x, jsonobject, tagName)) {
+ if (jsonobject.length() == 0) {
+ context.accumulate(tagName, "");
+ } else if (jsonobject.length() == 1 &&
+ jsonobject.opt("content") != null) {
+ context.accumulate(tagName,
+ jsonobject.opt("content"));
+ } else {
+ context.accumulate(tagName, jsonobject);
+ }
+ return false;
+ }
+ }
+ }
+ } else {
+ throw x.syntaxError("Misshaped tag");
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Try to convert a string into a number, boolean, or null. If the string
+ * can't be converted, return the string. This is much less ambitious than
+ * JSONObject.stringToValue, especially because it does not attempt to
+ * convert plus forms, octal forms, hex forms, or E forms lacking decimal
+ * points.
+ * @param string A String.
+ * @return A simple JSON value.
+ */
+ public static Object stringToValue(String string) {
+ if ("".equals(string)) {
+ return string;
+ }
+ if ("true".equalsIgnoreCase(string)) {
+ return Boolean.TRUE;
+ }
+ if ("false".equalsIgnoreCase(string)) {
+ return Boolean.FALSE;
+ }
+ if ("null".equalsIgnoreCase(string)) {
+ return JSONObject.NULL;
+ }
+ if ("0".equals(string)) {
+ return new Integer(0);
+ }
+
+// If it might be a number, try converting it. If that doesn't work,
+// return the string.
+
+ try {
+ char initial = string.charAt(0);
+ boolean negative = false;
+ if (initial == '-') {
+ initial = string.charAt(1);
+ negative = true;
+ }
+ if (initial == '0' && string.charAt(negative ? 2 : 1) == '0') {
+ return string;
+ }
+ if ((initial >= '0' && initial <= '9')) {
+ if (string.indexOf('.') >= 0) {
+ return Double.valueOf(string);
+ } else if (string.indexOf('e') < 0 && string.indexOf('E') < 0) {
+ Long myLong = new Long(string);
+ if (myLong.longValue() == myLong.intValue()) {
+ return new Integer(myLong.intValue());
+ } else {
+ return myLong;
+ }
+ }
+ }
+ } catch (Exception ignore) {
+ }
+ return string;
+ }
+
+
+ /**
+ * Convert a well-formed (but not necessarily valid) XML string into a
+ * JSONObject. Some information may be lost in this transformation
+ * because JSON is a data format and XML is a document format. XML uses
+ * elements, attributes, and content text, while JSON uses unordered
+ * collections of name/value pairs and arrays of values. JSON does not
+ * does not like to distinguish between elements and attributes.
+ * Sequences of similar elements are represented as JSONArrays. Content
+ * text may be placed in a "content" member. Comments, prologs, DTDs, and
+ * <code>&lt;[ [ ]]></code> are ignored.
+ * @param string The source string.
+ * @return A JSONObject containing the structured data from the XML string.
+ * @throws JSONException
+ */
+ public static JSONObject toJSONObject(String string) throws JSONException {
+ JSONObject jo = new JSONObject();
+ XMLTokener x = new XMLTokener(string);
+ while (x.more() && x.skipPast("<")) {
+ parse(x, jo, null);
+ }
+ return jo;
+ }
+
+
+ /**
+ * Convert a JSONObject into a well-formed, element-normal XML string.
+ * @param object A JSONObject.
+ * @return A string.
+ * @throws JSONException
+ */
+ public static String toString(Object object) throws JSONException {
+ return toString(object, null);
+ }
+
+
+ /**
+ * Convert a JSONObject into a well-formed, element-normal XML string.
+ * @param object A JSONObject.
+ * @param tagName The optional name of the enclosing tag.
+ * @return A string.
+ * @throws JSONException
+ */
+ public static String toString(Object object, String tagName)
+ throws JSONException {
+ StringBuffer sb = new StringBuffer();
+ int i;
+ JSONArray ja;
+ JSONObject jo;
+ String key;
+ Iterator<String> keys;
+ int length;
+ String string;
+ Object value;
+ if (object instanceof JSONObject) {
+
+// Emit <tagName>
+
+ if (tagName != null) {
+ sb.append('<');
+ sb.append(tagName);
+ sb.append('>');
+ }
+
+// Loop thru the keys.
+
+ jo = (JSONObject)object;
+ keys = jo.keys();
+ while (keys.hasNext()) {
+ key = keys.next().toString();
+ value = jo.opt(key);
+ if (value == null) {
+ value = "";
+ }
+ if (value instanceof String) {
+ string = (String)value;
+ } else {
+ string = null;
+ }
+
+// Emit content in body
+
+ if ("content".equals(key)) {
+ if (value instanceof JSONArray) {
+ ja = (JSONArray)value;
+ length = ja.length();
+ for (i = 0; i < length; i += 1) {
+ if (i > 0) {
+ sb.append('\n');
+ }
+ sb.append(escape(ja.get(i).toString()));
+ }
+ } else {
+ sb.append(escape(value.toString()));
+ }
+
+// Emit an array of similar keys
+
+ } else if (value instanceof JSONArray) {
+ ja = (JSONArray)value;
+ length = ja.length();
+ for (i = 0; i < length; i += 1) {
+ value = ja.get(i);
+ if (value instanceof JSONArray) {
+ sb.append('<');
+ sb.append(key);
+ sb.append('>');
+ sb.append(toString(value));
+ sb.append("</");
+ sb.append(key);
+ sb.append('>');
+ } else {
+ sb.append(toString(value, key));
+ }
+ }
+ } else if ("".equals(value)) {
+ sb.append('<');
+ sb.append(key);
+ sb.append("/>");
+
+// Emit a new tag <k>
+
+ } else {
+ sb.append(toString(value, key));
+ }
+ }
+ if (tagName != null) {
+
+// Emit the </tagname> close tag
+
+ sb.append("</");
+ sb.append(tagName);
+ sb.append('>');
+ }
+ return sb.toString();
+
+// XML does not have good support for arrays. If an array appears in a place
+// where XML is lacking, synthesize an <array> element.
+
+ } else {
+ if (object.getClass().isArray()) {
+ object = new JSONArray(object);
+ }
+ if (object instanceof JSONArray) {
+ ja = (JSONArray)object;
+ length = ja.length();
+ for (i = 0; i < length; i += 1) {
+ sb.append(toString(ja.opt(i), tagName == null ? "array" : tagName));
+ }
+ return sb.toString();
+ } else {
+ string = (object == null) ? "null" : escape(object.toString());
+ return (tagName == null) ? "\"" + string + "\"" :
+ (string.length() == 0) ? "<" + tagName + "/>" :
+ "<" + tagName + ">" + string + "</" + tagName + ">";
+ }
+ }
+ }
+}
diff --git a/datarouter-prov/src/main/java/org/json/XMLTokener.java b/datarouter-prov/src/main/java/org/json/XMLTokener.java
new file mode 100644
index 00000000..bdb54666
--- /dev/null
+++ b/datarouter-prov/src/main/java/org/json/XMLTokener.java
@@ -0,0 +1,390 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package org.json;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/*
+Copyright (c) 2002 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+/**
+ * The XMLTokener extends the JSONTokener to provide additional methods
+ * for the parsing of XML texts.
+ * @author JSON.org
+ * @version 2012-11-13
+ */
+public class XMLTokener extends JSONTokener {
+
+
+ /** The table of entity values. It initially contains Character values for
+ * amp, apos, gt, lt, quot.
+ */
+ public static final Map<String,Character> entity;
+
+ static {
+ entity = new HashMap<String,Character>(8);
+ entity.put("amp", XML.AMP);
+ entity.put("apos", XML.APOS);
+ entity.put("gt", XML.GT);
+ entity.put("lt", XML.LT);
+ entity.put("quot", XML.QUOT);
+ }
+
+ /**
+ * Construct an XMLTokener from a string.
+ * @param s A source string.
+ */
+ public XMLTokener(String s) {
+ super(s);
+ }
+
+ /**
+ * Get the text in the CDATA block.
+ * @return The string up to the <code>]]&gt;</code>.
+ * @throws JSONException If the <code>]]&gt;</code> is not found.
+ */
+ public String nextCDATA() throws JSONException {
+ char c;
+ int i;
+ StringBuffer sb = new StringBuffer();
+ for (;;) {
+ c = next();
+ if (end()) {
+ throw syntaxError("Unclosed CDATA");
+ }
+ sb.append(c);
+ i = sb.length() - 3;
+ if (i >= 0 && sb.charAt(i) == ']' &&
+ sb.charAt(i + 1) == ']' && sb.charAt(i + 2) == '>') {
+ sb.setLength(i);
+ return sb.toString();
+ }
+ }
+ }
+
+
+ /**
+ * Get the next XML outer token, trimming whitespace. There are two kinds
+ * of tokens: the '<' character which begins a markup tag, and the content
+ * text between markup tags.
+ *
+ * @return A string, or a '<' Character, or null if there is no more
+ * source text.
+ * @throws JSONException
+ */
+ public Object nextContent() throws JSONException {
+ char c;
+ StringBuffer sb;
+ do {
+ c = next();
+ } while (Character.isWhitespace(c));
+ if (c == 0) {
+ return null;
+ }
+ if (c == '<') {
+ return XML.LT;
+ }
+ sb = new StringBuffer();
+ for (;;) {
+ if (c == '<' || c == 0) {
+ back();
+ return sb.toString().trim();
+ }
+ if (c == '&') {
+ sb.append(nextEntity(c));
+ } else {
+ sb.append(c);
+ }
+ c = next();
+ }
+ }
+
+
+ /**
+ * Return the next entity. These entities are translated to Characters:
+ * <code>&amp; &apos; &gt; &lt; &quot;</code>.
+ * @param ampersand An ampersand character.
+ * @return A Character or an entity String if the entity is not recognized.
+ * @throws JSONException If missing ';' in XML entity.
+ */
+ public Object nextEntity(char ampersand) throws JSONException {
+ StringBuffer sb = new StringBuffer();
+ for (;;) {
+ char c = next();
+ if (Character.isLetterOrDigit(c) || c == '#') {
+ sb.append(Character.toLowerCase(c));
+ } else if (c == ';') {
+ break;
+ } else {
+ throw syntaxError("Missing ';' in XML entity: &" + sb);
+ }
+ }
+ String string = sb.toString();
+ Object object = entity.get(string);
+ return object != null ? object : ampersand + string + ";";
+ }
+
+
+ /**
+ * Returns the next XML meta token. This is used for skipping over <!...>
+ * and <?...?> structures.
+ * @return Syntax characters (<code>< > / = ! ?</code>) are returned as
+ * Character, and strings and names are returned as Boolean. We don't care
+ * what the values actually are.
+ * @throws JSONException If a string is not properly closed or if the XML
+ * is badly structured.
+ */
+ public Object nextMeta() throws JSONException {
+ char c;
+ char q;
+ do {
+ c = next();
+ } while (Character.isWhitespace(c));
+ switch (c) {
+ case 0:
+ throw syntaxError("Misshaped meta tag");
+ case '<':
+ return XML.LT;
+ case '>':
+ return XML.GT;
+ case '/':
+ return XML.SLASH;
+ case '=':
+ return XML.EQ;
+ case '!':
+ return XML.BANG;
+ case '?':
+ return XML.QUEST;
+ case '"':
+ case '\'':
+ q = c;
+ for (;;) {
+ c = next();
+ if (c == 0) {
+ throw syntaxError("Unterminated string");
+ }
+ if (c == q) {
+ return Boolean.TRUE;
+ }
+ }
+ default:
+ for (;;) {
+ c = next();
+ if (Character.isWhitespace(c)) {
+ return Boolean.TRUE;
+ }
+ switch (c) {
+ case 0:
+ case '<':
+ case '>':
+ case '/':
+ case '=':
+ case '!':
+ case '?':
+ case '"':
+ case '\'':
+ back();
+ return Boolean.TRUE;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Get the next XML Token. These tokens are found inside of angle
+ * brackets. It may be one of these characters: <code>/ > = ! ?</code> or it
+ * may be a string wrapped in single quotes or double quotes, or it may be a
+ * name.
+ * @return a String or a Character.
+ * @throws JSONException If the XML is not well formed.
+ */
+ public Object nextToken() throws JSONException {
+ char c;
+ char q;
+ StringBuffer sb;
+ do {
+ c = next();
+ } while (Character.isWhitespace(c));
+ switch (c) {
+ case 0:
+ throw syntaxError("Misshaped element");
+ case '<':
+ throw syntaxError("Misplaced '<'");
+ case '>':
+ return XML.GT;
+ case '/':
+ return XML.SLASH;
+ case '=':
+ return XML.EQ;
+ case '!':
+ return XML.BANG;
+ case '?':
+ return XML.QUEST;
+
+// Quoted string
+
+ case '"':
+ case '\'':
+ q = c;
+ sb = new StringBuffer();
+ for (;;) {
+ c = next();
+ if (c == 0) {
+ throw syntaxError("Unterminated string");
+ }
+ if (c == q) {
+ return sb.toString();
+ }
+ if (c == '&') {
+ sb.append(nextEntity(c));
+ } else {
+ sb.append(c);
+ }
+ }
+ default:
+
+// Name
+
+ sb = new StringBuffer();
+ for (;;) {
+ sb.append(c);
+ c = next();
+ if (Character.isWhitespace(c)) {
+ return sb.toString();
+ }
+ switch (c) {
+ case 0:
+ return sb.toString();
+ case '>':
+ case '/':
+ case '=':
+ case '!':
+ case '?':
+ case '[':
+ case ']':
+ back();
+ return sb.toString();
+ case '<':
+ case '"':
+ case '\'':
+ throw syntaxError("Bad character in a name");
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Skip characters until past the requested string.
+ * If it is not found, we are left at the end of the source with a result of false.
+ * @param to A string to skip past.
+ * @throws JSONException
+ */
+ public boolean skipPast(String to) throws JSONException {
+ boolean b;
+ char c;
+ int i;
+ int j;
+ int offset = 0;
+ int length = to.length();
+ char[] circle = new char[length];
+
+ /*
+ * First fill the circle buffer with as many characters as are in the
+ * to string. If we reach an early end, bail.
+ */
+
+ for (i = 0; i < length; i += 1) {
+ c = next();
+ if (c == 0) {
+ return false;
+ }
+ circle[i] = c;
+ }
+
+ /* We will loop, possibly for all of the remaining characters. */
+
+ for (;;) {
+ j = offset;
+ b = true;
+
+ /* Compare the circle buffer with the to string. */
+
+ for (i = 0; i < length; i += 1) {
+ if (circle[j] != to.charAt(i)) {
+ b = false;
+ break;
+ }
+ j += 1;
+ if (j >= length) {
+ j -= length;
+ }
+ }
+
+ /* If we exit the loop with b intact, then victory is ours. */
+
+ if (b) {
+ return true;
+ }
+
+ /* Get the next character. If there isn't one, then defeat is ours. */
+
+ c = next();
+ if (c == 0) {
+ return false;
+ }
+ /*
+ * Shove the character in the circle buffer and advance the
+ * circle offset. The offset is mod n.
+ */
+ circle[offset] = c;
+ offset += 1;
+ if (offset >= length) {
+ offset -= length;
+ }
+ }
+ }
+}
diff --git a/datarouter-prov/src/main/java/org/json/package.html b/datarouter-prov/src/main/java/org/json/package.html
new file mode 100644
index 00000000..392a0c68
--- /dev/null
+++ b/datarouter-prov/src/main/java/org/json/package.html
@@ -0,0 +1,40 @@
+#-------------------------------------------------------------------------------
+# ============LICENSE_START==================================================
+# * org.onap.dmaap
+# * ===========================================================================
+# * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+# * ===========================================================================
+# * 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.
+# * ============LICENSE_END====================================================
+# *
+# * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+# *
+#-------------------------------------------------------------------------------
+<!-- CVS: $Id: package.html,v 1.1 2013/04/26 21:01:51 eby Exp $ -->
+<!--
+ AT&T - PROPRIETARY
+ THIS FILE CONTAINS PROPRIETARY INFORMATION OF
+ AT&T AND IS NOT TO BE DISCLOSED OR USED EXCEPT IN
+ ACCORDANCE WITH APPLICABLE AGREEMENTS.
+
+ Copyright (c) 2013 AT&T Knowledge Ventures
+ Unpublished and Not for Publication
+ All Rights Reserved
+-->
+<html>
+<body>
+<p>
+This package provides the json.org JSON library.
+</p>
+</body>
+</html>
diff --git a/datarouter-prov/src/main/resources/EelfMessages.properties b/datarouter-prov/src/main/resources/EelfMessages.properties
new file mode 100644
index 00000000..5e8b179c
--- /dev/null
+++ b/datarouter-prov/src/main/resources/EelfMessages.properties
@@ -0,0 +1,58 @@
+#-------------------------------------------------------------------------------
+# ============LICENSE_START==================================================
+# * org.onap.dmaap
+# * ===========================================================================
+# * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+# * ===========================================================================
+# * 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.
+# * ============LICENSE_END====================================================
+# *
+# * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+# *
+#-------------------------------------------------------------------------------
+########################################################################
+#Resource key=Error Code|Message text|Resolution text |Description text
+#######
+#Newlines can be utilized to add some clarity ensuring continuing line
+#has atleast one leading space
+#ResourceKey=\
+# ERR0000E\
+# Sample error msg txt\
+# Sample resolution msg\
+# Sample description txt
+#
+######
+#Error code classification category
+#100 Permission errors
+#200 Availability errors/Timeouts
+#300 Data errors
+#400 Schema Interface type/validation errors
+#500 Business process errors
+#900 Unknown errors
+#
+########################################################################
+
+# Messages for Data Router EELF framework
+
+#Prints FeedID in the EELF apilog
+MESSAGE_WITH__FEEDID=EELF0001I| FeedID = {0}
+
+#Prints User in the EELF apilog
+MESSAGE_WITH_BEHALF=EELF0002I| User = {0}
+
+#Prints User and FeedID in the EELF apilog
+MESSAGE_WITH_BEHALF_AND_FEEDID=EELF0003I| User = {0} FeedID = {1}
+
+#Prints User and SubID in the EELF apilog
+MESSAGE_WITH_BEHALF_AND_SUBID=EELF0004I| User = {0} SubscriberID = {1}
+
diff --git a/datarouter-prov/src/main/resources/authz.jar b/datarouter-prov/src/main/resources/authz.jar
new file mode 100644
index 00000000..6d0dd8a3
--- /dev/null
+++ b/datarouter-prov/src/main/resources/authz.jar
Binary files differ
diff --git a/datarouter-prov/src/main/resources/docker-compose/database/install_db.sql b/datarouter-prov/src/main/resources/docker-compose/database/install_db.sql
new file mode 100644
index 00000000..64a07626
--- /dev/null
+++ b/datarouter-prov/src/main/resources/docker-compose/database/install_db.sql
@@ -0,0 +1,143 @@
+CREATE DATABASE IF NOT EXISTS datarouter;
+
+CREATE USER 'datarouter'@'%' IDENTIFIED BY 'datarouter';
+
+GRANT ALL PRIVILEGES ON * . * TO 'datarouter'@'%';
+
+use datarouter;
+
+CREATE TABLE FEEDS (
+ FEEDID INT UNSIGNED NOT NULL PRIMARY KEY,
+ NAME VARCHAR(20) NOT NULL,
+ VERSION VARCHAR(20) NOT NULL,
+ DESCRIPTION VARCHAR(256),
+ AUTH_CLASS VARCHAR(32) NOT NULL,
+ PUBLISHER VARCHAR(8) NOT NULL,
+ SELF_LINK VARCHAR(256),
+ PUBLISH_LINK VARCHAR(256),
+ SUBSCRIBE_LINK VARCHAR(256),
+ LOG_LINK VARCHAR(256),
+ DELETED BOOLEAN DEFAULT FALSE,
+ LAST_MOD TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE TABLE FEED_ENDPOINT_IDS (
+ FEEDID INT UNSIGNED NOT NULL,
+ USERID VARCHAR(20) NOT NULL,
+ PASSWORD VARCHAR(32) NOT NULL
+);
+
+CREATE TABLE FEED_ENDPOINT_ADDRS (
+ FEEDID INT UNSIGNED NOT NULL,
+ ADDR VARCHAR(44) NOT NULL
+);
+
+CREATE TABLE SUBSCRIPTIONS (
+ SUBID INT UNSIGNED NOT NULL PRIMARY KEY,
+ FEEDID INT UNSIGNED NOT NULL,
+ DELIVERY_URL VARCHAR(256),
+ DELIVERY_USER VARCHAR(20),
+ DELIVERY_PASSWORD VARCHAR(32),
+ DELIVERY_USE100 BOOLEAN DEFAULT FALSE,
+ METADATA_ONLY BOOLEAN DEFAULT FALSE,
+ SUBSCRIBER VARCHAR(8) NOT NULL,
+ SELF_LINK VARCHAR(256),
+ LOG_LINK VARCHAR(256),
+ LAST_MOD TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE TABLE PARAMETERS (
+ KEYNAME VARCHAR(32) NOT NULL PRIMARY KEY,
+ VALUE VARCHAR(4096) NOT NULL
+);
+
+CREATE TABLE LOG_RECORDS (
+ TYPE ENUM('pub', 'del', 'exp') NOT NULL,
+ EVENT_TIME BIGINT NOT NULL, /* time of the publish request */
+ PUBLISH_ID VARCHAR(64) NOT NULL, /* unique ID assigned to this publish attempt */
+ FEEDID INT UNSIGNED NOT NULL, /* pointer to feed in FEEDS */
+ REQURI VARCHAR(256) NOT NULL, /* request URI */
+ METHOD ENUM('DELETE', 'GET', 'HEAD', 'OPTIONS', 'PUT', 'POST', 'TRACE') NOT NULL, /* HTTP method */
+ CONTENT_TYPE VARCHAR(256) NOT NULL, /* content type of published file */
+ CONTENT_LENGTH BIGINT UNSIGNED NOT NULL, /* content length of published file */
+
+ FEED_FILEID VARCHAR(128), /* file ID of published file */
+ REMOTE_ADDR VARCHAR(40), /* IP address of publishing endpoint */
+ USER VARCHAR(20), /* user name of publishing endpoint */
+ STATUS SMALLINT, /* status code returned to delivering agent */
+
+ DELIVERY_SUBID INT UNSIGNED, /* pointer to subscription in SUBSCRIPTIONS */
+ DELIVERY_FILEID VARCHAR(128), /* file ID of file being delivered */
+ RESULT SMALLINT, /* result received from subscribing agent */
+
+ ATTEMPTS INT, /* deliveries attempted */
+ REASON ENUM('notRetryable', 'retriesExhausted'),
+
+ RECORD_ID BIGINT UNSIGNED NOT NULL PRIMARY KEY, /* unique ID for this record */
+
+ INDEX (FEEDID) USING BTREE,
+ INDEX (DELIVERY_SUBID) USING BTREE,
+ INDEX (RECORD_ID) USING BTREE
+) ENGINE = MyISAM;
+
+CREATE TABLE INGRESS_ROUTES (
+ SEQUENCE INT UNSIGNED NOT NULL,
+ FEEDID INT UNSIGNED NOT NULL,
+ USERID VARCHAR(20),
+ SUBNET VARCHAR(44),
+ NODESET INT UNSIGNED NOT NULL
+);
+
+CREATE TABLE EGRESS_ROUTES (
+ SUBID INT UNSIGNED NOT NULL PRIMARY KEY,
+ NODEID INT UNSIGNED NOT NULL
+);
+
+CREATE TABLE NETWORK_ROUTES (
+ FROMNODE INT UNSIGNED NOT NULL,
+ TONODE INT UNSIGNED NOT NULL,
+ VIANODE INT UNSIGNED NOT NULL
+);
+
+CREATE TABLE NODESETS (
+ SETID INT UNSIGNED NOT NULL,
+ NODEID INT UNSIGNED NOT NULL
+);
+
+CREATE TABLE NODES (
+ NODEID INT UNSIGNED NOT NULL PRIMARY KEY,
+ NAME VARCHAR(255) NOT NULL,
+ ACTIVE BOOLEAN DEFAULT TRUE
+);
+
+CREATE TABLE GROUPS (
+ GROUPID INT UNSIGNED NOT NULL PRIMARY KEY,
+ AUTHID VARCHAR(100) NOT NULL,
+ NAME VARCHAR(50) NOT NULL,
+ DESCRIPTION VARCHAR(255),
+ CLASSIFICATION VARCHAR(20) NOT NULL,
+ MEMBERS TINYTEXT,
+ LAST_MOD TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+
+-- 'PROV_AUTH_ADDRESSES', '192.168.56.1' ipv4 address of provision server
+INSERT INTO PARAMETERS VALUES
+ ('ACTIVE_POD', 'prov.datarouternew.com'),
+ ('PROV_ACTIVE_NAME', 'prov.datarouternew.com'),
+ ('STANDBY_POD', ''),
+ ('PROV_NAME', 'prov.datarouternew.com'),
+ ('NODES', 'node.datarouternew.com'),
+ ('PROV_DOMAIN', 'datarouternew.com'),
+ ('DELIVERY_INIT_RETRY_INTERVAL', '10'),
+ ('DELIVERY_MAX_AGE', '86400'),
+ ('DELIVERY_MAX_RETRY_INTERVAL', '3600'),
+ ('DELIVERY_RETRY_RATIO', '2'),
+ ('LOGROLL_INTERVAL', '300'),
+ ('PROV_AUTH_ADDRESSES', 'prov.datarouternew.com'),
+ ('PROV_AUTH_SUBJECTS', ''),
+ ('PROV_MAXFEED_COUNT', '10000'),
+ ('PROV_MAXSUB_COUNT', '100000'),
+ ('PROV_REQUIRE_CERT', 'false'),
+ ('PROV_REQUIRE_SECURE', 'false'),
+ ('_INT_VALUES', 'LOGROLL_INTERVAL|PROV_MAXFEED_COUNT|PROV_MAXSUB_COUNT|DELIVERY_INIT_RETRY_INTERVAL|DELIVERY_MAX_RETRY_INTERVAL|DELIVERY_RETRY_RATIO|DELIVERY_MAX_AGE')
+ ; \ No newline at end of file
diff --git a/datarouter-prov/src/main/resources/docker-compose/docker-compose.yml b/datarouter-prov/src/main/resources/docker-compose/docker-compose.yml
new file mode 100644
index 00000000..4e2a81a7
--- /dev/null
+++ b/datarouter-prov/src/main/resources/docker-compose/docker-compose.yml
@@ -0,0 +1,69 @@
+#-------------------------------------------------------------------------------
+# ============LICENSE_START==================================================
+# * org.onap.dmaap
+# * ===========================================================================
+# * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+# * ===========================================================================
+# * 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.
+# * ============LICENSE_END====================================================
+# *
+# * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+# *
+#-------------------------------------------------------------------------------
+version: '2'
+services:
+ datarouter-prov:
+ image: attos/datarouter-prov
+ container_name: datarouter-prov
+ hostname: prov.datarouternew.com
+ ports:
+ - "8443:8443"
+ - "8080:8080"
+# volumes:
+# - ./prov_data/proserver.properties:/opt/app/datartr/etc/proserver.properties
+# - ./prov_data/datarouter-prov-jar-with-dependencies.jar:/opt/app/datartr/lib/datarouter-prov-jar-with-dependencies.jar
+# - ./prov_data/addSubscriber.txt:/opt/app/datartr/addSubscriber.txt
+# - ./prov_data/addFeed3.txt:/opt/app/datartr/addFeed3.txt
+ entrypoint: ["bash", "-c", "sleep 10; /bin/sh -c ./startup.sh"]
+ depends_on:
+ - mysql_container
+ extra_hosts:
+ - "node.datarouternew.com:172.18.0.4"
+
+
+ datarouter-node:
+ image: attos/datarouter-node
+ container_name: datarouter-node
+ hostname: node.datarouternew.com
+ ports:
+ - "9443:8443"
+ - "9090:8080"
+# volumes:
+# - ./node_data/node.properties:/opt/app/datartr/etc/node.properties
+ entrypoint: ["bash", "-c", "sleep 15; /bin/sh -c ./startup.sh"]
+ depends_on:
+ - datarouter-prov
+ extra_hosts:
+ - "prov.datarouternew.com:172.18.0.3"
+
+ mysql_container:
+ image: mysql/mysql-server:5.6
+ container_name: mysql
+ ports:
+ - "3306:3306"
+ environment:
+ MYSQL_ROOT_PASSWORD: att2017
+ volumes:
+ - ./database:/tmp/database
+ - ./database:/docker-entrypoint-initdb.d
+
diff --git a/datarouter-prov/src/main/resources/docker-compose/node_data/node.properties b/datarouter-prov/src/main/resources/docker-compose/node_data/node.properties
new file mode 100644
index 00000000..f57833c8
--- /dev/null
+++ b/datarouter-prov/src/main/resources/docker-compose/node_data/node.properties
@@ -0,0 +1,112 @@
+#-------------------------------------------------------------------------------
+# ============LICENSE_START==================================================
+# * org.onap.dmaap
+# * ===========================================================================
+# * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+# * ===========================================================================
+# * 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.
+# * ============LICENSE_END====================================================
+# *
+# * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+# *
+#-------------------------------------------------------------------------------
+#
+# Configuration parameters fixed at startup for the DataRouter node
+#
+# URL to retrieve dynamic configuration
+#
+#ProvisioningURL: ${DRTR_PROV_INTURL:-https://feeds-drtr.web.att.com/internal/prov}
+ProvisioningURL=https://prov.datarouternew.com:8443/internal/prov
+
+#
+# URL to upload PUB/DEL/EXP logs
+#
+#LogUploadURL: ${DRTR_LOG_URL:-https://feeds-drtr.web.att.com/internal/logs}
+LogUploadURL=https://prov.datarouternew.com:8443/internal/logs
+
+#
+# The port number for http as seen within the server
+#
+#IntHttpPort: ${DRTR_NODE_INTHTTPPORT:-8080}
+IntHttpPort=8080
+#
+# The port number for https as seen within the server
+#
+IntHttpsPort=8443
+#
+# The external port number for https taking port mapping into account
+#
+ExtHttpsPort=443
+#
+# The minimum interval between fetches of the dynamic configuration
+# from the provisioning server
+#
+MinProvFetchInterval=10000
+#
+# The minimum interval between saves of the redirection data file
+#
+MinRedirSaveInterval=10000
+#
+# The path to the directory where log files are stored
+#
+LogDir=/opt/app/datartr/logs
+#
+# The retention interval (in days) for log files
+#
+LogRetention=30
+#
+# The path to the directories where data and meta data files are stored
+#
+SpoolDir=/opt/app/datartr/spool
+#
+# The path to the redirection data file
+#
+#RedirectionFile: etc/redirections.dat
+#
+# The type of keystore for https
+#
+KeyStoreType: jks
+#
+# The path to the keystore for https
+#
+KeyStoreFile:/opt/app/datartr/self_signed/keystore.jks
+#
+# The password for the https keystore
+#
+KeyStorePassword=changeit
+#
+# The password for the private key in the https keystore
+#
+KeyPassword=changeit
+#
+# The type of truststore for https
+#
+TrustStoreType=jks
+#
+# The path to the truststore for https
+#
+#TrustStoreFile=/usr/lib/jvm/java-8-oracle/jre/lib/security/cacerts
+TrustStoreFile=/opt/app/datartr/self_signed/cacerts.jks
+#
+# The password for the https truststore
+#
+TrustStorePassword=changeit
+#
+# The path to the file used to trigger an orderly shutdown
+#
+QuiesceFile=etc/SHUTDOWN
+#
+# The key used to generate passwords for node to node transfers
+#
+NodeAuthKey=Node123!
+
diff --git a/datarouter-prov/src/main/resources/docker-compose/node_data/self_signed/cacerts.jks b/datarouter-prov/src/main/resources/docker-compose/node_data/self_signed/cacerts.jks
new file mode 100644
index 00000000..dfd81433
--- /dev/null
+++ b/datarouter-prov/src/main/resources/docker-compose/node_data/self_signed/cacerts.jks
Binary files differ
diff --git a/datarouter-prov/src/main/resources/docker-compose/node_data/self_signed/keystore.jks b/datarouter-prov/src/main/resources/docker-compose/node_data/self_signed/keystore.jks
new file mode 100644
index 00000000..e5a4e781
--- /dev/null
+++ b/datarouter-prov/src/main/resources/docker-compose/node_data/self_signed/keystore.jks
Binary files differ
diff --git a/datarouter-prov/src/main/resources/docker-compose/node_data/self_signed/mykey.cer b/datarouter-prov/src/main/resources/docker-compose/node_data/self_signed/mykey.cer
new file mode 100644
index 00000000..2a5c9d70
--- /dev/null
+++ b/datarouter-prov/src/main/resources/docker-compose/node_data/self_signed/mykey.cer
Binary files differ
diff --git a/datarouter-prov/src/main/resources/docker-compose/node_data/self_signed/nodekey.cer b/datarouter-prov/src/main/resources/docker-compose/node_data/self_signed/nodekey.cer
new file mode 100644
index 00000000..4cdfdfe3
--- /dev/null
+++ b/datarouter-prov/src/main/resources/docker-compose/node_data/self_signed/nodekey.cer
Binary files differ
diff --git a/datarouter-prov/src/main/resources/docker-compose/prov_data/addFeed3.txt b/datarouter-prov/src/main/resources/docker-compose/prov_data/addFeed3.txt
new file mode 100644
index 00000000..a21c7ae0
--- /dev/null
+++ b/datarouter-prov/src/main/resources/docker-compose/prov_data/addFeed3.txt
@@ -0,0 +1,44 @@
+#-------------------------------------------------------------------------------
+# ============LICENSE_START==================================================
+# * org.onap.dmaap
+# * ===========================================================================
+# * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+# * ===========================================================================
+# * 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.
+# * ============LICENSE_END====================================================
+# *
+# * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+# *
+#-------------------------------------------------------------------------------
+{
+ "name": "Jettydemo",
+ "version": "m1.0",
+ "description": "Jettydemo",
+ "business_description": "Jettydemo",
+ "suspend": false,
+ "deleted": false,
+ "changeowner": true,
+ "authorization": {
+ "classification": "unclassified",
+ "endpoint_addrs": [
+ "172.18.0.3",
+ ],
+ "endpoint_ids": [
+ {
+ "password": "rs873m",
+ "id": "rs873m"
+ }
+ ]
+ },
+}
+
diff --git a/datarouter-prov/src/main/resources/docker-compose/prov_data/addSubscriber.txt b/datarouter-prov/src/main/resources/docker-compose/prov_data/addSubscriber.txt
new file mode 100644
index 00000000..e9746311
--- /dev/null
+++ b/datarouter-prov/src/main/resources/docker-compose/prov_data/addSubscriber.txt
@@ -0,0 +1,36 @@
+#-------------------------------------------------------------------------------
+# ============LICENSE_START==================================================
+# * org.onap.dmaap
+# * ===========================================================================
+# * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+# * ===========================================================================
+# * 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.
+# * ============LICENSE_END====================================================
+# *
+# * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+# *
+#-------------------------------------------------------------------------------
+{
+ "delivery" :
+
+ {
+ "url" : "http://172.18.0.3:7070/",
+ "user" : "LOGIN",
+ "password" : "PASSWORD",
+ "use100" : true
+ },
+ "metadataOnly" : false,
+ "suspend" : false,
+ "groupid" : 29,
+ "subscriber" : "sg481n"
+}
diff --git a/datarouter-prov/src/main/resources/docker-compose/prov_data/provserver.properties b/datarouter-prov/src/main/resources/docker-compose/prov_data/provserver.properties
new file mode 100644
index 00000000..6a03cbd1
--- /dev/null
+++ b/datarouter-prov/src/main/resources/docker-compose/prov_data/provserver.properties
@@ -0,0 +1,59 @@
+#-------------------------------------------------------------------------------
+# ============LICENSE_START==================================================
+# * org.onap.dmaap
+# * ===========================================================================
+# * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+# * ===========================================================================
+# * 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.
+# * ============LICENSE_END====================================================
+# *
+# * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+# *
+#-------------------------------------------------------------------------------
+#
+# AT&T - PROPRIETARY
+# THIS FILE CONTAINS PROPRIETARY INFORMATION OF
+# AT&T AND IS NOT TO BE DISCLOSED OR USED EXCEPT IN
+# ACCORDANCE WITH APPLICABLE AGREEMENTS.
+#
+# Copyright (c) 2013 AT&T Knowledge Ventures
+# Unpublished and Not for Publication
+# All Rights Reserved
+#
+# CVS: $Id: provserver.properties,v 1.7 2013/05/29 14:44:36 eby Exp $
+#
+
+#Jetty Server properties
+com.att.research.datarouter.provserver.http.port = 8080
+com.att.research.datarouter.provserver.https.port = 8443
+com.att.research.datarouter.provserver.https.relaxation = true
+com.att.research.datarouter.provserver.keymanager.password = changeit
+com.att.research.datarouter.provserver.keystore.type = jks
+com.att.research.datarouter.provserver.keystore.path = /opt/app/datartr/self_signed/keystore.jks
+
+com.att.research.datarouter.provserver.keystore.password = changeit
+#com.att.research.datarouter.provserver.truststore.path = /home/eby/dr2/misc/cacerts+1
+#com.att.research.datarouter.provserver.truststore.path = /usr/lib/jvm/java-8-oracle/jre/lib/security/cacerts
+com.att.research.datarouter.provserver.truststore.path = /opt/app/datartr/self_signed/cacerts.jks
+
+com.att.research.datarouter.provserver.truststore.password = changeit
+com.att.research.datarouter.provserver.accesslog.dir = /opt/app/datartr/logs
+com.att.research.datarouter.provserver.spooldir = /opt/app/datartr/spool
+#com.att.research.datarouter.provserver.dbscripts = /home/eby/dr2/cvs/datarouter/prov/misc/
+com.att.research.datarouter.provserver.logretention = 30
+
+# Database access
+com.att.research.datarouter.db.driver = com.mysql.jdbc.Driver
+com.att.research.datarouter.db.url = jdbc:mysql://172.18.0.2:3306/datarouter
+com.att.research.datarouter.db.login = datarouter
+com.att.research.datarouter.db.password = datarouter
diff --git a/datarouter-prov/src/main/resources/docker-compose/prov_data/self_signed/cacerts.jks b/datarouter-prov/src/main/resources/docker-compose/prov_data/self_signed/cacerts.jks
new file mode 100644
index 00000000..76a480ad
--- /dev/null
+++ b/datarouter-prov/src/main/resources/docker-compose/prov_data/self_signed/cacerts.jks
Binary files differ
diff --git a/datarouter-prov/src/main/resources/docker-compose/prov_data/self_signed/keystore.jks b/datarouter-prov/src/main/resources/docker-compose/prov_data/self_signed/keystore.jks
new file mode 100644
index 00000000..2c22b4ac
--- /dev/null
+++ b/datarouter-prov/src/main/resources/docker-compose/prov_data/self_signed/keystore.jks
Binary files differ
diff --git a/datarouter-prov/src/main/resources/docker-compose/prov_data/self_signed/mykey.cer b/datarouter-prov/src/main/resources/docker-compose/prov_data/self_signed/mykey.cer
new file mode 100644
index 00000000..2a5c9d70
--- /dev/null
+++ b/datarouter-prov/src/main/resources/docker-compose/prov_data/self_signed/mykey.cer
Binary files differ
diff --git a/datarouter-prov/src/main/resources/docker/Dockerfile b/datarouter-prov/src/main/resources/docker/Dockerfile
new file mode 100644
index 00000000..215c4332
--- /dev/null
+++ b/datarouter-prov/src/main/resources/docker/Dockerfile
@@ -0,0 +1,9 @@
+FROM java:8
+ADD opt /opt/
+ADD startup.sh /startup.sh
+RUN chmod 700 /startup.sh
+ENTRYPOINT ./startup.sh start
+EXPOSE 8443
+EXPOSE 8080
+
+
diff --git a/datarouter-prov/src/main/resources/docker/startup.sh b/datarouter-prov/src/main/resources/docker/startup.sh
new file mode 100644
index 00000000..191a8049
--- /dev/null
+++ b/datarouter-prov/src/main/resources/docker/startup.sh
@@ -0,0 +1,16 @@
+LIB=/opt/app/datartr/lib
+ETC=/opt/app/datartr/etc
+echo "this is LIB" $LIB
+echo "this is ETC" $ETC
+mkdir -p /opt/app/datartr/logs
+mkdir -p /opt/app/datartr/spool
+CLASSPATH=$ETC
+for FILE in `find $LIB -name *.jar`; do
+ CLASSPATH=$CLASSPATH:$FILE
+done
+java -classpath $CLASSPATH com.att.research.datarouter.provisioning.Main
+
+runner_file="$LIB/datarouter-prov-jar-with-dependencies.jar"
+echo "Starting using" $runner_file
+java -Dcom.att.eelf.logging.file==/opt/app/datartr/etc/logback.xml -Dcom.att.eelf.logging.path=/root -jar $runner_file
+
diff --git a/datarouter-prov/src/main/resources/log4j.properties b/datarouter-prov/src/main/resources/log4j.properties
new file mode 100644
index 00000000..bb4eaa0c
--- /dev/null
+++ b/datarouter-prov/src/main/resources/log4j.properties
@@ -0,0 +1,68 @@
+#-------------------------------------------------------------------------------
+# ============LICENSE_START==================================================
+# * org.onap.dmaap
+# * ===========================================================================
+# * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+# * ===========================================================================
+# * 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.
+# * ============LICENSE_END====================================================
+# *
+# * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+# *
+#-------------------------------------------------------------------------------
+
+
+log4j.rootLogger=info
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d %5p [%t] - %m%n
+
+#
+# Logger used for provisioning events
+#
+log4j.logger.com.att.research.datarouter.provisioning.events=info, eventlog
+log4j.additivity.com.att.research.datarouter.provisioning.events=false
+
+log4j.appender.eventlog=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.eventlog.file=/root/dr2/logs/provevent.log
+log4j.appender.eventlog.datePattern='.'yyyyMMdd
+log4j.appender.eventlog.append=true
+log4j.appender.eventlog.layout=org.apache.log4j.PatternLayout
+log4j.appender.eventlog.layout.ConversionPattern=%d %-5p [%t] - %m%n
+
+#
+# Logger used for internal provisioning server events
+#
+log4j.logger.com.att.research.datarouter.provisioning.internal=debug, intlog
+log4j.additivity.com.att.research.datarouter.provisioning.internal=false
+
+log4j.appender.intlog=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.intlog.file=/root/dr2/logs/provint.log
+log4j.appender.intlog.datePattern='.'yyyyMMdd
+log4j.appender.intlog.append=true
+log4j.appender.intlog.layout=org.apache.log4j.PatternLayout
+log4j.appender.intlog.layout.ConversionPattern=%d %-5p [%t] - %m%n
+
+#
+# Logger used for policy engine
+#
+log4j.logger.com.att.research.datarouter.authz.impl.ProvAuthorizer=debug, pelog
+log4j.additivity.com.att.research.datarouter.authz.impl.ProvAuthorizer=false
+
+log4j.appender.pelog=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.pelog.file=/root/dr2/logs/policyengine.log
+log4j.appender.pelog.datePattern='.'yyyyMMdd
+log4j.appender.pelog.append=true
+log4j.appender.pelog.layout=org.apache.log4j.PatternLayout
+log4j.appender.pelog.layout.ConversionPattern=%d %-5p [%t] - %m%n
diff --git a/datarouter-prov/src/main/resources/logback.xml b/datarouter-prov/src/main/resources/logback.xml
new file mode 100644
index 00000000..7d73e0de
--- /dev/null
+++ b/datarouter-prov/src/main/resources/logback.xml
@@ -0,0 +1,405 @@
+<!--
+ ============LICENSE_START==================================================
+ * org.onap.dmaap
+ * ===========================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * ===========================================================================
+ * 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.
+ * ============LICENSE_END====================================================
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ *
+-->
+<configuration scan="true" scanPeriod="3 seconds" debug="true">
+ <!--<jmxConfigurator /> -->
+ <!-- directory path for all other type logs -->
+ <!-- property name="logDir" value="/home/eby/dr2/logs" / -->
+ <property name="logDir" value="/opt/app/datartr/logs" />
+
+ <!-- directory path for debugging type logs -->
+ <!-- property name="debugDir" value="/home/eby/dr2/debug-logs" /-->
+
+ <!-- specify the component name
+ <ECOMP-component-name>::= "MSO" | "DCAE" | "ASDC " | "AAI" |"Policy" | "SDNC" | "AC" -->
+ <!-- This creates the MSO directory in in the LogDir which is not needed, mentioned last directory of the path-->
+ <!-- property name="componentName" value="logs"></property -->
+
+ <!-- log file names -->
+ <property name="generalLogName" value="apicalls" />
+ <!-- name="securityLogName" value="security" -->
+ <!-- name="performanceLogName" value="performance" -->
+ <!-- name="serverLogName" value="server" -->
+ <!-- name="policyLogName" value="policy"-->
+ <property name="errorLogName" value="errors" />
+ <!-- name="metricsLogName" value="metrics" -->
+ <!-- name="auditLogName" value="audit" -->
+ <!-- name="debugLogName" value="debug" -->
+ <property name="jettyLogName" value="jetty"></property>
+ <property name="defaultPattern" value="%d{MM/dd-HH:mm:ss.SSS}|%logger|%X{RequestId}|%X{ServiceInstanceId}|%thread|%X{ServiceName}|%X{InstanceUUID}|%.-5level|%X{AlertSeverity}|%X{ServerIPAddress}|%X{ServerFQDN}|%X{RemoteHost}|%X{Timer}|%msg%n" />
+ <property name="jettyLoggerPattern" value="%d{MM/dd-HH:mm:ss.SSS}|%logger|%thread|%.-5level|%msg%n" />
+
+ <property name="debugLoggerPattern" value="%d{MM/dd-HH:mm:ss.SSS}|%X{RequestId}|%X{ServiceInstanceId}|%thread|%X{ServiceName}|%X{InstanceUUID}|%.-5level|%X{AlertSeverity}|%X{ServerIPAddress}|%X{ServerFQDN}|%X{RemoteHost}|%X{Timer}|[%caller{3}]|%msg%n" />
+
+ <property name="logDirectory" value="${logDir}" />
+ <!-- property name="debugLogDirectory" value="${debugDir}/${componentName}" /-->
+
+
+ <!-- Example evaluator filter applied against console appender -->
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>${defaultPattern}</pattern>
+ </encoder>
+ </appender>
+
+ <!-- ============================================================================ -->
+ <!-- EELF Appenders -->
+ <!-- ============================================================================ -->
+
+ <!-- The EELFAppender is used to record events to the general application
+ log -->
+
+
+ <appender name="EELF"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${logDirectory}/${generalLogName}.log</file>
+ <filter class="ch.qos.logback.classic.filter.LevelFilter">
+ <level>INFO</level>
+ <onMatch>ACCEPT</onMatch>
+ <onMismatch>DENY</onMismatch>
+ </filter>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${logDirectory}/${generalLogName}.%i.log.zip
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>9</maxIndex>
+ </rollingPolicy>
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>5MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${defaultPattern}</pattern>
+ </encoder>
+ </appender>
+
+ <appender name="asyncEELF" class="ch.qos.logback.classic.AsyncAppender">
+ <queueSize>256</queueSize>
+ <appender-ref ref="EELF" />
+ </appender>
+
+ <!-- EELF Security Appender. This appender is used to record security events
+ to the security log file. Security events are separate from other loggers
+ in EELF so that security log records can be captured and managed in a secure
+ way separate from the other logs. This appender is set to never discard any
+ events. -->
+ <!--appender name="EELFSecurity"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${logDirectory}/${securityLogName}.log</file>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${logDirectory}/${securityLogName}.%i.log.zip
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>9</maxIndex>
+ </rollingPolicy>
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>5MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${defaultPattern}</pattern>
+ </encoder>
+ </appender>
+
+ <appender name="asyncEELFSecurity" class="ch.qos.logback.classic.AsyncAppender">
+ <queueSize>256</queueSize>
+ <discardingThreshold>0</discardingThreshold>
+ <appender-ref ref="EELFSecurity" />
+ </appender-->
+
+ <!-- EELF Performance Appender. This appender is used to record performance
+ records. -->
+ <!--appender name="EELFPerformance"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${logDirectory}/${performanceLogName}.log</file>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${logDirectory}/${performanceLogName}.%i.log.zip
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>9</maxIndex>
+ </rollingPolicy>
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>5MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <outputPatternAsHeader>true</outputPatternAsHeader>
+ <pattern>${defaultPattern}</pattern>
+ </encoder>
+ </appender>
+ <appender name="asyncEELFPerformance" class="ch.qos.logback.classic.AsyncAppender">
+ <queueSize>256</queueSize>
+ <appender-ref ref="EELFPerformance" />
+ </appender-->
+
+ <!-- EELF Server Appender. This appender is used to record Server related
+ logging events. The Server logger and appender are specializations of the
+ EELF application root logger and appender. This can be used to segregate Server
+ events from other components, or it can be eliminated to record these events
+ as part of the application root log. -->
+ <!--appender name="EELFServer"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${logDirectory}/${serverLogName}.log</file>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${logDirectory}/${serverLogName}.%i.log.zip
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>9</maxIndex>
+ </rollingPolicy>
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>5MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${defaultPattern}</pattern>
+ </encoder>
+ </appender>
+ <appender name="asyncEELFServer" class="ch.qos.logback.classic.AsyncAppender">
+ <queueSize>256</queueSize>
+ <appender-ref ref="EELFServer" />
+ </appender-->
+
+
+ <!-- EELF Policy Appender. This appender is used to record Policy engine
+ related logging events. The Policy logger and appender are specializations
+ of the EELF application root logger and appender. This can be used to segregate
+ Policy engine events from other components, or it can be eliminated to record
+ these events as part of the application root log. -->
+ <!--appender name="EELFPolicy"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${logDirectory}/${policyLogName}.log</file>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${logDirectory}/${policyLogName}.%i.log.zip
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>9</maxIndex>
+ </rollingPolicy>
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>5MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${defaultPattern}</pattern>
+ </encoder>
+ </appender>
+ <appender name="asyncEELFPolicy" class="ch.qos.logback.classic.AsyncAppender">
+ <queueSize>256</queueSize>
+ <appender-ref ref="EELFPolicy" >
+ </appender-->
+
+
+ <!-- EELF Audit Appender. This appender is used to record audit engine
+ related logging events. The audit logger and appender are specializations
+ of the EELF application root logger and appender. This can be used to segregate
+ Policy engine events from other components, or it can be eliminated to record
+ these events as part of the application root log. -->
+
+ <!--appender name="EELFAudit"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${logDirectory}/${auditLogName}.log</file>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${logDirectory}/${auditLogName}.%i.log.zip
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>9</maxIndex>
+ </rollingPolicy>
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>5MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${defaultPattern}</pattern>
+ </encoder>
+ </appender>
+ <appender name="asyncEELFAudit" class="ch.qos.logback.classic.AsyncAppender">
+ <queueSize>256</queueSize>
+ <appender-ref ref="EELFAudit" />
+ </appender-->
+
+<!--appender name="EELFMetrics"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${logDirectory}/${metricsLogName}.log</file>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${logDirectory}/${metricsLogName}.%i.log.zip
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>9</maxIndex>
+ </rollingPolicy>
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>5MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder-->
+ <!-- <pattern>"%d{HH:mm:ss.SSS} [%thread] %-5level %logger{1024} -
+ %msg%n"</pattern> -->
+ <!--pattern>${defaultPattern}</pattern>
+ </encoder>
+ </appender>
+
+
+ <appender name="asyncEELFMetrics" class="ch.qos.logback.classic.AsyncAppender">
+ <queueSize>256</queueSize>
+ <appender-ref ref="EELFMetrics"/>
+ </appender-->
+
+ <appender name="EELFError"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${logDirectory}/${errorLogName}.log</file>
+ <filter class="ch.qos.logback.classic.filter.LevelFilter">
+ <level>ERROR</level>
+ <onMatch>ACCEPT</onMatch>
+ <onMismatch>DENY</onMismatch>
+ </filter>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${logDirectory}/${errorLogName}.%i.log.zip
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>9</maxIndex>
+ </rollingPolicy>
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>5MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${defaultPattern}</pattern>
+ </encoder>
+ </appender>
+
+ <appender name="asyncEELFError" class="ch.qos.logback.classic.AsyncAppender">
+ <queueSize>256</queueSize>
+ <appender-ref ref="EELFError"/>
+ </appender>
+
+ <!-- ============================================================================ -->
+ <appender name="jettylog"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${logDirectory}/${jettyLogName}.log</file>
+ <filter class="com.att.research.datarouter.provisioning.eelf.JettyFilter" />
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${logDirectory}/${jettyLogName}.%i.log.zip
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>9</maxIndex>
+ </rollingPolicy>
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>5MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${jettyLoggerPattern}</pattern>
+ </encoder>
+ </appender>
+
+ <appender name="asyncEELFjettylog" class="ch.qos.logback.classic.AsyncAppender">
+ <queueSize>256</queueSize>
+ <appender-ref ref="jettylog" />
+ <includeCallerData>true</includeCallerData>
+ </appender>
+
+ <!-- ============================================================================ -->
+
+
+ <!--appender name="EELFDebug"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${debugLogDirectory}/${debugLogName}.log</file>
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${debugLogDirectory}/${debugLogName}.%i.log.zip
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>9</maxIndex>
+ </rollingPolicy>
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>5MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${debugLoggerPattern}</pattern>
+ </encoder>
+ </appender>
+
+ <appender name="asyncEELFDebug" class="ch.qos.logback.classic.AsyncAppender">
+ <queueSize>256</queueSize>
+ <appender-ref ref="EELFDebug" />
+ <includeCallerData>true</includeCallerData>
+ </appender-->
+
+
+ <!-- ============================================================================ -->
+ <!-- EELF loggers -->
+ <!-- ============================================================================ -->
+ <logger name="com.att.eelf" level="info" additivity="false">
+ <appender-ref ref="asyncEELF" />
+ </logger>
+
+ <logger name="com.att.eelf.error" level="error" additivity="false">
+ <appender-ref ref="asyncEELFError" />
+ </logger>
+
+ <logger name="log4j.logger.org.eclipse.jetty" additivity="false" level="info">
+ <appender-ref ref="asyncEELFjettylog"/>
+ </logger>
+
+ <!-- logger name="com.att.eelf.security" level="info" additivity="false">
+ <appender-ref ref="asyncEELFSecurity" />
+ </logger>
+ <logger name="com.att.eelf.perf" level="info" additivity="false">
+ <appender-ref ref="asyncEELFPerformance" />
+ </logger>
+ <logger name="com.att.eelf.server" level="info" additivity="false">
+ <appender-ref ref="asyncEELFServer" />
+ </logger>
+ <logger name="com.att.eelf.policy" level="info" additivity="false">
+ <appender-ref ref="asyncEELFPolicy" />
+ </logger>
+
+ <logger name="com.att.eelf.audit" level="info" additivity="false">
+ <appender-ref ref="asyncEELFAudit" />
+ </logger>
+
+ <logger name="com.att.eelf.metrics" level="info" additivity="false">
+ <appender-ref ref="asyncEELFMetrics" />
+ </logger>
+
+ <logger name="com.att.eelf.debug" level="debug" additivity="false">
+ <appender-ref ref="asyncEELFDebug" />
+ </logger-->
+
+
+
+
+ <root level="INFO">
+ <appender-ref ref="asyncEELF" />
+ <appender-ref ref="asyncEELFError" />
+ <appender-ref ref="asyncEELFjettylog" />
+ </root>
+
+</configuration>
diff --git a/datarouter-prov/src/main/resources/misc/doaction b/datarouter-prov/src/main/resources/misc/doaction
new file mode 100644
index 00000000..43193324
--- /dev/null
+++ b/datarouter-prov/src/main/resources/misc/doaction
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+cd /opt/app/datartr/etc
+for action in "$@"
+do
+case "$action" in
+'stop')
+ /opt/app/platform/init.d/drtrprov stop
+ ;;
+'start')
+ /opt/app/platform/init.d/drtrprov start || exit 1
+ ;;
+'backup')
+ cp log4j.properties log4j.properties.save 2>/dev/null
+ cp provserver.properties provserver.properties.save 2>/dev/null
+ cp mail.properties mail.properties.save 2>/dev/null
+ cp havecert havecert.save 2>/dev/null
+ cp mysql_init_0001 mysql_init_0001.save 2>/dev/null
+ ;;
+'restore')
+ cp log4j.properties.save log4j.properties 2>/dev/null
+ cp provserver.properties.save provserver.properties 2>/dev/null
+ cp mail.properties.save mail.properties 2>/dev/null
+ cp havecert.save havecert 2>/dev/null
+ cp mysql_init_0001.save mysql_init_0001 2>/dev/null
+ ;;
+'config')
+ /bin/bash log4j.properties.tmpl >log4j.properties
+ /bin/bash provserver.properties.tmpl >provserver.properties
+ /bin/bash mail.properties.tmpl >mail.properties
+ /bin/bash havecert.tmpl >havecert
+ /bin/bash mysql_init_0001.tmpl >mysql_init_0001
+ echo "$AFTSWM_ACTION_NEW_VERSION" >VERSION.prov
+ chmod +x havecert
+ rm -f /opt/app/platform/rc.d/K90zdrtrprov /opt/app/platform/rc.d/S99zdrtrprov
+ ln -s ../init.d/drtrprov /opt/app/platform/rc.d/K90zdrtrprov
+ ln -s ../init.d/drtrprov /opt/app/platform/rc.d/S99zdrtrprov
+ ;;
+'clean')
+ rm -f log4j.properties log4j.properties.save
+ rm -f provserver.properties provserver.properties.save
+ rm -f mail.properties mail.properties.save
+ rm -f havecert havecert.properties.save
+ rm -f mysql_init_0001 mysql_init_0001.save
+ rm -f VERSION.prov
+ rm -f /opt/app/platform/rc.d/K90zdrtrprov /opt/app/platform/rc.d/S99zdrtrprov
+ ;;
+*)
+ exit 1
+ ;;
+esac
+done
+exit 0
diff --git a/datarouter-prov/src/main/resources/misc/dr-route b/datarouter-prov/src/main/resources/misc/dr-route
new file mode 100644
index 00000000..77c6c180
--- /dev/null
+++ b/datarouter-prov/src/main/resources/misc/dr-route
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# AT&T - PROPRIETARY
+# THIS FILE CONTAINS PROPRIETARY INFORMATION OF
+# AT&T AND IS NOT TO BE DISCLOSED OR USED EXCEPT IN
+# ACCORDANCE WITH APPLICABLE AGREEMENTS.
+#
+# Copyright (c) 2013 AT&T Knowledge Ventures
+# Unpublished and Not for Publication
+# All Rights Reserved
+#
+# dr-route -- A script to interact with a provisioning server to manage the DR routing tables.
+#
+# $Id: dr-route,v 1.2 2013/11/06 16:23:54 eby Exp $
+#
+
+JAVA_HOME=/opt/java/jdk/jdk180
+JAVA_OPTS="-Xms1G -Xmx1G"
+TZ=GMT0
+PATH=$JAVA_HOME/bin:/bin:/usr/bin
+CLASSPATH=`echo /opt/app/datartr/etc /opt/app/datartr/lib/*.jar | tr ' ' ':'`
+export CLASSPATH JAVA_HOME JAVA_OPTS TZ PATH
+
+$JAVA_HOME/bin/java \
+ -Dlog4j.configuration=file:///opt/app/datartr/etc/log4j.drroute.properties \
+ com.att.research.datarouter.provisioning.utils.DRRouteCLI $*
diff --git a/datarouter-prov/src/main/resources/misc/drtrprov b/datarouter-prov/src/main/resources/misc/drtrprov
new file mode 100644
index 00000000..c801ce04
--- /dev/null
+++ b/datarouter-prov/src/main/resources/misc/drtrprov
@@ -0,0 +1,131 @@
+#!/bin/bash
+#
+# AT&T - PROPRIETARY
+# THIS FILE CONTAINS PROPRIETARY INFORMATION OF
+# AT&T AND IS NOT TO BE DISCLOSED OR USED EXCEPT IN
+# ACCORDANCE WITH APPLICABLE AGREEMENTS.
+#
+# Copyright (c) 2013 AT&T Knowledge Ventures
+# Unpublished and Not for Publication
+# All Rights Reserved
+#
+# This is the startup/shutdown script for the AT&T Data Router Provisioning Server.
+#
+# $Id: drtrprov,v 1.3 2013/10/29 16:57:57 eby Exp $
+#
+
+umask 0022
+
+JAVA_HOME=/opt/java/jdk/jdk180
+JAVA_OPTS="-Xms2G -Xmx8G"
+TZ=GMT0
+PATH=$JAVA_HOME/bin:/bin:/usr/bin
+CLASSPATH=`echo /opt/app/datartr/etc /opt/app/datartr/lib/*.jar | tr ' ' ':'`
+export CLASSPATH JAVA_HOME JAVA_OPTS TZ PATH
+
+pids() {
+ pgrep -u datartr -f provisioning.Main
+}
+
+start() {
+ ID=`id -n -u`
+ GRP=`id -n -g`
+ if [ "$ID" != "root" ]
+ then
+ echo drtrprov must be started as user datartr not $ID
+ exit 1
+ fi
+# if [ "$GRP" != "datartr" ]
+# then
+# echo drtrprov must be started as group datartr not $GRP
+# exit 1
+# fi
+# cd /opt/app/datartr
+# if etc/havecert
+# then
+# echo >/dev/null
+# else
+# echo No certificate file available. Cannot start
+# exit 0
+# fi
+ if [ "`pgrep -u mysql mysqld`" = "" ]
+ then
+ echo MySQL is not running. It must be started before drtrprov
+ exit 0
+ fi
+ PIDS=`pids`
+ if [ "$PIDS" != "" ]
+ then
+ echo drtrprov already running
+ exit 0
+ fi
+ echo '0 1 * * * /opt/app/datartr/bin/runreports' | crontab
+ nohup java $JAVA_OPTS com.att.research.datarouter.provisioning.Main </dev/null &
+ sleep 5
+ PIDS=`pids`
+ if [ "$PIDS" = "" ]
+ then
+ echo drtrprov startup failed
+ else
+ echo drtrprov started
+ fi
+}
+
+stop() {
+ ID=`id -n -u`
+ GRP=`id -n -g`
+ if [ "$ID" != "datartr" ]
+ then
+ echo drtrprov must be stopped as user datartr not $ID
+ exit 1
+ fi
+ if [ "$GRP" != "datartr" ]
+ then
+ echo drtrprov must be stopped as group datartr not $GRP
+ exit 1
+ fi
+ /usr/bin/curl http://127.0.0.1:8080/internal/halt
+ sleep 5
+ PIDS=`pids`
+ if [ "$PIDS" != "" ]
+ then
+ sleep 5
+ kill -9 $PIDS
+ sleep 5
+ echo drtrprov stopped
+ else
+ echo drtrprov not running
+ fi
+}
+
+status() {
+ PIDS=`pids`
+ if [ "$PIDS" != "" ]
+ then
+ echo drtrprov running
+ else
+ echo drtrprov not running
+ fi
+}
+
+case "$1" in
+'start')
+ start
+ ;;
+'stop')
+ stop
+ ;;
+'restart')
+ stop
+ sleep 20
+ start
+ ;;
+'status')
+ status
+ ;;
+*)
+ echo "Usage: $0 { start | stop | restart | status }"
+ exit 1
+ ;;
+esac
+exit 0
diff --git a/datarouter-prov/src/main/resources/misc/havecert.tmpl b/datarouter-prov/src/main/resources/misc/havecert.tmpl
new file mode 100644
index 00000000..e2389868
--- /dev/null
+++ b/datarouter-prov/src/main/resources/misc/havecert.tmpl
@@ -0,0 +1,11 @@
+#!/bin/bash
+cat <<!EOF
+TZ=GMT0
+cd /opt/app/datartr;
+if [ -f ${DRTR_PROV_KSTOREFILE:-etc/keystore} ]
+then
+ exit 0
+fi
+echo `date '+%F %T,000'` WARN Certificate file "${DRTR_PROV_KSTOREFILE:-etc/keystore}" is missing >>${DRTR_PROV_LOGS:-logs}/provint.log
+exit 1
+!EOF
diff --git a/datarouter-prov/src/main/resources/misc/log4j.drroute.properties b/datarouter-prov/src/main/resources/misc/log4j.drroute.properties
new file mode 100644
index 00000000..4ff4278c
--- /dev/null
+++ b/datarouter-prov/src/main/resources/misc/log4j.drroute.properties
@@ -0,0 +1,41 @@
+#-------------------------------------------------------------------------------
+# ============LICENSE_START==================================================
+# * org.onap.dmaap
+# * ===========================================================================
+# * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+# * ===========================================================================
+# * 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.
+# * ============LICENSE_END====================================================
+# *
+# * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+# *
+#-------------------------------------------------------------------------------
+#
+# AT&T - PROPRIETARY
+# THIS FILE CONTAINS PROPRIETARY INFORMATION OF
+# AT&T AND IS NOT TO BE DISCLOSED OR USED EXCEPT IN
+# ACCORDANCE WITH APPLICABLE AGREEMENTS.
+#
+# Copyright (c) 2013 AT&T Knowledge Ventures
+# Unpublished and Not for Publication
+# All Rights Reserved
+#
+# CVS: $Id: log4j.drroute.properties,v 1.1 2013/11/06 16:23:54 eby Exp $
+# This log4j properties file is used only by dr-route
+#
+
+log4j.rootLogger=INFO, stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
diff --git a/datarouter-prov/src/main/resources/misc/log4j.properties.tmpl b/datarouter-prov/src/main/resources/misc/log4j.properties.tmpl
new file mode 100644
index 00000000..ed1d7fab
--- /dev/null
+++ b/datarouter-prov/src/main/resources/misc/log4j.properties.tmpl
@@ -0,0 +1,68 @@
+cat <<!EOF
+#
+# AT&T - PROPRIETARY
+# THIS FILE CONTAINS PROPRIETARY INFORMATION OF
+# AT&T AND IS NOT TO BE DISCLOSED OR USED EXCEPT IN
+# ACCORDANCE WITH APPLICABLE AGREEMENTS.
+#
+# Copyright (c) 2013 AT&T Knowledge Ventures
+# Unpublished and Not for Publication
+# All Rights Reserved
+#
+# CVS: $Id: log4j.properties.tmpl,v 1.4 2014/01/13 19:44:57 eby Exp $
+#
+
+log4j.rootLogger=info
+
+#
+# Logger used for provisioning events
+#
+log4j.logger.com.att.research.datarouter.provisioning.events=info, eventlog
+log4j.additivity.com.att.research.datarouter.provisioning.events=false
+
+log4j.appender.eventlog=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.eventlog.file=${DRTR_PROV_LOGS:-/opt/app/datartr/logs}/provevent.log
+log4j.appender.eventlog.datePattern='.'yyyyMMdd
+log4j.appender.eventlog.append=true
+log4j.appender.eventlog.layout=org.apache.log4j.PatternLayout
+log4j.appender.eventlog.layout.ConversionPattern=%d %-5p [%t] - %m%n
+
+#
+# Logger used for internal provisioning server events
+#
+log4j.logger.com.att.research.datarouter.provisioning.internal=debug, intlog
+log4j.additivity.com.att.research.datarouter.provisioning.internal=false
+
+log4j.appender.intlog=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.intlog.file=${DRTR_PROV_LOGS:-/opt/app/datartr/logs}/provint.log
+log4j.appender.intlog.datePattern='.'yyyyMMdd
+log4j.appender.intlog.append=true
+log4j.appender.intlog.layout=org.apache.log4j.PatternLayout
+log4j.appender.intlog.layout.ConversionPattern=%d %-5p [%t] - %m%n
+
+#
+# Logger used for policy engine
+#
+log4j.logger.com.att.research.datarouter.authz.impl.ProvAuthorizer=debug, pelog
+log4j.additivity.com.att.research.datarouter.authz.impl.ProvAuthorizer=false
+
+log4j.appender.pelog=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.pelog.file=${DRTR_PROV_LOGS:-/opt/app/datartr/logs}/policyengine.log
+log4j.appender.pelog.datePattern='.'yyyyMMdd
+log4j.appender.pelog.append=true
+log4j.appender.pelog.layout=org.apache.log4j.PatternLayout
+log4j.appender.pelog.layout.ConversionPattern=%d %-5p [%t] - %m%n
+
+#
+# Logger used for Jetty server
+#
+log4j.logger.org.eclipse.jetty=info, jetty
+log4j.additivity.org.eclipse.jetty.server.Server=false
+
+log4j.appender.jetty=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.jetty.file=${DRTR_PROV_LOGS:-/opt/app/datartr/logs}/jetty.log
+log4j.appender.jetty.datePattern='.'yyyyMMdd
+log4j.appender.jetty.append=true
+log4j.appender.jetty.layout=org.apache.log4j.PatternLayout
+log4j.appender.jetty.layout.ConversionPattern=%d %-5p [%t] - %m%n
+!EOF
diff --git a/datarouter-prov/src/main/resources/misc/mysql_dr_schema.sql b/datarouter-prov/src/main/resources/misc/mysql_dr_schema.sql
new file mode 100644
index 00000000..837030c3
--- /dev/null
+++ b/datarouter-prov/src/main/resources/misc/mysql_dr_schema.sql
@@ -0,0 +1,139 @@
+create database datarouter;
+
+use datarouter;
+
+CREATE TABLE FEEDS (
+ FEEDID INT UNSIGNED NOT NULL PRIMARY KEY,
+ NAME VARCHAR(20) NOT NULL,
+ VERSION VARCHAR(20) NOT NULL,
+ DESCRIPTION VARCHAR(256),
+ AUTH_CLASS VARCHAR(32) NOT NULL,
+ PUBLISHER VARCHAR(8) NOT NULL,
+ SELF_LINK VARCHAR(256),
+ PUBLISH_LINK VARCHAR(256),
+ SUBSCRIBE_LINK VARCHAR(256),
+ LOG_LINK VARCHAR(256),
+ DELETED BOOLEAN DEFAULT FALSE,
+ LAST_MOD TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE TABLE FEED_ENDPOINT_IDS (
+ FEEDID INT UNSIGNED NOT NULL,
+ USERID VARCHAR(20) NOT NULL,
+ PASSWORD VARCHAR(32) NOT NULL
+);
+
+CREATE TABLE FEED_ENDPOINT_ADDRS (
+ FEEDID INT UNSIGNED NOT NULL,
+ ADDR VARCHAR(44) NOT NULL
+);
+
+CREATE TABLE SUBSCRIPTIONS (
+ SUBID INT UNSIGNED NOT NULL PRIMARY KEY,
+ FEEDID INT UNSIGNED NOT NULL,
+ DELIVERY_URL VARCHAR(256),
+ DELIVERY_USER VARCHAR(20),
+ DELIVERY_PASSWORD VARCHAR(32),
+ DELIVERY_USE100 BOOLEAN DEFAULT FALSE,
+ METADATA_ONLY BOOLEAN DEFAULT FALSE,
+ SUBSCRIBER VARCHAR(8) NOT NULL,
+ SELF_LINK VARCHAR(256),
+ LOG_LINK VARCHAR(256),
+ LAST_MOD TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE TABLE PARAMETERS (
+ KEYNAME VARCHAR(32) NOT NULL PRIMARY KEY,
+ VALUE VARCHAR(4096) NOT NULL
+);
+
+CREATE TABLE LOG_RECORDS (
+ TYPE ENUM('pub', 'del', 'exp') NOT NULL,
+ EVENT_TIME BIGINT NOT NULL, /* time of the publish request */
+ PUBLISH_ID VARCHAR(64) NOT NULL, /* unique ID assigned to this publish attempt */
+ FEEDID INT UNSIGNED NOT NULL, /* pointer to feed in FEEDS */
+ REQURI VARCHAR(256) NOT NULL, /* request URI */
+ METHOD ENUM('DELETE', 'GET', 'HEAD', 'OPTIONS', 'PUT', 'POST', 'TRACE') NOT NULL, /* HTTP method */
+ CONTENT_TYPE VARCHAR(256) NOT NULL, /* content type of published file */
+ CONTENT_LENGTH BIGINT UNSIGNED NOT NULL, /* content length of published file */
+
+ FEED_FILEID VARCHAR(128), /* file ID of published file */
+ REMOTE_ADDR VARCHAR(40), /* IP address of publishing endpoint */
+ USER VARCHAR(20), /* user name of publishing endpoint */
+ STATUS SMALLINT, /* status code returned to delivering agent */
+
+ DELIVERY_SUBID INT UNSIGNED, /* pointer to subscription in SUBSCRIPTIONS */
+ DELIVERY_FILEID VARCHAR(128), /* file ID of file being delivered */
+ RESULT SMALLINT, /* result received from subscribing agent */
+
+ ATTEMPTS INT, /* deliveries attempted */
+ REASON ENUM('notRetryable', 'retriesExhausted'),
+
+ RECORD_ID BIGINT UNSIGNED NOT NULL PRIMARY KEY, /* unique ID for this record */
+
+ INDEX (FEEDID) USING BTREE,
+ INDEX (DELIVERY_SUBID) USING BTREE,
+ INDEX (RECORD_ID) USING BTREE
+) ENGINE = MyISAM;
+
+CREATE TABLE INGRESS_ROUTES (
+ SEQUENCE INT UNSIGNED NOT NULL,
+ FEEDID INT UNSIGNED NOT NULL,
+ USERID VARCHAR(20),
+ SUBNET VARCHAR(44),
+ NODESET INT UNSIGNED NOT NULL
+);
+
+CREATE TABLE EGRESS_ROUTES (
+ SUBID INT UNSIGNED NOT NULL PRIMARY KEY,
+ NODEID INT UNSIGNED NOT NULL
+);
+
+CREATE TABLE NETWORK_ROUTES (
+ FROMNODE INT UNSIGNED NOT NULL,
+ TONODE INT UNSIGNED NOT NULL,
+ VIANODE INT UNSIGNED NOT NULL
+);
+
+CREATE TABLE NODESETS (
+ SETID INT UNSIGNED NOT NULL,
+ NODEID INT UNSIGNED NOT NULL
+);
+
+CREATE TABLE NODES (
+ NODEID INT UNSIGNED NOT NULL PRIMARY KEY,
+ NAME VARCHAR(255) NOT NULL,
+ ACTIVE BOOLEAN DEFAULT TRUE
+);
+
+CREATE TABLE GROUPS (
+ GROUPID INT UNSIGNED NOT NULL PRIMARY KEY,
+ AUTHID VARCHAR(100) NOT NULL,
+ NAME VARCHAR(50) NOT NULL,
+ DESCRIPTION VARCHAR(255),
+ CLASSIFICATION VARCHAR(20) NOT NULL,
+ MEMBERS TINYTEXT,
+ LAST_MOD TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+
+-- 'PROV_AUTH_ADDRESSES', '192.168.56.1' ipv4 address of provision server
+INSERT INTO PARAMETERS VALUES
+ ('ACTIVE_POD', '127.0.0.1'),
+ ('PROV_ACTIVE_NAME', '${PROV_ACTIVE_NAME}'),
+ ('STANDBY_POD', '${DRTR_PROV_STANDBYPOD}'),
+ ('PROV_NAME', 'ALCDTL47TJ6015:6080'),
+ ('NODES', '127.0.0.1:8080'),
+ ('PROV_DOMAIN', '127.0.0.1'),
+ ('DELIVERY_INIT_RETRY_INTERVAL', '10'),
+ ('DELIVERY_MAX_AGE', '86400'),
+ ('DELIVERY_MAX_RETRY_INTERVAL', '3600'),
+ ('DELIVERY_RETRY_RATIO', '2'),
+ ('LOGROLL_INTERVAL', '300'),
+ ('PROV_AUTH_ADDRESSES', '192.168.56.1'),
+ ('PROV_AUTH_SUBJECTS', ''),
+ ('PROV_MAXFEED_COUNT', '10000'),
+ ('PROV_MAXSUB_COUNT', '100000'),
+ ('PROV_REQUIRE_CERT', 'false'),
+ ('PROV_REQUIRE_SECURE', 'false'),
+ ('_INT_VALUES', 'LOGROLL_INTERVAL|PROV_MAXFEED_COUNT|PROV_MAXSUB_COUNT|DELIVERY_INIT_RETRY_INTERVAL|DELIVERY_MAX_RETRY_INTERVAL|DELIVERY_RETRY_RATIO|DELIVERY_MAX_AGE')
+ ; \ No newline at end of file
diff --git a/datarouter-prov/src/main/resources/misc/notes b/datarouter-prov/src/main/resources/misc/notes
new file mode 100644
index 00000000..e3f872e7
--- /dev/null
+++ b/datarouter-prov/src/main/resources/misc/notes
@@ -0,0 +1,78 @@
+Package notes for com.att.dmaap.datarouter:prov
+
+This component is for the Data Router Provisioning Server software.
+
+The following pre-requisite components should already be present:
+ com.att.aft.swm:swm-cli
+ com.att.aft.swm:swm-node
+ - SWM Variables: AFTSWM_AUTOLINK_PARENTS=/opt/app:/opt/app/workload,/opt/app/aft:/opt/app/workload/aft
+ com.att.platform:uam-auto
+ com.att.java:jdk8lin
+ com.att.platform:initd
+ com.att.platform:port-fwd
+ - SWM Variables: PLATFORM_PORT_FWD=80,8080|443,8443
+ mysql:mysql
+ mysql:mysql-config
+ - SWM Variables: MYSQL_CONFIG_SIZE=small
+ MYSQL_DB_DATABASES=datarouter
+ MYSQL_DB_datarouter_USERS=datarouter,tier2
+ MYSQL_DB_datarouter_USERS_datarouter_LEVEL=RW
+ MYSQL_DB_datarouter_USERS_datarouter_PASSWORD=datarouter
+ MYSQL_DB_datarouter_USERS_tier2_LEVEL=RO
+ MYSQL_DB_datarouter_USERS_tier2_PASSWORD=<password>
+ MYSQL_MAX_ALLOWED_PACKET=32M
+ MYSQL_MAX_CONNECTIONS=300
+ MYSQL_PASSWORD=datarouter
+ MYSQL_PORT=3306
+
+
+In a production environment, the SWM variables that MUST be overwridden are:
+ DRTR_PROV_ACTIVEPOD, DRTR_PROV_STANDBYPOD, DRTR_PROV_NODES
+In addition, in a non-production environment, the DRTR_PROV_CNAME SWM variable
+must also be overwridden.
+
+The SWM variables that can be set to control the provisioning server are:
+
+DRTR_PROV_ACTIVEPOD
+ The FQDN of the active POD
+DRTR_PROV_STANDBYPOD
+ The FQDN of the standby POD
+DRTR_PROV_CNAME (default feeds-drtr.web.att.com)
+ The DNS CNAME used for the prov server in this environment.
+DRTR_PROV_NODES
+ Pipe-delimited list of DR nodes to init the DB with.
+DRTR_PROV_DOMAIN (default web.att.com)
+ Domain to use for non-FQDN node names
+
+DRTR_PROV_INTHTTPPORT (default 8080)
+ The TCP/IP port number the component should listen on for "go fetch"
+ requests from the provisioning server
+DRTR_PROV_INTHTTPSPORT (default 8443)
+ The TCP/IP port number the component should listen on for publish
+ requests from feed publishers and other nodes
+DRTR_PROV_LOGS (default /opt/app/datartr/logs)
+ The directory where log files should be kept
+DRTR_PROV_SPOOL (default /opt/app/datartr/spool)
+ The directory where logfiles from the DR nodes are spooled before being
+ imported into the DB.
+
+DRTR_PROV_KEYMGRPASS (default changeit)
+ The password for the key manager
+DRTR_PROV_KSTOREFILE (default /opt/app/datartr/etc/keystore)
+ The java keystore file containing the server certificate and private key
+ for this server
+DRTR_PROV_KSTOREPASS (default changeit)
+ The password for the keystore file
+DRTR_PROV_TSTOREFILE (by default, use the truststore from the Java JDK)
+ The java keystore file containing the trusted certificate authority
+ certificates
+DRTR_PROV_TSTOREPASS (default changeit)
+ The password for the trust store file. Only applies if a trust store
+ file is specified.
+
+DRTR_PROV_DBLOGIN (default datarouter)
+ The login used to access MySQL
+DRTR_PROV_DBPASS (default datarouter)
+ The password used to access MySQL
+DRTR_PROV_DBSCRIPTS (default /opt/app/datartr/etc)
+ The directory containing DB initialization scripts
diff --git a/datarouter-prov/src/main/resources/misc/provcmd b/datarouter-prov/src/main/resources/misc/provcmd
new file mode 100644
index 00000000..63efa543
--- /dev/null
+++ b/datarouter-prov/src/main/resources/misc/provcmd
@@ -0,0 +1,163 @@
+#!/bin/bash
+#
+# AT&T - PROPRIETARY
+# THIS FILE CONTAINS PROPRIETARY INFORMATION OF
+# AT&T AND IS NOT TO BE DISCLOSED OR USED EXCEPT IN
+# ACCORDANCE WITH APPLICABLE AGREEMENTS.
+#
+# Copyright (c) 2013 AT&T Knowledge Ventures
+# Unpublished and Not for Publication
+# All Rights Reserved
+#
+# provcmd -- A script to interact with a provisioning server to manage the provisioning parameters.
+# Set $VERBOSE to a non-empty string to see the curl commands as they are executed.
+#
+# $Id: provcmd,v 1.6 2014/03/31 13:23:33 eby Exp $
+#
+
+PATH=/opt/app/datartr/bin:/bin:/usr/bin:$PATH
+PROVCMD="$0"
+export PATH PROVSRVR PROVCMD NOPROXY
+
+if [ ! -x /usr/bin/curl ]
+then
+ echo provcmd: curl is required for this tool.
+ exit 1
+fi
+optloop=
+while [ -z "$optloop" ]
+do
+ if [ "$1" == '-s' ]
+ then
+ shift
+ PROVSRVR="$1"
+ shift
+ elif [ "$1" == '-v' ]
+ then
+ shift
+ VERBOSE=x
+ elif [ "$1" == '-N' ]
+ then
+ shift
+ NOPROXY='?noproxy=1'
+ else
+ optloop=1
+ fi
+done
+if [ -z "$PROVSRVR" ]
+then
+ echo "provcmd: you need to specify the server, either via the -s option"
+ echo " or by setting and exporting PROVSRVR"
+ exit 1
+fi
+
+CMD="$1"
+shift
+if [ "$CMD" == 'delete' ]
+then
+ if [ $# -gt 0 ]
+ then
+ for i
+ do
+ [ -n "$VERBOSE" ] && echo curl -4 -k -X DELETE "https://$PROVSRVR/internal/api/$1$NOPROXY"
+ curl -4 -k -X DELETE "https://$PROVSRVR/internal/api/$1$NOPROXY"
+ done
+ exit 0
+ fi
+elif [ "$CMD" == 'create' ]
+then
+ if [ $# -eq 2 ]
+ then
+ # create (with POST), then set the value
+ [ -n "$VERBOSE" ] && echo curl -4 -k -X POST --data '' "https://$PROVSRVR/internal/api/$1$NOPROXY"
+ curl -4 -k -X POST --data '' "https://$PROVSRVR/internal/api/$1$NOPROXY"
+ $PROVCMD set "$1" "$2"
+ exit 0
+ fi
+elif [ "$CMD" == 'get' ]
+then
+ if [ $# -eq 1 ]
+ then
+ # get
+ [ -n "$VERBOSE" ] && echo curl -4 -k "https://$PROVSRVR/internal/api/$1$NOPROXY"
+ curl -4 -k "https://$PROVSRVR/internal/api/$1$NOPROXY" 2>/dev/null | tr '|' '\012' | sort
+ exit 0
+ fi
+elif [ "$CMD" == 'set' ]
+then
+ if [ $# -ge 2 ]
+ then
+ p="$1"
+ shift
+ v=""
+ for i; do [ -n "$v" ] && v="$v|"; v="$v$i"; done
+ # set (with PUT)
+ ue=`urlencode "$v"`
+ NOPROXY=`echo $NOPROXY | tr '?' '&'`
+ [ -n "$VERBOSE" ] && echo curl -4 -k -X PUT "https://$PROVSRVR/internal/api/$p?val=$ue$NOPROXY"
+ curl -4 -k -X PUT "https://$PROVSRVR/internal/api/$p?val=$ue$NOPROXY"
+ exit 0
+ fi
+elif [ "$CMD" == 'append' ]
+then
+ if [ $# -ge 2 ]
+ then
+ p="$1"
+ shift
+ tmp=`curl -4 -k "https://$PROVSRVR/internal/api/$p$NOPROXY" 2>/dev/null`
+ $PROVCMD set "$p" "$tmp" "$@"
+ exit 0
+ fi
+elif [ "$CMD" == 'remove' ]
+then
+ if [ $# -eq 2 ]
+ then
+ p="$1"
+ rm="$2"
+ $PROVCMD get "$p" | grep -v "^$rm\$" > /tmp/pc$$
+ IFS=$'\r\n'
+ $PROVCMD set "$p" `cat /tmp/pc$$`
+ rm /tmp/pc$$
+ exit 0
+ fi
+fi
+
+# Some error somewhere - display usage
+cat <<'EOF'
+usage: provcmd [ -s server ] delete name1 [ name2 ... ]
+ provcmd [ -s server ] get name
+ provcmd [ -s server ] create name value
+ provcmd [ -s server ] set name value1 [ value2 ... ]
+ provcmd [ -s server ] append name value1 [ value2 ... ]
+ provcmd [ -s server ] remove name value
+
+delete - remove the parameters named name1, name2 ...
+get - displays the parameters' value
+create - creates a new parameter
+set - sets the value of an existing parameter
+append - appends the value to a list-based parameter
+remove - removes a value from a list based parameter
+
+server - the provisioning server FQDN (feeds-drtr.web.att.com for production)
+
+Standard Parameters Names:
+------------------------------
+ACTIVE_POD
+DELIVERY_INIT_RETRY_INTERVAL
+DELIVERY_MAX_AGE
+DELIVERY_MAX_RETRY_INTERVAL
+DELIVERY_RETRY_RATIO
+LOGROLL_INTERVAL
+NODES
+PROV_ACTIVE_NAME
+PROV_AUTH_ADDRESSES
+PROV_AUTH_SUBJECTS
+PROV_DOMAIN
+PROV_MAXFEED_COUNT
+PROV_MAXSUB_COUNT
+PROV_NAME
+PROV_REQUIRE_CERT
+PROV_REQUIRE_SECURE
+STANDBY_POD
+EOF
+exit 1
diff --git a/datarouter-prov/src/main/resources/misc/runreports b/datarouter-prov/src/main/resources/misc/runreports
new file mode 100644
index 00000000..009b7496
--- /dev/null
+++ b/datarouter-prov/src/main/resources/misc/runreports
@@ -0,0 +1,54 @@
+#!/bin/bash
+#
+# AT&T - PROPRIETARY
+# THIS FILE CONTAINS PROPRIETARY INFORMATION OF
+# AT&T AND IS NOT TO BE DISCLOSED OR USED EXCEPT IN
+# ACCORDANCE WITH APPLICABLE AGREEMENTS.
+#
+# Copyright (c) 2013 AT&T Knowledge Ventures
+# Unpublished and Not for Publication
+# All Rights Reserved
+#
+# This script runs daily to generate reports files in the logs directory.
+#
+# $Id: runreports,v 1.2 2013/11/06 16:23:54 eby Exp $
+#
+
+umask 0022
+
+JAVA_HOME=/opt/java/jdk/jdk180
+JAVA_OPTS="-Xms1G -Xmx4G"
+JAVA_CLASS=com.att.research.datarouter.reports.Report
+TZ=GMT0
+PATH=$JAVA_HOME/bin:/bin:/usr/bin
+CLASSPATH=`echo /opt/app/datartr/etc /opt/app/datartr/lib/*.jar | tr ' ' ':'`
+LOGDIR=/opt/app/datartr/logs
+YESTERDAY=`/bin/date --date=yesterday '+%Y%m%d'`
+
+export CLASSPATH JAVA_HOME JAVA_OPTS TZ PATH
+
+ID=`id -n -u`
+GRP=`id -n -g`
+if [ "$ID" != "datartr" ]
+then
+ echo runreports must be started as user datartr not $ID
+ exit 1
+fi
+if [ "$GRP" != "datartr" ]
+then
+ echo runreports must be started as group datartr not $GRP
+ exit 1
+fi
+if [ "`pgrep -u mysql mysqld`" = "" ]
+then
+ echo MySQL is not running. It must be started before runreports
+ exit 1
+fi
+
+# Volume report
+java $JAVA_OPTS $JAVA_CLASS -t volume -o $LOGDIR/volume.csv.$YESTERDAY yesterday </dev/null >/dev/null
+
+# Subscriber report
+java $JAVA_OPTS $JAVA_CLASS -t subscriber -o $LOGDIR/subscriber.csv.$YESTERDAY yesterday </dev/null >/dev/null
+
+exit 0
diff --git a/datarouter-prov/src/main/resources/provserver.properties b/datarouter-prov/src/main/resources/provserver.properties
new file mode 100644
index 00000000..af5073e5
--- /dev/null
+++ b/datarouter-prov/src/main/resources/provserver.properties
@@ -0,0 +1,48 @@
+#-------------------------------------------------------------------------------
+# ============LICENSE_START==================================================
+# * org.onap.dmaap
+# * ===========================================================================
+# * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+# * ===========================================================================
+# * 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.
+# * ============LICENSE_END====================================================
+# *
+# * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+# *
+#-------------------------------------------------------------------------------
+
+
+#Jetty Server properties
+com.att.research.datarouter.provserver.http.port = 8080
+com.att.research.datarouter.provserver.https.port = 8443
+com.att.research.datarouter.provserver.https.relaxation = true
+com.att.research.datarouter.provserver.keymanager.password = changeit
+com.att.research.datarouter.provserver.keystore.type = jks
+com.att.research.datarouter.provserver.keystore.path = /opt/app/datartr/self_signed/keystore.jks
+
+com.att.research.datarouter.provserver.keystore.password = changeit
+#com.att.research.datarouter.provserver.truststore.path = /home/eby/dr2/misc/cacerts+1
+#com.att.research.datarouter.provserver.truststore.path = /usr/lib/jvm/java-8-oracle/jre/lib/security/cacerts
+com.att.research.datarouter.provserver.truststore.path = /opt/app/datartr/self_signed/cacerts.jks
+
+com.att.research.datarouter.provserver.truststore.password = changeit
+com.att.research.datarouter.provserver.accesslog.dir = /opt/app/datartr/logs
+com.att.research.datarouter.provserver.spooldir = /opt/app/datartr/spool
+#com.att.research.datarouter.provserver.dbscripts = /home/eby/dr2/cvs/datarouter/prov/misc/
+com.att.research.datarouter.provserver.logretention = 30
+
+# Database access
+com.att.research.datarouter.db.driver = com.mysql.jdbc.Driver
+com.att.research.datarouter.db.url = jdbc:mysql://172.18.0.2:3306/datarouter
+com.att.research.datarouter.db.login = datarouter
+com.att.research.datarouter.db.password = datarouter
diff --git a/datarouter-prov/src/main/resources/startup.sh b/datarouter-prov/src/main/resources/startup.sh
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/datarouter-prov/src/main/resources/startup.sh
diff --git a/datarouter-prov/src/main/resources/subscriber.jar b/datarouter-prov/src/main/resources/subscriber.jar
new file mode 100644
index 00000000..c8e4775a
--- /dev/null
+++ b/datarouter-prov/src/main/resources/subscriber.jar
Binary files differ
diff --git a/datarouter-prov/src/test/java/datarouter/provisioning/AllTests.java b/datarouter-prov/src/test/java/datarouter/provisioning/AllTests.java
new file mode 100644
index 00000000..92159552
--- /dev/null
+++ b/datarouter-prov/src/test/java/datarouter/provisioning/AllTests.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package datarouter.provisioning;
+
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+@RunWith(Suite.class)
+@SuiteClasses({
+ testDRFeedsPost.class,
+ testDRFeedsGet.class,
+ testDRFeedsPut.class,
+ testDRFeedsDelete.class,
+ testFeedPut.class,
+ testSubscribePost.class,
+ testInternalGet.class,
+ testInternalMisc.class,
+ testPublish.class,
+ testLogGet.class,
+ testFeedDelete.class,
+ testCleanup.class,
+ testRLEBitSet.class
+})
+public class AllTests {
+}
diff --git a/datarouter-prov/src/test/java/datarouter/provisioning/FillDB.java b/datarouter-prov/src/test/java/datarouter/provisioning/FillDB.java
new file mode 100644
index 00000000..c41cc801
--- /dev/null
+++ b/datarouter-prov/src/test/java/datarouter/provisioning/FillDB.java
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package datarouter.provisioning;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.entity.ContentType;
+import org.apache.http.impl.client.AbstractHttpClient;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.util.EntityUtils;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import com.att.research.datarouter.provisioning.FeedServlet;
+
+public class FillDB {
+ public static void main(String[] args)
+ throws KeyStoreException, FileNotFoundException, KeyManagementException, UnrecoverableKeyException, NoSuchAlgorithmException
+ {
+ AbstractHttpClient httpclient = new DefaultHttpClient();
+
+ String keystore = "/home/eby/dr2/misc/client.keystore";
+ String kspass = "changeit";
+ KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ FileInputStream instream = new FileInputStream(new File(keystore));
+ try {
+ trustStore.load(instream, kspass.toCharArray());
+ } catch (Exception x) {
+ System.err.println("READING KEYSTORE: "+x);
+ } finally {
+ try { instream.close(); } catch (Exception ignore) {}
+ }
+
+ SSLSocketFactory socketFactory = new SSLSocketFactory(trustStore, "changeit", trustStore);
+ Scheme sch = new Scheme("https", 443, socketFactory);
+ httpclient.getConnectionManager().getSchemeRegistry().register(sch);
+
+ JSONObject jo = buildFeedRequest();
+ for (int i = 0; i < 10000; i++) {
+ jo.put("version", ""+System.currentTimeMillis());
+ int rv = -1;
+ String url = "https://conwy.proto.research.att.com:6443/";
+ HttpPost httpPost = new HttpPost(url);
+ try {
+ httpPost.addHeader(FeedServlet.BEHALF_HEADER, "JUnit");
+ String t = jo.toString();
+ HttpEntity body = new ByteArrayEntity(t.getBytes(), ContentType.create(FeedServlet.FEED_CONTENT_TYPE));
+ httpPost.setEntity(body);
+
+ HttpResponse response = httpclient.execute(httpPost);
+ rv = response.getStatusLine().getStatusCode();
+ HttpEntity entity = response.getEntity();
+ EntityUtils.consume(entity);
+ } catch (IOException e) {
+ System.err.println(e);
+ } finally {
+ httpPost.releaseConnection();
+ }
+ System.out.println(i + " " + rv);
+ }
+ }
+ private static JSONObject buildFeedRequest() {
+ JSONObject jo = new JSONObject();
+ jo.put("name", "feed");
+ jo.put("version", ""+System.currentTimeMillis());
+ jo.put("description", "Sample feed used by JUnit to test");
+
+ JSONObject jo2 = new JSONObject();
+ jo2.put("classification", "unrestricted");
+
+ JSONArray ja = new JSONArray();
+ JSONObject jo3 = new JSONObject();
+ jo3.put("id", "id001");
+ jo3.put("password", "re1kwelj");
+ JSONObject jo4 = new JSONObject();
+ jo4.put("id", "id002");
+ jo4.put("password", "o9eqlmbd");
+ ja.put(jo3);
+ ja.put(jo4);
+ jo2.put("endpoint_ids", ja);
+
+ ja = new JSONArray();
+ ja.put("10.0.0.1");
+ ja.put("192.168.0.1");
+ ja.put("135.207.136.128/25");
+ jo2.put("endpoint_addrs", ja);
+
+ jo.put("authorization", jo2);
+ return jo;
+ }
+}
diff --git a/datarouter-prov/src/test/java/datarouter/provisioning/package.html b/datarouter-prov/src/test/java/datarouter/provisioning/package.html
new file mode 100644
index 00000000..d0383b8d
--- /dev/null
+++ b/datarouter-prov/src/test/java/datarouter/provisioning/package.html
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+<html>
+<body>
+<p>
+This package provides JUnit tests for the provisioning server.
+</p>
+</body>
+</html>
diff --git a/datarouter-prov/src/test/java/datarouter/provisioning/testBase.java b/datarouter-prov/src/test/java/datarouter/provisioning/testBase.java
new file mode 100644
index 00000000..9a96933b
--- /dev/null
+++ b/datarouter-prov/src/test/java/datarouter/provisioning/testBase.java
@@ -0,0 +1,158 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package datarouter.provisioning;
+
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.util.Properties;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.StatusLine;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.impl.client.AbstractHttpClient;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.util.EntityUtils;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+import org.junit.After;
+import org.junit.Before;
+
+import com.att.research.datarouter.provisioning.FeedServlet;
+
+public class testBase {
+ /** The properties file to read the DB properties from */
+ public static final String CONFIG_FILE = "tests.properties";
+
+ public Properties props;
+ protected AbstractHttpClient httpclient;
+ protected String s_33;
+ protected String s_257;
+ protected static JSONObject db_state;
+
+ @Before
+ public void setUp() throws Exception {
+ if (props == null) {
+ props = new Properties();
+ InputStream inStream = getClass().getClassLoader().getResourceAsStream(CONFIG_FILE);
+ try {
+ props.load(inStream);
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ inStream.close();
+ }
+ }
+
+ httpclient = new DefaultHttpClient();
+ String s = "0123456789ABCDEF";
+ s_33 = s + s + "!";
+ s = s + s + s + s;
+ s_257 = s + s + s + s + "!";
+
+ // keystore
+ String store = props.getProperty("test.keystore");
+ String pass = props.getProperty("test.kspassword");
+ KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ FileInputStream instream = new FileInputStream(new File(store));
+ try {
+ keyStore.load(instream, pass.toCharArray());
+ } catch (Exception x) {
+ System.err.println("READING KEYSTORE: "+x);
+ } finally {
+ try { instream.close(); } catch (Exception ignore) {}
+ }
+
+ store = props.getProperty("test.truststore");
+ pass = props.getProperty("test.tspassword");
+ KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ instream = new FileInputStream(new File(store));
+ try {
+ trustStore.load(instream, pass.toCharArray());
+ } catch (Exception x) {
+ System.err.println("READING TRUSTSTORE: "+x);
+ } finally {
+ try { instream.close(); } catch (Exception ignore) {}
+ }
+
+ SSLSocketFactory socketFactory = new SSLSocketFactory(keyStore, "changeit", trustStore);
+ Scheme sch = new Scheme("https", 443, socketFactory);
+ httpclient.getConnectionManager().getSchemeRegistry().register(sch);
+ }
+
+ public JSONObject getDBstate() {
+ // set db_state!
+ if (db_state == null) {
+ String url = props.getProperty("test.host") + "/internal/prov";
+ HttpGet httpGet = new HttpGet(url);
+ try {
+ httpGet.addHeader(FeedServlet.BEHALF_HEADER, "JUnit");
+ HttpResponse response = httpclient.execute(httpGet);
+ HttpEntity entity = response.getEntity();
+ String ctype = entity.getContentType().getValue().trim();
+ // save the response body as db_state
+ boolean ok = ctype.equals(FeedServlet.PROVFULL_CONTENT_TYPE1);
+ ok |= ctype.equals(FeedServlet.PROVFULL_CONTENT_TYPE2);
+ if (ok) {
+ db_state = null;
+ try {
+ db_state = new JSONObject(new JSONTokener(entity.getContent()));
+ } catch (Exception e) {
+ fail("Bad JSON: "+e.getMessage());
+ }
+ } else {
+ EntityUtils.consume(entity);
+ }
+ } catch (IOException e) {
+ fail(e.getMessage());
+ } finally {
+ httpGet.releaseConnection();
+ }
+ }
+ return db_state;
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ // When HttpClient instance is no longer needed,
+ // shut down the connection manager to ensure
+ // immediate deallocation of all system resources
+ httpclient.getConnectionManager().shutdown();
+ }
+
+ protected void ckResponse(HttpResponse response, int expect) {
+ System.out.println(response.getStatusLine());
+ StatusLine sl = response.getStatusLine();
+ int code = sl.getStatusCode();
+ if (code != expect)
+ fail("Unexpected response, expect "+expect+" got "+code+" "+sl.getReasonPhrase());
+ }
+}
diff --git a/datarouter-prov/src/test/java/datarouter/provisioning/testCleanup.java b/datarouter-prov/src/test/java/datarouter/provisioning/testCleanup.java
new file mode 100644
index 00000000..fa1c5f4c
--- /dev/null
+++ b/datarouter-prov/src/test/java/datarouter/provisioning/testCleanup.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package datarouter.provisioning;
+
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.util.EntityUtils;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.att.research.datarouter.provisioning.FeedServlet;
+
+public class testCleanup extends testBase {
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ getDBstate();
+ }
+
+ @Test
+ public void testNormal() {
+ // Delete all feeds w/JUnit as publisher
+ JSONArray ja = db_state.getJSONArray("feeds");
+ for (int i = 0; i < ja.length(); i++) {
+ JSONObject feed = ja.getJSONObject(i);
+ if (feed != null && !feed.getBoolean("deleted")) {
+ if (feed.getString("publisher").equals("JUnit")) {
+ int feedid = feed.getInt("feedid");
+ delete("/feed/"+feedid);
+ }
+ }
+ }
+ // Delete all subscriptions w/JUnit as subscriber
+ ja = db_state.getJSONArray("subscriptions");
+ for (int i = 0; i < ja.length(); i++) {
+ JSONObject sub = ja.getJSONObject(i);
+ if (sub != null && sub.getString("subscriber").equals("JUnit")) {
+ int subid = sub.getInt("subid");
+ delete("/subs/"+subid);
+ }
+ }
+ }
+ private void delete(String uri) {
+ String url = props.getProperty("test.host") + uri;;
+ HttpDelete del = new HttpDelete(url);
+ try {
+ del.addHeader(FeedServlet.BEHALF_HEADER, "JUnit");
+ HttpResponse response = httpclient.execute(del);
+ HttpEntity entity = response.getEntity();
+ EntityUtils.consume(entity);
+ } catch (IOException e) {
+ fail(e.getMessage());
+ } finally {
+ del.releaseConnection();
+ }
+ }
+}
diff --git a/datarouter-prov/src/test/java/datarouter/provisioning/testDRFeedsDelete.java b/datarouter-prov/src/test/java/datarouter/provisioning/testDRFeedsDelete.java
new file mode 100644
index 00000000..fb4a554b
--- /dev/null
+++ b/datarouter-prov/src/test/java/datarouter/provisioning/testDRFeedsDelete.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package datarouter.provisioning;
+
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.util.EntityUtils;
+import org.junit.Test;
+
+import com.att.research.datarouter.provisioning.FeedServlet;
+
+public class testDRFeedsDelete extends testBase {
+ @Test
+ public void testNotAllowed() {
+ String url = props.getProperty("test.host") + "/";
+ HttpDelete del = new HttpDelete(url);
+ try {
+ del.addHeader(FeedServlet.BEHALF_HEADER, "JUnit");
+
+ HttpResponse response = httpclient.execute(del);
+ ckResponse(response, HttpServletResponse.SC_METHOD_NOT_ALLOWED);
+
+ HttpEntity entity = response.getEntity();
+ EntityUtils.consume(entity);
+ } catch (IOException e) {
+ fail(e.getMessage());
+ } finally {
+ del.releaseConnection();
+ }
+ }
+}
diff --git a/datarouter-prov/src/test/java/datarouter/provisioning/testDRFeedsGet.java b/datarouter-prov/src/test/java/datarouter/provisioning/testDRFeedsGet.java
new file mode 100644
index 00000000..33303fa4
--- /dev/null
+++ b/datarouter-prov/src/test/java/datarouter/provisioning/testDRFeedsGet.java
@@ -0,0 +1,188 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package datarouter.provisioning;
+
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.util.EntityUtils;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.att.research.datarouter.provisioning.FeedServlet;
+
+public class testDRFeedsGet extends testBase {
+ private JSONArray returnedlist;
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ getDBstate();
+ }
+
+ @Test
+ public void testNormal() {
+ testCommon(HttpServletResponse.SC_OK);
+ int expect = 0;
+ JSONArray ja = db_state.getJSONArray("feeds");
+ for (int i = 0; i < ja.length(); i++) {
+ JSONObject jo = ja.getJSONObject(i);
+ if (!jo.getBoolean("deleted"))
+ expect++;
+ }
+ if (returnedlist.length() != expect)
+ fail("bad length, got "+ returnedlist.length() + " expect " + expect);
+ }
+ @Test
+ public void testNormalGoodName() {
+ JSONArray ja = db_state.getJSONArray("feeds");
+ JSONObject feed0 = ja.getJSONObject(0);
+ String name = feed0.getString("name");
+ String query = "?name=" + name;
+ int expect = 0;
+ for (int n = 0; n < ja.length(); n++) {
+ JSONObject jo = ja.getJSONObject(n);
+ if (!jo.getBoolean("deleted") && jo.getString("name").equals(name))
+ expect++;
+ }
+ testCommon(HttpServletResponse.SC_OK, query, FeedServlet.FEEDLIST_CONTENT_TYPE, "JUnit");
+ if (returnedlist.length() != expect)
+ fail("bad length, got "+ returnedlist.length() + " expect "+expect);
+ }
+ @Test
+ public void testNormalBadName() {
+ String query = "?name=ZZTOP123456";
+ testCommon(HttpServletResponse.SC_OK, query, FeedServlet.FEEDLIST_CONTENT_TYPE, "JUnit");
+ if (returnedlist.length() != 0)
+ fail("bad length, got "+ returnedlist.length() + " expect 0");
+ }
+ @Test
+ public void testNormalBadPath() {
+ String query = "flarg/?publisher=JUnit";
+ testCommon(HttpServletResponse.SC_NOT_FOUND, query, "text/html;charset=ISO-8859-1", "JUnit");
+ }
+ @Test
+ public void testNormalGoodPublisher() {
+ JSONArray ja = db_state.getJSONArray("feeds");
+ JSONObject feed0 = ja.getJSONObject(0);
+ String query = "?publisher=" + feed0.getString("publisher");
+ testCommon(HttpServletResponse.SC_OK, query, FeedServlet.FEEDLIST_CONTENT_TYPE, "JUnit");
+ int expect = 0;
+ for (int i = 0; i < ja.length(); i++) {
+ JSONObject jo = ja.getJSONObject(i);
+ if (jo.getString("publisher").equals(feed0.getString("publisher")) && !jo.getBoolean("deleted"))
+ expect++;
+ }
+ if (returnedlist.length() != expect)
+ fail("bad length, got "+returnedlist.length()+" expected "+expect);
+ }
+ @Test
+ public void testNormalBadPublisher() {
+ String query = "?publisher=ZZTOP123456";
+ testCommon(HttpServletResponse.SC_OK, query, FeedServlet.FEEDLIST_CONTENT_TYPE, "JUnit");
+ if (returnedlist.length() != 0)
+ fail("bad length");
+ }
+ @Test
+ public void testNormalGoodSubscriber() {
+ JSONArray ja = db_state.getJSONArray("subscriptions");
+ if (ja.length() > 0) {
+ JSONObject sub0 = ja.getJSONObject(0);
+ String query = "?subscriber=" + sub0.getString("subscriber");
+ testCommon(HttpServletResponse.SC_OK, query, FeedServlet.FEEDLIST_CONTENT_TYPE, "JUnit");
+// aarg! - this is complicated!
+// int expect = 0;
+// for (int i = 0; i < ja.length(); i++) {
+// JSONObject jo = ja.getJSONObject(i);
+// if (jo.getString("subscriber").equals(sub0.getString("subscriber")))
+// expect++;
+// }
+// if (returnedlist.length() != 1)
+// fail("bad length "+returnedlist.toString());
+ } else {
+ // There are no subscriptions yet, so use a made up name
+ testCommon(HttpServletResponse.SC_OK, "?subscriber=foo", FeedServlet.FEEDLIST_CONTENT_TYPE, "JUnit");
+ }
+ }
+ @Test
+ public void testNormalBadSubscriber() {
+ String query = "?subscriber=ZZTOP123456";
+ testCommon(HttpServletResponse.SC_OK, query, FeedServlet.FEEDLIST_CONTENT_TYPE, "JUnit");
+ if (returnedlist.length() != 0)
+ fail("bad length");
+ }
+ private void testCommon(int expect) {
+ testCommon(expect, "", FeedServlet.FEEDLIST_CONTENT_TYPE, "JUnit");
+ }
+ private void testCommon(int expect, String query, String ectype, String bhdr) {
+ String url = props.getProperty("test.host") + "/" + query;
+ HttpGet httpGet = new HttpGet(url);
+ try {
+ if (bhdr != null)
+ httpGet.addHeader(FeedServlet.BEHALF_HEADER, bhdr);
+
+ HttpResponse response = httpclient.execute(httpGet);
+ ckResponse(response, expect);
+
+ HttpEntity entity = response.getEntity();
+ String ctype = entity.getContentType().getValue().trim();
+ if (!ctype.equals(ectype))
+ fail("Got wrong content type: "+ctype);
+
+ // do something useful with the response body and ensure it is fully consumed
+ if (ctype.equals(FeedServlet.FEEDLIST_CONTENT_TYPE)) {
+ try {
+ returnedlist = new JSONArray(new JSONTokener(entity.getContent()));
+ } catch (Exception e) {
+ fail("Bad JSON: "+e.getMessage());
+ }
+ } else {
+ EntityUtils.consume(entity);
+ }
+ } catch (IOException e) {
+ fail(e.getMessage());
+ } finally {
+ httpGet.releaseConnection();
+ }
+ }
+}
diff --git a/datarouter-prov/src/test/java/datarouter/provisioning/testDRFeedsPost.java b/datarouter-prov/src/test/java/datarouter/provisioning/testDRFeedsPost.java
new file mode 100644
index 00000000..65d041d1
--- /dev/null
+++ b/datarouter-prov/src/test/java/datarouter/provisioning/testDRFeedsPost.java
@@ -0,0 +1,282 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package datarouter.provisioning;
+
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.entity.ContentType;
+import org.apache.http.util.EntityUtils;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.att.research.datarouter.provisioning.FeedServlet;
+
+public class testDRFeedsPost extends testBase {
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ }
+
+ @Test
+ public void testNormal() {
+ JSONObject jo = buildFeedRequest();
+ testCommon(jo, HttpServletResponse.SC_CREATED);
+ }
+ @Test
+ public void testNormalNoCTVersion() {
+ JSONObject jo = buildFeedRequest();
+ testCommon(jo, HttpServletResponse.SC_CREATED, "application/vnd.att-dr.feed", "JUnit");
+ }
+ @Test
+ public void testBadContentType() {
+ JSONObject jo = buildFeedRequest();
+ testCommon(jo, HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE, "bad/bad", "Junit");
+ }
+ @Test
+ public void testNoBehalfHeader() {
+ JSONObject jo = buildFeedRequest();
+ testCommon(jo, HttpServletResponse.SC_BAD_REQUEST, FeedServlet.FEED_CONTENT_TYPE, null);
+ }
+ @Test
+ public void testMissingName() {
+ JSONObject jo = buildFeedRequest();
+ jo.remove("name");
+ testCommon(jo, 400);
+ }
+ @Test
+ public void testTooLongName() {
+ JSONObject jo = buildFeedRequest();
+ jo.put("name", "123456789012345678901234567890");
+ testCommon(jo, 400);
+ }
+ @Test
+ public void testMissingVersion() {
+ JSONObject jo = buildFeedRequest();
+ jo.remove("version");
+ testCommon(jo, 400);
+ }
+ @Test
+ public void testTooLongVersion() {
+ JSONObject jo = buildFeedRequest();
+ jo.put("version", "123456789012345678901234567890");
+ testCommon(jo, 400);
+ }
+ @Test
+ public void testTooLongDescription() {
+ // normal request
+ JSONObject jo = buildFeedRequest();
+ jo.put("description", s_257);
+ testCommon(jo, 400);
+ }
+ @Test
+ public void testMissingAuthorization() {
+ JSONObject jo = buildFeedRequest();
+ jo.remove("authorization");
+ testCommon(jo, 400);
+ }
+ @Test
+ public void testMissingClassification() {
+ JSONObject jo = buildFeedRequest();
+ JSONObject j2 = jo.getJSONObject("authorization");
+ j2.remove("classification");
+ testCommon(jo, 400);
+ }
+ @Test
+ public void testTooLongClassification() {
+ JSONObject jo = buildFeedRequest();
+ JSONObject j2 = jo.getJSONObject("authorization");
+ j2.put("classification", s_33);
+ testCommon(jo, 400);
+ }
+ @Test
+ public void testNoEndpointIds() {
+ JSONObject jo = buildFeedRequest();
+ JSONObject j2 = jo.getJSONObject("authorization");
+ j2.put("endpoint_ids", new JSONArray());
+ testCommon(jo, 400);
+ }
+ @Test
+ public void testBadIPAddress1() {
+ JSONObject jo = buildFeedRequest();
+ JSONObject j2 = jo.getJSONObject("authorization");
+ JSONArray ja = j2.getJSONArray("endpoint_addrs");
+ ja.put("ZZZ^&#$%@#&^%$@#&^");
+ testCommon(jo, 400);
+ }
+ @Test
+ public void testBadIPAddress2() {
+ JSONObject jo = buildFeedRequest();
+ JSONObject j2 = jo.getJSONObject("authorization");
+ JSONArray ja = j2.getJSONArray("endpoint_addrs");
+ ja.put("135.207.136.678"); // bad IPv4 addr
+ testCommon(jo, 400);
+ }
+ @Test
+ public void testBadIPAddress3() {
+ JSONObject jo = buildFeedRequest();
+ JSONObject j2 = jo.getJSONObject("authorization");
+ JSONArray ja = j2.getJSONArray("endpoint_addrs");
+ ja.put("2001:1890:1110:d000:1a29::17567"); // bad IPv6 addr
+ testCommon(jo, 400);
+ }
+ @Test
+ public void testBadNetMask() {
+ JSONObject jo = buildFeedRequest();
+ JSONObject j2 = jo.getJSONObject("authorization");
+ JSONArray ja = j2.getJSONArray("endpoint_addrs");
+ ja.put("10.10.10.10/64");
+ testCommon(jo, 400);
+ }
+ @Test
+ public void testGoodIPAddress1() {
+ JSONObject jo = buildFeedRequest();
+ JSONObject j2 = jo.getJSONObject("authorization");
+ JSONArray ja = j2.getJSONArray("endpoint_addrs");
+ ja.put("135.207.136.175"); // good IPv4 addr
+ testCommon(jo, 201);
+ }
+ @Test
+ public void testGoodIPAddress2() {
+ JSONObject jo = buildFeedRequest();
+ JSONObject j2 = jo.getJSONObject("authorization");
+ JSONArray ja = j2.getJSONArray("endpoint_addrs");
+ ja.put("2001:1890:1110:d000:1a29::175"); // good IPv6 addr
+ testCommon(jo, 201);
+ }
+ @Test
+ public void testGoodNetMask() {
+ JSONObject jo = buildFeedRequest();
+ JSONObject j2 = jo.getJSONObject("authorization");
+ JSONArray ja = j2.getJSONArray("endpoint_addrs");
+ ja.put("2001:1890:1110:d000:1a29::175/120");
+ testCommon(jo, 201);
+ }
+ private void testCommon(JSONObject jo, int expect) {
+ testCommon(jo, expect, FeedServlet.FEED_CONTENT_TYPE, "JUnit");
+ }
+ private void testCommon(JSONObject jo, int expect, String ctype, String bhdr) {
+ String url = props.getProperty("test.host") + "/";
+ HttpPost httpPost = new HttpPost(url);
+ try {
+ if (bhdr != null)
+ httpPost.addHeader(FeedServlet.BEHALF_HEADER, bhdr);
+ String t = jo.toString();
+ HttpEntity body = new ByteArrayEntity(t.getBytes(), ContentType.create(ctype));
+ httpPost.setEntity(body);
+
+ HttpResponse response = httpclient.execute(httpPost);
+ ckResponse(response, expect);
+
+ HttpEntity entity = response.getEntity();
+ ctype = entity.getContentType().getValue().trim();
+ int code = response.getStatusLine().getStatusCode();
+ if (code == HttpServletResponse.SC_CREATED && !ctype.equals(FeedServlet.FEEDFULL_CONTENT_TYPE))
+ fail("Got wrong content type: "+ctype);
+
+ if (code == HttpServletResponse.SC_CREATED) {
+ Header[] loc = response.getHeaders("Location");
+ if (loc == null)
+ fail("Missing Location header.");
+ }
+
+ // do something useful with the response body and ensure it is fully consumed
+ if (ctype.equals(FeedServlet.FEEDFULL_CONTENT_TYPE)) {
+ // ck Location header!
+ JSONObject jo2 = null;
+ try {
+ jo2 = new JSONObject(new JSONTokener(entity.getContent()));
+ System.err.println(jo2.toString());
+ } catch (Exception e) {
+ fail("Bad JSON: "+e.getMessage());
+ }
+ try {
+ jo2.getString("publisher");
+ JSONObject jo3 = jo2.getJSONObject("links");
+ jo3.getString("self");
+ jo3.getString("publish");
+ jo3.getString("subscribe");
+ jo3.getString("log");
+ } catch (JSONException e) {
+ fail("required field missing from result: "+e.getMessage());
+ }
+ } else {
+ EntityUtils.consume(entity);
+ }
+ } catch (IOException e) {
+ fail(e.getMessage());
+ } finally {
+ httpPost.releaseConnection();
+ }
+ }
+ private JSONObject buildFeedRequest() {
+ JSONObject jo = new JSONObject();
+ jo.put("name", "JunitFeed");
+ jo.put("version", ""+System.currentTimeMillis()); // make version unique
+ jo.put("description", "Sample feed used by JUnit to test");
+
+ JSONObject jo2 = new JSONObject();
+ jo2.put("classification", "unrestricted");
+
+ JSONArray ja = new JSONArray();
+ JSONObject jo3 = new JSONObject();
+ jo3.put("id", "id001");
+ jo3.put("password", "re1kwelj");
+ JSONObject jo4 = new JSONObject();
+ jo4.put("id", "id002");
+ jo4.put("password", "o9eqlmbd");
+ ja.put(jo3);
+ ja.put(jo4);
+ jo2.put("endpoint_ids", ja);
+
+ ja = new JSONArray();
+ ja.put("10.0.0.1");
+ ja.put("192.168.0.1");
+ ja.put("135.207.136.128/25");
+ jo2.put("endpoint_addrs", ja);
+
+ jo.put("authorization", jo2);
+ return jo;
+ }
+}
+/*
+curl -v -X POST -H 'X-ATT-DR-ON-BEHALF-OF: tester' -H 'Content-type: application/vnd.att-dr.feed' --user publisher:tomcat \
+ --data "$data" http://127.0.0.1:8080/prov/feed/
+*/
diff --git a/datarouter-prov/src/test/java/datarouter/provisioning/testDRFeedsPut.java b/datarouter-prov/src/test/java/datarouter/provisioning/testDRFeedsPut.java
new file mode 100644
index 00000000..aee58b26
--- /dev/null
+++ b/datarouter-prov/src/test/java/datarouter/provisioning/testDRFeedsPut.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package datarouter.provisioning;
+
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.util.EntityUtils;
+import org.junit.Test;
+
+import com.att.research.datarouter.provisioning.FeedServlet;
+
+public class testDRFeedsPut extends testBase {
+ @Test
+ public void testNotAllowed() {
+ String url = props.getProperty("test.host") + "/";
+ HttpPut put = new HttpPut(url);
+ try {
+ put.addHeader(FeedServlet.BEHALF_HEADER, "JUnit");
+
+ HttpResponse response = httpclient.execute(put);
+ ckResponse(response, HttpServletResponse.SC_METHOD_NOT_ALLOWED);
+
+ HttpEntity entity = response.getEntity();
+ EntityUtils.consume(entity);
+ } catch (IOException e) {
+ fail(e.getMessage());
+ } finally {
+ put.releaseConnection();
+ }
+ }
+}
diff --git a/datarouter-prov/src/test/java/datarouter/provisioning/testFeedDelete.java b/datarouter-prov/src/test/java/datarouter/provisioning/testFeedDelete.java
new file mode 100644
index 00000000..6e2b7181
--- /dev/null
+++ b/datarouter-prov/src/test/java/datarouter/provisioning/testFeedDelete.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package datarouter.provisioning;
+
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.util.EntityUtils;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.att.research.datarouter.provisioning.FeedServlet;
+
+public class testFeedDelete extends testBase {
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ getDBstate();
+ }
+
+ @Test
+ public void testDeleteNormal() {
+ // Delete the first non-deleted feed in the DB
+ JSONArray ja = db_state.getJSONArray("feeds");
+ for (int i = ja.length()-1; i >= 0; i--) {
+ JSONObject feed = ja.getJSONObject(i);
+ if (!feed.getBoolean("deleted")) {
+ int feedid = feed.getInt("feedid");
+ testCommon(HttpServletResponse.SC_NO_CONTENT, "/feed/"+feedid);
+ return;
+ }
+ }
+ }
+ @Test
+ public void testDeleteNoFeedID() {
+ testCommon(HttpServletResponse.SC_BAD_REQUEST, "/feed/");
+ }
+ @Test
+ public void testDeleteNoFeed() {
+ testCommon(HttpServletResponse.SC_NOT_FOUND, "/feed/999999");
+ }
+ private void testCommon(int expect, String uri) {
+ String url = props.getProperty("test.host") + uri;
+ HttpDelete del = new HttpDelete(url);
+ try {
+ del.addHeader(FeedServlet.BEHALF_HEADER, "JUnit");
+
+ HttpResponse response = httpclient.execute(del);
+ ckResponse(response, expect);
+
+ HttpEntity entity = response.getEntity();
+ EntityUtils.consume(entity);
+ } catch (IOException e) {
+ fail(e.getMessage());
+ } finally {
+ del.releaseConnection();
+ }
+ }
+}
diff --git a/datarouter-prov/src/test/java/datarouter/provisioning/testFeedPut.java b/datarouter-prov/src/test/java/datarouter/provisioning/testFeedPut.java
new file mode 100644
index 00000000..8393a98a
--- /dev/null
+++ b/datarouter-prov/src/test/java/datarouter/provisioning/testFeedPut.java
@@ -0,0 +1,202 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package datarouter.provisioning;
+
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.entity.ContentType;
+import org.apache.http.util.EntityUtils;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.att.research.datarouter.provisioning.FeedServlet;
+
+public class testFeedPut extends testBase {
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ getDBstate();
+ }
+
+ @Test
+ public void testPutNoFeedID() {
+ JSONObject jo = buildFeedRequest();
+ testCommon(jo, HttpServletResponse.SC_BAD_REQUEST, "/feed/");
+ }
+ @Test
+ public void testPutNoFeed() {
+ JSONObject jo = buildFeedRequest();
+ testCommon(jo, HttpServletResponse.SC_NOT_FOUND, "/feed/999999");
+ }
+ @Test
+ public void testBadContentType() {
+ JSONObject jo = buildFeedRequest();
+ testCommon(jo, HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE, "bad/bad", "JUnit");
+ }
+ @Test
+ public void testChangeName() {
+ JSONObject jo = buildFeedRequest();
+ jo.put("name", "badname");
+ testCommon(jo, HttpServletResponse.SC_BAD_REQUEST, FeedServlet.FEED_CONTENT_TYPE, "JUnit");
+ }
+ @Test
+ public void testChangeVersion() {
+ JSONObject jo = buildFeedRequest();
+ jo.put("version", "badvers");
+ testCommon(jo, HttpServletResponse.SC_BAD_REQUEST, FeedServlet.FEED_CONTENT_TYPE, "JUnit");
+ }
+ @Test
+ public void testBadPublisher() {
+ JSONObject jo = buildFeedRequest();
+ testCommon(jo, HttpServletResponse.SC_BAD_REQUEST, FeedServlet.FEED_CONTENT_TYPE, "BadBadBad");
+ }
+ @Test
+ public void testChangeDescription() {
+ JSONObject jo = buildFeedRequest();
+ // change descr
+ jo.put("description", "This description HAS BEEN CHANGED!!!");
+ testCommon(jo, HttpServletResponse.SC_OK, FeedServlet.FEED_CONTENT_TYPE, "JUnit");
+ }
+
+ private void testCommon(JSONObject jo, int expect, String uri) {
+ testCommon(jo, expect, FeedServlet.FEED_CONTENT_TYPE, "Junit", uri);
+ }
+ private void testCommon(JSONObject jo, int expect, String ctype, String bhdr) {
+ JSONArray ja = db_state.getJSONArray("feeds");
+ for (int i = 0; i < ja.length(); i++) {
+ JSONObject feed0 = ja.getJSONObject(i);
+ if (!feed0.getBoolean("deleted") && feed0.getString("publisher").equals(bhdr)) {
+ int feedid = feed0.getInt("feedid");
+ testCommon(jo, expect, ctype, bhdr, "/feed/"+feedid);
+ return;
+ }
+ }
+ }
+ private void testCommon(JSONObject jo, int expect, String ctype, String bhdr, String uri) {
+ String url = props.getProperty("test.host") + uri;
+ HttpPut put = new HttpPut(url);
+ try {
+ if (bhdr != null)
+ put.addHeader(FeedServlet.BEHALF_HEADER, bhdr);
+ String t = jo.toString();
+ HttpEntity body = new ByteArrayEntity(t.getBytes(), ContentType.create(ctype));
+ put.setEntity(body);
+
+ HttpResponse response = httpclient.execute(put);
+ ckResponse(response, expect);
+
+ HttpEntity entity = response.getEntity();
+ ctype = entity.getContentType().getValue().trim();
+ int code = response.getStatusLine().getStatusCode();
+ if (code == HttpServletResponse.SC_CREATED && !ctype.equals(FeedServlet.FEEDFULL_CONTENT_TYPE))
+ fail("Got wrong content type: "+ctype);
+
+ if (code == HttpServletResponse.SC_CREATED) {
+ Header[] loc = response.getHeaders("Location");
+ if (loc == null)
+ fail("Missing Location header.");
+ }
+
+ // do something useful with the response body and ensure it is fully consumed
+ if (ctype.equals(FeedServlet.FEEDFULL_CONTENT_TYPE)) {
+ // ck Location header!
+ JSONObject jo2 = null;
+ try {
+ jo2 = new JSONObject(new JSONTokener(entity.getContent()));
+ System.err.println(jo2.toString());
+ } catch (Exception e) {
+ fail("Bad JSON: "+e.getMessage());
+ }
+ try {
+ jo2.getString("publisher");
+ JSONObject jo3 = jo2.getJSONObject("links");
+ jo3.getString("self");
+ jo3.getString("publish");
+ jo3.getString("subscribe");
+ jo3.getString("log");
+ } catch (JSONException e) {
+ fail("required field missing from result: "+e.getMessage());
+ }
+ } else {
+ EntityUtils.consume(entity);
+ }
+ } catch (IOException e) {
+ fail(e.getMessage());
+ } finally {
+ put.releaseConnection();
+ }
+ }
+ private JSONObject buildFeedRequest() {
+ JSONObject jo = new JSONObject();
+ jo.put("name", "feed");
+ jo.put("version", "1.0.0");
+ jo.put("description", "Sample feed used by JUnit to test");
+
+ JSONObject jo2 = new JSONObject();
+ jo2.put("classification", "unrestricted");
+
+ JSONArray ja = new JSONArray();
+ JSONObject jo3 = new JSONObject();
+ jo3.put("id", "id001");
+ jo3.put("password", "re1kwelj");
+ JSONObject jo4 = new JSONObject();
+ jo4.put("id", "id002");
+ jo4.put("password", "o9eqlmbd");
+ ja.put(jo3);
+ ja.put(jo4);
+ jo2.put("endpoint_ids", ja);
+
+ ja = new JSONArray();
+ ja.put("20.0.0.1");
+ ja.put("195.68.12.15");
+ ja.put("135.207.136.128/25");
+ jo2.put("endpoint_addrs", ja);
+
+ jo.put("authorization", jo2);
+ return jo;
+ }
+}
diff --git a/datarouter-prov/src/test/java/datarouter/provisioning/testInternalGet.java b/datarouter-prov/src/test/java/datarouter/provisioning/testInternalGet.java
new file mode 100644
index 00000000..877975ac
--- /dev/null
+++ b/datarouter-prov/src/test/java/datarouter/provisioning/testInternalGet.java
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package datarouter.provisioning;
+
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.util.EntityUtils;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.att.research.datarouter.provisioning.FeedServlet;
+import com.att.research.datarouter.provisioning.beans.Parameters;
+
+public class testInternalGet extends testBase {
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ }
+
+ @Test
+ public void testNormal() {
+ String url = props.getProperty("test.host") + "/internal/prov";
+ HttpGet httpPost = new HttpGet(url);
+ try {
+ httpPost.addHeader(FeedServlet.BEHALF_HEADER, "JUnit");
+
+ HttpResponse response = httpclient.execute(httpPost);
+ int code = response.getStatusLine().getStatusCode();
+ if (code != 200)
+ fail("Unexpected response, expect "+200+" got "+code);
+
+ HttpEntity entity = response.getEntity();
+ String ctype = entity.getContentType().getValue().trim();
+ boolean ok = ctype.equals(FeedServlet.PROVFULL_CONTENT_TYPE1);
+ ok |= ctype.equals(FeedServlet.PROVFULL_CONTENT_TYPE2);
+ if (!ok)
+ fail("Got wrong content type: "+ctype);
+
+ // do something useful with the response body and ensure it is fully consumed
+ if (ok) {
+ JSONObject jo = null;
+ try {
+ jo = new JSONObject(new JSONTokener(entity.getContent()));
+ } catch (Exception e) {
+ fail("Bad JSON: "+e.getMessage());
+ }
+ try {
+ jo.getJSONArray("feeds");
+ jo.getJSONArray("subscriptions");
+ JSONObject jo2 = jo.getJSONObject("parameters");
+ jo2.getJSONArray(Parameters.NODES);
+ jo2.getString(Parameters.ACTIVE_POD);
+ jo2.getString(Parameters.STANDBY_POD);
+ jo2.getInt(Parameters.LOGROLL_INTERVAL);
+ jo2.getInt(Parameters.DELIVERY_INIT_RETRY_INTERVAL);
+ jo2.getInt(Parameters.DELIVERY_MAX_RETRY_INTERVAL);
+ jo2.getInt(Parameters.DELIVERY_RETRY_RATIO);
+ jo2.getInt(Parameters.DELIVERY_MAX_AGE);
+ } catch (JSONException e) {
+ fail("required field missing from result: "+e.getMessage());
+ }
+ } else {
+ EntityUtils.consume(entity);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ } finally {
+ httpPost.releaseConnection();
+ }
+ }
+}
diff --git a/datarouter-prov/src/test/java/datarouter/provisioning/testInternalMisc.java b/datarouter-prov/src/test/java/datarouter/provisioning/testInternalMisc.java
new file mode 100644
index 00000000..f8a1da10
--- /dev/null
+++ b/datarouter-prov/src/test/java/datarouter/provisioning/testInternalMisc.java
@@ -0,0 +1,151 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package datarouter.provisioning;
+
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.util.EntityUtils;
+import org.json.JSONArray;
+import org.json.JSONTokener;
+import org.junit.Test;
+
+import com.att.research.datarouter.provisioning.FeedServlet;
+
+public class testInternalMisc extends testBase {
+ @Test
+ public void testInternalDrlogs() {
+ String url = props.getProperty("test.host") + "/internal/drlogs";
+ HttpGet httpPost = new HttpGet(url);
+ try {
+ httpPost.addHeader(FeedServlet.BEHALF_HEADER, "JUnit");
+
+ HttpResponse response = httpclient.execute(httpPost);
+ int code = response.getStatusLine().getStatusCode();
+ if (code != 200)
+ fail("Unexpected response, expect "+HttpServletResponse.SC_NOT_FOUND+" got "+code);
+
+ HttpEntity entity = response.getEntity();
+ String ctype = entity.getContentType().getValue().trim();
+ boolean ok = ctype.equals("text/plain");
+ if (!ok)
+ fail("Got wrong content type: "+ctype);
+
+ EntityUtils.consume(entity);
+ } catch (IOException e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ } finally {
+ httpPost.releaseConnection();
+ }
+ }
+
+ @Test
+ public void testInternalHalt() {
+ String url = props.getProperty("test.host") + "/internal/halt";
+ HttpGet httpPost = new HttpGet(url);
+ try {
+ httpPost.addHeader(FeedServlet.BEHALF_HEADER, "JUnit");
+
+ HttpResponse response = httpclient.execute(httpPost);
+ int code = response.getStatusLine().getStatusCode();
+ if (code != HttpServletResponse.SC_NOT_FOUND)
+ fail("Unexpected response, expect "+HttpServletResponse.SC_NOT_FOUND+" got "+code);
+
+ HttpEntity entity = response.getEntity();
+ EntityUtils.consume(entity);
+ } catch (IOException e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ } finally {
+ httpPost.releaseConnection();
+ }
+ }
+
+ @SuppressWarnings("unused")
+ @Test
+ public void testInternalLogs() {
+ String url = props.getProperty("test.host") + "/internal/logs";
+ HttpGet httpPost = new HttpGet(url);
+ try {
+ httpPost.addHeader(FeedServlet.BEHALF_HEADER, "JUnit");
+
+ HttpResponse response = httpclient.execute(httpPost);
+ int code = response.getStatusLine().getStatusCode();
+ if (code != 200)
+ fail("Unexpected response, expect "+200+" got "+code);
+
+ HttpEntity entity = response.getEntity();
+ String ctype = entity.getContentType().getValue().trim();
+ boolean ok = ctype.equals("application/json");
+ if (!ok)
+ fail("Got wrong content type: "+ctype);
+
+ // do something useful with the response body and ensure it is fully consumed
+ if (ok) {
+ try {
+ new JSONArray(new JSONTokener(entity.getContent()));
+ } catch (Exception e) {
+ fail("Bad JSON: "+e.getMessage());
+ }
+ } else {
+ EntityUtils.consume(entity);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ } finally {
+ httpPost.releaseConnection();
+ }
+ }
+
+ @Test
+ public void testInternalBadURL() {
+ String url = props.getProperty("test.host") + "/internal/badurl";
+ HttpGet httpPost = new HttpGet(url);
+ try {
+ httpPost.addHeader(FeedServlet.BEHALF_HEADER, "JUnit");
+
+ HttpResponse response = httpclient.execute(httpPost);
+ int code = response.getStatusLine().getStatusCode();
+ if (code != HttpServletResponse.SC_NOT_FOUND)
+ fail("Unexpected response, expect "+HttpServletResponse.SC_NOT_FOUND+" got "+code);
+
+ HttpEntity entity = response.getEntity();
+ EntityUtils.consume(entity);
+ } catch (IOException e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ } finally {
+ httpPost.releaseConnection();
+ }
+ }
+
+}
diff --git a/datarouter-prov/src/test/java/datarouter/provisioning/testLogGet.java b/datarouter-prov/src/test/java/datarouter/provisioning/testLogGet.java
new file mode 100644
index 00000000..510150a2
--- /dev/null
+++ b/datarouter-prov/src/test/java/datarouter/provisioning/testLogGet.java
@@ -0,0 +1,181 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+package datarouter.provisioning;
+
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.util.EntityUtils;
+import org.json.JSONArray;
+import org.json.JSONTokener;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.att.research.datarouter.provisioning.FeedServlet;
+
+public class testLogGet extends testBase {
+ private JSONArray returnedlist;
+ private int feedid = 4;
+ private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ // need to seed the DB here
+ }
+
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ // need to "unseed" the DB
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ getDBstate();
+// JSONArray ja = db_state.getJSONArray("feeds");
+// for (int i = 0; i < ja.length(); i++) {
+// JSONObject jo = ja.getJSONObject(i);
+// if (!jo.getBoolean("deleted"))
+// feedid = jo.getInt("feedid");
+// }
+ }
+
+ @Test
+ public void testNormal() {
+ testCommon(HttpServletResponse.SC_OK);
+ }
+ @Test
+ public void testNormalPubOnly() {
+ testCommon(HttpServletResponse.SC_OK, "?type=pub");
+ }
+ @Test
+ public void testNormalDelOnly() {
+ testCommon(HttpServletResponse.SC_OK, "?type=del");
+ }
+ @Test
+ public void testNormalExpOnly() {
+ testCommon(HttpServletResponse.SC_OK, "?type=exp");
+ }
+ @Test
+ public void testNormalXXXOnly() {
+ testCommon(HttpServletResponse.SC_BAD_REQUEST, "?type=xxx");
+ }
+ @Test
+ public void testNormalStatusSuccess() {
+ testCommon(HttpServletResponse.SC_OK, "?statusCode=success");
+ }
+ @Test
+ public void testNormalStatusRedirect() {
+ testCommon(HttpServletResponse.SC_OK, "?statusCode=redirect");
+ }
+ @Test
+ public void testNormalStatusFailure() {
+ testCommon(HttpServletResponse.SC_OK, "?statusCode=failure");
+ }
+ @Test
+ public void testNormalStatus200() {
+ testCommon(HttpServletResponse.SC_OK, "?statusCode=200");
+ }
+ @Test
+ public void testNormalStatusXXX() {
+ testCommon(HttpServletResponse.SC_BAD_REQUEST, "?statusCode=xxx");
+ }
+ @Test
+ public void testNormalExpiryNotRetryable() {
+ testCommon(HttpServletResponse.SC_OK, "?expiryReason=notRetryable");
+ }
+ @Test
+ public void testNormalExpiryRetriesExhausted() {
+ testCommon(HttpServletResponse.SC_OK, "?expiryReason=retriesExhausted");
+ }
+ @Test
+ public void testNormalExpiryXXX() {
+ testCommon(HttpServletResponse.SC_BAD_REQUEST, "?expiryReason=xxx");
+ }
+ @Test
+ public void testNormalPublishId() {
+ testCommon(HttpServletResponse.SC_OK, "?publishId=1366985877801.mtdvnj00-drtr.proto.research.att.com");
+ }
+ @Test
+ public void testNormalStart() {
+ long n = System.currentTimeMillis() - (5 * 24 * 60 * 60 * 1000L); // 5 days
+ testCommon(HttpServletResponse.SC_OK, String.format("?start=%s", sdf.format(n)));
+ }
+ @Test
+ public void testBadStart() {
+ testCommon(HttpServletResponse.SC_BAD_REQUEST, "?start=xxx");
+ }
+ @Test
+ public void testLongEnd() {
+ testCommon(HttpServletResponse.SC_OK, "?end=1364837896220");
+ }
+ @Test
+ public void testBadEnd() {
+ testCommon(HttpServletResponse.SC_BAD_REQUEST, "?end=2013-04-25T11:01:25Q");
+ }
+ private void testCommon(int expect) {
+ testCommon(expect, "");
+ }
+ private void testCommon(int expect, String query) {
+ String url = props.getProperty("test.host") + "/feedlog/" + feedid + query;
+ HttpGet httpGet = new HttpGet(url);
+ try {
+ HttpResponse response = httpclient.execute(httpGet);
+ ckResponse(response, expect);
+
+ HttpEntity entity = response.getEntity();
+ String ctype = entity.getContentType().getValue().trim();
+ if (expect == HttpServletResponse.SC_OK) {
+ if (!ctype.equals(FeedServlet.LOGLIST_CONTENT_TYPE))
+ fail("Got wrong content type: "+ctype);
+ }
+
+ // do something useful with the response body and ensure it is fully consumed
+ if (ctype.equals(FeedServlet.LOGLIST_CONTENT_TYPE)) {
+ try {
+ returnedlist = new JSONArray(new JSONTokener(entity.getContent()));
+ int n = returnedlist.length();
+ if (n != 0)
+ System.err.println(n + " items");
+ } catch (Exception e) {
+ fail("Bad JSON: "+e.getMessage());
+ }
+ } else {
+ EntityUtils.consume(entity);
+ }
+ } catch (IOException e) {
+ fail(e.getMessage());
+ } finally {
+ httpGet.releaseConnection();
+ }
+ }
+}
diff --git a/datarouter-prov/src/test/java/datarouter/provisioning/testPublish.java b/datarouter-prov/src/test/java/datarouter/provisioning/testPublish.java
new file mode 100644
index 00000000..15d0565f
--- /dev/null
+++ b/datarouter-prov/src/test/java/datarouter/provisioning/testPublish.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package datarouter.provisioning;
+
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.Header;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.RedirectStrategy;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.impl.client.DefaultRedirectStrategy;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.att.research.datarouter.provisioning.FeedServlet;
+
+public class testPublish extends testBase {
+ private String publish_url;
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ getDBstate();
+ // Get publish URL from first feed
+ JSONArray ja = db_state.getJSONArray("feeds");
+ for (int i = ja.length()-1; i >= 0; i--) {
+ JSONObject feed = ja.getJSONObject(i);
+ if (!feed.getBoolean("deleted")) {
+ publish_url = feed.getJSONObject("links").getString("publish");
+ publish_url += "/" + System.currentTimeMillis();
+ return;
+ }
+ }
+ }
+
+ @Test
+ public void testDelete() {
+ HttpDelete x = new HttpDelete(publish_url);
+ testCommon(x);
+ }
+ @Test
+ public void testGet() {
+ HttpGet x = new HttpGet(publish_url);
+ testCommon(x);
+ }
+ @Test
+ public void testPut() {
+ HttpPut x = new HttpPut(publish_url);
+ testCommon(x);
+ }
+ @Test
+ public void testPost() {
+ HttpPost x = new HttpPost(publish_url);
+ testCommon(x);
+ }
+ private void testCommon(HttpRequestBase rb) {
+ try {
+ rb.addHeader(FeedServlet.BEHALF_HEADER, "JUnit");
+ RedirectStrategy strategy = new DefaultRedirectStrategy() {
+ protected boolean isRedirectable(String method) {
+ return false;
+ }
+ };
+ httpclient.setRedirectStrategy(strategy);
+ HttpResponse response = httpclient.execute(rb);
+ ckResponse(response, HttpServletResponse.SC_MOVED_PERMANENTLY);
+
+ // Make sure there is a Location hdr
+ Header[] loc = response.getHeaders("Location");
+ if (loc == null || loc.length == 0)
+ fail("No location header");
+ } catch (IOException e) {
+ fail(e.getMessage());
+ } finally {
+ rb.releaseConnection();
+ }
+ }
+}
diff --git a/datarouter-prov/src/test/java/datarouter/provisioning/testRLEBitSet.java b/datarouter-prov/src/test/java/datarouter/provisioning/testRLEBitSet.java
new file mode 100644
index 00000000..40970edf
--- /dev/null
+++ b/datarouter-prov/src/test/java/datarouter/provisioning/testRLEBitSet.java
@@ -0,0 +1,231 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package datarouter.provisioning;
+
+import static org.junit.Assert.fail;
+
+import java.util.Iterator;
+
+import org.junit.Test;
+
+import com.att.research.datarouter.provisioning.utils.RLEBitSet;
+
+public class testRLEBitSet {
+ @Test
+ public void testBasicConstructor() {
+ RLEBitSet bs = new RLEBitSet();
+ if (!bs.isEmpty())
+ fail("bit set not empty");
+ }
+ @Test
+ public void testStringConstructor() {
+ RLEBitSet bs = new RLEBitSet("1-10");
+ if (bs.isEmpty())
+ fail("bit set is empty");
+ if (!bs.toString().equals("1-10"))
+ fail("bad value");
+ bs = new RLEBitSet("69,70,71");
+ if (bs.isEmpty())
+ fail("bit set is empty");
+ if (!bs.toString().equals("69-71"))
+ fail("bad value");
+ bs = new RLEBitSet("555 444 443 442");
+ if (!bs.toString().equals("442-444,555"))
+ fail("bad value");
+ }
+ @Test
+ public void testLength() {
+ RLEBitSet bs = new RLEBitSet();
+ if (bs.length() != 0)
+ fail("testLength fail "+bs + " " + bs.length());
+ bs = new RLEBitSet("1-10");
+ if (bs.length() != 11)
+ fail("testLength fail "+bs + " " + bs.length());
+ bs = new RLEBitSet("1-20,100000000-100000005");
+ if (bs.length() != 100000006)
+ fail("testLength fail "+bs + " " + bs.length());
+ }
+ @Test
+ public void testGet() {
+ RLEBitSet bs = new RLEBitSet("1-10");
+ if (!bs.get(5))
+ fail("get");
+ if (bs.get(69))
+ fail("get");
+ }
+ @Test
+ public void testSetOneBit() {
+ RLEBitSet bs = new RLEBitSet();
+ for (int i = 12; i < 200; i++)
+ bs.set(i);
+ bs.set(690);
+ for (int i = 305; i < 309; i++)
+ bs.set(i);
+ bs.set(304);
+ if (!bs.toString().equals("12-199,304-308,690"))
+ fail("testSetOneBit fail "+bs);
+ }
+ @Test
+ public void testSetString() {
+ RLEBitSet bs = new RLEBitSet();
+ bs.set("1-100");
+ if (!bs.toString().equals("1-100"))
+ fail("testSetString fail "+bs);
+ }
+ @Test
+ public void testSetRange() {
+ RLEBitSet bs = new RLEBitSet();
+ bs.set(50,60);
+ if (!bs.toString().equals("50-59"))
+ fail("testSetRange fail "+bs);
+ }
+ @Test
+ public void testClearOneBit() {
+ RLEBitSet bs = new RLEBitSet("1-10");
+ bs.clear(5);
+ if (!bs.toString().equals("1-4,6-10"))
+ fail("testClearOneBit fail");
+ bs = new RLEBitSet("1-10");
+ bs.clear(11);
+ if (!bs.toString().equals("1-10"))
+ fail("testClearOneBit fail "+bs);
+ }
+ @Test
+ public void testClearRangeLeft() {
+ RLEBitSet bs = new RLEBitSet("100-200");
+ bs.clear(40,50);
+ if (!bs.toString().equals("100-200"))
+ fail("testClearRangeLeft fail "+bs);
+ }
+ @Test
+ public void testClearRangeRight() {
+ RLEBitSet bs = new RLEBitSet("100-200");
+ bs.clear(400,500);
+ if (!bs.toString().equals("100-200"))
+ fail("testClearRangeRight fail "+bs);
+ }
+ @Test
+ public void testClearRangeMiddle() {
+ RLEBitSet bs = new RLEBitSet("100-200");
+ bs.clear(120,130);
+ if (!bs.toString().equals("100-119,130-200"))
+ fail("testClearRangeRight fail "+bs);
+ }
+ @Test
+ public void testClearRangeIntersect() {
+ RLEBitSet bs = new RLEBitSet("100-200");
+ bs.clear(100,200);
+ if (!bs.toString().equals("200"))
+ fail("testClearRangeIntersect fail "+bs);
+ }
+ @Test
+ public void testClearOverlapLeft() {
+ RLEBitSet bs = new RLEBitSet("100-200");
+ bs.clear(50,150);
+ if (!bs.toString().equals("150-200"))
+ fail("testClearOverlapLeft fail "+bs);
+ }
+ @Test
+ public void testClearOverlapRight() {
+ RLEBitSet bs = new RLEBitSet("100-200");
+ bs.clear(150,250);
+ if (!bs.toString().equals("100-149"))
+ fail("testClearOverlapRight fail "+bs);
+ }
+ @Test
+ public void testClearOverlapAll() {
+ RLEBitSet bs = new RLEBitSet("100-200");
+ bs.clear(50,250);
+ if (!bs.toString().equals(""))
+ fail("testClearOverlapAll fail "+bs);
+ }
+ @Test
+ public void testAnd() {
+ RLEBitSet bs = new RLEBitSet("100-200");
+ RLEBitSet b2 = new RLEBitSet("150-400");
+ bs.and(b2);
+ if (!bs.toString().equals("150-200"))
+ fail("testAnd fail "+bs);
+ bs = new RLEBitSet("100-200");
+ b2 = new RLEBitSet("1500-4000");
+ bs.and(b2);
+ if (!bs.isEmpty())
+ fail("testAnd fail "+bs);
+ }
+ @Test
+ public void testAndNot() {
+ RLEBitSet bs = new RLEBitSet("100-200");
+ RLEBitSet b2 = new RLEBitSet("150-159");
+ bs.andNot(b2);
+ if (!bs.toString().equals("100-149,160-200"))
+ fail("testAndNot fail "+bs);
+ }
+ @Test
+ public void testIsEmpty() {
+ RLEBitSet bs = new RLEBitSet("");
+ if (!bs.isEmpty())
+ fail("testIsEmpty fail "+bs);
+ bs.set(1);
+ if (bs.isEmpty())
+ fail("testIsEmpty fail "+bs);
+ }
+ @Test
+ public void testCardinality() {
+ RLEBitSet bs = new RLEBitSet("1-120,10000000-10000005");
+ if (bs.cardinality() != 126)
+ fail("testCardinality fail 1");
+ }
+ @Test
+ public void testIterator() {
+ RLEBitSet bs = new RLEBitSet("1,5,10-12");
+ Iterator<Long[]> i = bs.getRangeIterator();
+ if (!i.hasNext())
+ fail("iterator fail 1");
+ Long[] ll = i.next();
+ if (ll == null || ll[0] != 1 || ll[1] != 1)
+ fail("iterator fail 2");
+
+ if (!i.hasNext())
+ fail("iterator fail 3");
+ ll = i.next();
+ if (ll == null || ll[0] != 5 || ll[1] != 5)
+ fail("iterator fail 4");
+
+ if (!i.hasNext())
+ fail("iterator fail 5");
+ ll = i.next();
+ if (ll == null || ll[0] != 10 || ll[1] != 12)
+ fail("iterator fail 6");
+
+ if (i.hasNext())
+ fail("iterator fail 7");
+ }
+ @Test
+ public void testClone() {
+ RLEBitSet bs1 = new RLEBitSet("1,5,10-12");
+ RLEBitSet bs2 = (RLEBitSet) bs1.clone();
+ if (!bs1.toString().equals(bs2.toString()))
+ fail("clone");
+ }
+}
diff --git a/datarouter-prov/src/test/java/datarouter/provisioning/testRouteAPI.java b/datarouter-prov/src/test/java/datarouter/provisioning/testRouteAPI.java
new file mode 100644
index 00000000..7295d00f
--- /dev/null
+++ b/datarouter-prov/src/test/java/datarouter/provisioning/testRouteAPI.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package datarouter.provisioning;
+
+import datarouter.provisioning.testBase;
+
+public class testRouteAPI extends testBase {
+
+}
diff --git a/datarouter-prov/src/test/java/datarouter/provisioning/testSubscribePost.java b/datarouter-prov/src/test/java/datarouter/provisioning/testSubscribePost.java
new file mode 100644
index 00000000..0f624ec5
--- /dev/null
+++ b/datarouter-prov/src/test/java/datarouter/provisioning/testSubscribePost.java
@@ -0,0 +1,180 @@
+/*******************************************************************************
+ * ============LICENSE_START==================================================
+ * * org.onap.dmaap
+ * * ===========================================================================
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * * ===========================================================================
+ * * 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.
+ * * ============LICENSE_END====================================================
+ * *
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * *
+ ******************************************************************************/
+
+package datarouter.provisioning;
+
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.entity.ContentType;
+import org.apache.http.util.EntityUtils;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.att.research.datarouter.provisioning.FeedServlet;
+import com.att.research.datarouter.provisioning.SubscribeServlet;
+
+public class testSubscribePost extends testBase {
+ private int feednum = 0;
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ getDBstate();
+ // use the first feed to subscribe to
+ JSONArray ja = db_state.getJSONArray("feeds");
+ for (int i = 0; i < ja.length(); i++) {
+ JSONObject feed0 = ja.getJSONObject(i);
+ if (feed0 != null && !feed0.getBoolean("deleted")) {
+ feednum = feed0.getInt("feedid");
+ return;
+ }
+ }
+ }
+
+ @Test
+ public void testNormal() {
+ JSONObject jo = buildSubRequest();
+ testCommon(jo, HttpServletResponse.SC_CREATED);
+ }
+ @Test
+ public void testMissingUrl() {
+ JSONObject jo = buildSubRequest();
+ jo.getJSONObject("delivery").remove("url");
+ testCommon(jo, HttpServletResponse.SC_BAD_REQUEST);
+ }
+ @Test
+ public void testTooLongUrl() {
+ JSONObject jo = buildSubRequest();
+ jo.getJSONObject("delivery").put("url", "https://"+s_257);
+ testCommon(jo, HttpServletResponse.SC_BAD_REQUEST);
+ }
+ @Test
+ public void testMissingUser() {
+ JSONObject jo = buildSubRequest();
+ jo.getJSONObject("delivery").remove("user");
+ testCommon(jo, HttpServletResponse.SC_BAD_REQUEST);
+ }
+ @Test
+ public void testTooLongUser() {
+ JSONObject jo = buildSubRequest();
+ jo.getJSONObject("delivery").put("user", s_33);
+ testCommon(jo, HttpServletResponse.SC_BAD_REQUEST);
+ }
+ @Test
+ public void testMissingPassword() {
+ JSONObject jo = buildSubRequest();
+ jo.getJSONObject("delivery").remove("password");
+ testCommon(jo, HttpServletResponse.SC_BAD_REQUEST);
+ }
+ @Test
+ public void testTooLongPassword() {
+ JSONObject jo = buildSubRequest();
+ jo.getJSONObject("delivery").put("password", s_33);
+ testCommon(jo, HttpServletResponse.SC_BAD_REQUEST);
+ }
+ @Test
+ public void testNonBooleanMetadata() {
+ JSONObject jo = buildSubRequest();
+ jo.put("metadataOnly", s_33);
+ testCommon(jo, HttpServletResponse.SC_BAD_REQUEST);
+ }
+ private void testCommon(JSONObject jo, int expect) {
+ String url = props.getProperty("test.host") + "/subscribe/" + feednum;
+ HttpPost httpPost = new HttpPost(url);
+ try {
+ httpPost.addHeader(SubscribeServlet.BEHALF_HEADER, "JUnit");
+ String t = jo.toString();
+ HttpEntity body = new ByteArrayEntity(t.getBytes(), ContentType.create(SubscribeServlet.SUB_CONTENT_TYPE));
+ httpPost.setEntity(body);
+
+ HttpResponse response = httpclient.execute(httpPost);
+ ckResponse(response, expect);
+
+ HttpEntity entity = response.getEntity();
+ String ctype = entity.getContentType().getValue();
+ int code = response.getStatusLine().getStatusCode();
+ if (code == HttpServletResponse.SC_CREATED && !ctype.equals(SubscribeServlet.SUBFULL_CONTENT_TYPE))
+ fail("Got wrong content type: "+ctype);
+
+ // do something useful with the response body and ensure it is fully consumed
+ if (ctype.equals(FeedServlet.SUBFULL_CONTENT_TYPE)) {
+ JSONObject jo2 = null;
+ try {
+ jo2 = new JSONObject(new JSONTokener(entity.getContent()));
+ } catch (Exception e) {
+ fail("Bad JSON: "+e.getMessage());
+ }
+ try {
+ jo2.getString("subscriber");
+ JSONObject jo3 = jo2.getJSONObject("links");
+ jo3.getString("self");
+ jo3.getString("feed");
+ jo3.getString("log");
+ } catch (JSONException e) {
+ fail("required field missing from result: "+e.getMessage());
+ }
+ } else {
+ EntityUtils.consume(entity);
+ }
+ } catch (IOException e) {
+ fail(e.getMessage());
+ } finally {
+ httpPost.releaseConnection();
+ }
+ }
+ private JSONObject buildSubRequest() {
+ JSONObject jo = new JSONObject();
+
+ JSONObject jo2 = new JSONObject();
+ jo2.put("url", "https://www.att.com/");
+ jo2.put("user", "dmr");
+ jo2.put("password", "passw0rd");
+ jo2.put("use100", true);
+
+ jo.put("delivery", jo2);
+ jo.put("metadataOnly", Boolean.FALSE);
+ return jo;
+ }
+}