OOZIE-3389 Getting input dependency list on the UI throws NPE (andras.piros via asalamon74, kmarton)
diff --git a/core/src/main/java/org/apache/oozie/command/coord/CoordActionMissingDependenciesXCommand.java b/core/src/main/java/org/apache/oozie/command/coord/CoordActionMissingDependenciesXCommand.java
index d37cfe5..f5d8782 100644
--- a/core/src/main/java/org/apache/oozie/command/coord/CoordActionMissingDependenciesXCommand.java
+++ b/core/src/main/java/org/apache/oozie/command/coord/CoordActionMissingDependenciesXCommand.java
@@ -107,19 +107,20 @@
@Override
protected List<Pair<CoordinatorActionBean, Map<String, ActionDependency>>> execute() throws CommandException {
- List<Pair<CoordinatorActionBean, Map<String, ActionDependency>>> inputDependenciesListPair =
+ final List<Pair<CoordinatorActionBean, Map<String, ActionDependency>>> inputDependenciesListPair =
new ArrayList<Pair<CoordinatorActionBean, Map<String, ActionDependency>>>();
try {
- for (CoordinatorActionBean coordAction : coordActions) {
- CoordInputDependency coordPullInputDependency = coordAction.getPullInputDependencies();
- CoordInputDependency coordPushInputDependency = coordAction.getPushInputDependencies();
- Map<String, ActionDependency> dependencyMap = new HashMap<String, ActionDependency>();
+ for (final CoordinatorActionBean coordAction : coordActions) {
+ final CoordInputDependency coordPullInputDependency = coordAction.getPullInputDependencies();
+ final CoordInputDependency coordPushInputDependency = coordAction.getPushInputDependencies();
+ final Map<String, ActionDependency> dependencyMap = new HashMap<>();
dependencyMap.putAll(coordPullInputDependency.getMissingDependencies(coordAction));
dependencyMap.putAll(coordPushInputDependency.getMissingDependencies(coordAction));
- inputDependenciesListPair.add(
- new Pair<CoordinatorActionBean, Map<String, ActionDependency>>(coordAction, dependencyMap));
+ if (!dependencyMap.isEmpty()) {
+ inputDependenciesListPair.add(new Pair<>(coordAction, dependencyMap));
+ }
}
}
catch (Exception e) {
diff --git a/core/src/main/java/org/apache/oozie/coord/input/dependency/CoordOldInputDependency.java b/core/src/main/java/org/apache/oozie/coord/input/dependency/CoordOldInputDependency.java
index 56aef1c..af298b9 100644
--- a/core/src/main/java/org/apache/oozie/coord/input/dependency/CoordOldInputDependency.java
+++ b/core/src/main/java/org/apache/oozie/coord/input/dependency/CoordOldInputDependency.java
@@ -370,6 +370,11 @@
Element eAction = XmlUtils.parseXml(coordAction.getActionXml());
Element inputList = eAction.getChild("input-events", eAction.getNamespace());
+
+ if (inputList == null || inputList.getChildren().isEmpty()) {
+ return dependenciesMap;
+ }
+
List<Element> eDataEvents = inputList.getChildren("data-in", eAction.getNamespace());
for (Element event : eDataEvents) {
Element uri = event.getChild("uris", event.getNamespace());
diff --git a/core/src/test/java/org/apache/oozie/coord/input/dependency/TestCoordOldInputDependency.java b/core/src/test/java/org/apache/oozie/coord/input/dependency/TestCoordOldInputDependency.java
new file mode 100644
index 0000000..c6bd3c0
--- /dev/null
+++ b/core/src/test/java/org/apache/oozie/coord/input/dependency/TestCoordOldInputDependency.java
@@ -0,0 +1,154 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.oozie.coord.input.dependency;
+
+import org.apache.oozie.CoordinatorActionBean;
+import org.apache.oozie.client.CoordinatorAction;
+import org.apache.oozie.command.CommandException;
+import org.apache.oozie.service.Services;
+import org.apache.oozie.test.XHCatTestCase;
+import org.jdom.JDOMException;
+
+import java.io.IOException;
+
+public class TestCoordOldInputDependency extends XHCatTestCase {
+ private Services services;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ services = super.setupServicesForHCatalog();
+ services.init();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ services.destroy();
+ super.tearDown();
+ }
+
+ public void testNoMissingInputDependencies() throws JDOMException, IOException, CommandException {
+ final CoordinatorActionBean actionWithoutInputDependencies = createActionWithoutInputDependencies();
+
+ assertEquals("there should be no missing dependencies",
+ 0,
+ actionWithoutInputDependencies
+ .getPullInputDependencies()
+ .getMissingDependencies(actionWithoutInputDependencies).size());
+ }
+
+ public void testOneMissingInputDependency() throws JDOMException, IOException, CommandException {
+ final CoordinatorActionBean actionWithInputDependencies = createActionWithInputDependencies();
+
+ assertEquals("there should be one missing dependency",
+ 1,
+ actionWithInputDependencies
+ .getPullInputDependencies()
+ .getMissingDependencies(actionWithInputDependencies).size());
+ }
+
+ private CoordinatorActionBean createActionWithoutInputDependencies() {
+ final CoordinatorActionBean coordinatorAction = createAction();
+
+ coordinatorAction.setActionXml("<coordinator-app xmlns=\"uri:oozie:coordinator:0.2\" name=\"cron-coord\"" +
+ " frequency=\"0/10 * * * *\" timezone=\"UTC\" freq_timeunit=\"CRON\" end_of_duration=\"NONE\"" +
+ " instance-number=\"1\" action-nominal-time=\"2010-01-01T00:00Z\" action-actual-time=\"2018-11-29T12:55Z\">\n" +
+ " <action>\n" +
+ " <workflow>\n" +
+ " <app-path>hdfs://localhost:9000/user/forsage/examples/apps/cron-schedule</app-path>\n" +
+ " <configuration>\n" +
+ " <property>\n" +
+ " <name>resourceManager</name>\n" +
+ " <value>localhost:8032</value>\n" +
+ " </property>\n" +
+ " <property>\n" +
+ " <name>nameNode</name>\n" +
+ " <value>hdfs://localhost:9000</value>\n" +
+ " </property>\n" +
+ " <property>\n" +
+ " <name>queueName</name>\n" +
+ " <value>default</value>\n" +
+ " </property>\n" +
+ " </configuration>\n" +
+ " </workflow>\n" +
+ " </action>\n" +
+ "</coordinator-app>");
+
+ return coordinatorAction;
+ }
+
+ private CoordinatorActionBean createAction() {
+ final CoordinatorActionBean coordinatorAction = new CoordinatorActionBean();
+
+ coordinatorAction.setId("0000001-181129135145907-oozie-fors-C@1");
+ coordinatorAction.setJobId("0000001-181129135145907-oozie-fors-C");
+ coordinatorAction.setStatus(CoordinatorAction.Status.SUCCEEDED);
+ coordinatorAction.setExternalId("0000002-181129135145907-oozie-fors-W");
+
+ return coordinatorAction;
+ }
+
+ private CoordinatorActionBean createActionWithInputDependencies() throws IOException {
+ final CoordinatorActionBean action = createAction();
+
+ action.setActionXml("<coordinator-app xmlns=\"uri:oozie:coordinator:0.2\" name=\"cron-coord\"" +
+ " frequency=\"0/10 * * * *\" timezone=\"UTC\" freq_timeunit=\"CRON\" end_of_duration=\"NONE\"" +
+ " instance-number=\"1\" action-nominal-time=\"2010-01-01T00:00Z\" action-actual-time=\"2018-11-29T12:55Z\">\n" +
+ " <datasets>\n" +
+ " <dataset name=\"data-1\" frequency=\"${coord:minutes(20)}\" initial-instance=\"2010-01-01T00:00Z\">\n" +
+ " <uri-template>${nameNode}/${YEAR}/${MONTH}/${DAY}/${HOUR}/${MINUTE}</uri-template>\n" +
+ " </dataset>\n" +
+ " </datasets>\n" +
+ " <input-events>\n" +
+ " <data-in name=\"input-1\" dataset=\"data-1\">\n" +
+ " <start-instance>${coord:current(-2)}</start-instance>\n" +
+ " <end-instance>${coord:current(0)}</end-instance>\n" +
+ " </data-in>\n" +
+ " </input-events>\n" +
+ " <action>\n" +
+ " <workflow>\n" +
+ " <app-path>hdfs://localhost:9000/user/forsage/examples/apps/cron-schedule</app-path>\n" +
+ " <configuration>\n" +
+ " <property>\n" +
+ " <name>resourceManager</name>\n" +
+ " <value>localhost:8032</value>\n" +
+ " </property>\n" +
+ " <property>\n" +
+ " <name>nameNode</name>\n" +
+ " <value>hdfs://localhost:9000</value>\n" +
+ " </property>\n" +
+ " <property>\n" +
+ " <name>queueName</name>\n" +
+ " <value>default</value>\n" +
+ " </property>\n" +
+ " </configuration>\n" +
+ " </workflow>\n" +
+ " </action>\n" +
+ "</coordinator-app>");
+
+ final CoordInputDependency coordPullInputDependency = CoordInputDependencyFactory
+ .createPullInputDependencies(true);
+ coordPullInputDependency.addUnResolvedList("data-1", "data-1");
+
+ action.setMissingDependencies(coordPullInputDependency.serialize());
+ action.setPullInputDependencies(coordPullInputDependency);
+
+ return action;
+ }
+}
\ No newline at end of file
diff --git a/core/src/test/java/org/apache/oozie/servlet/TestV2JobServlet.java b/core/src/test/java/org/apache/oozie/servlet/TestV2JobServlet.java
index bacfe89..b42421d 100644
--- a/core/src/test/java/org/apache/oozie/servlet/TestV2JobServlet.java
+++ b/core/src/test/java/org/apache/oozie/servlet/TestV2JobServlet.java
@@ -19,7 +19,6 @@
package org.apache.oozie.servlet;
import org.apache.oozie.client.CoordinatorWfAction;
-import org.apache.oozie.client.OozieClient;
import org.apache.oozie.client.rest.RestConstants;
import org.apache.oozie.client.rest.JsonTags;
import org.apache.oozie.service.ConfigurationService;
@@ -117,13 +116,20 @@
url = createURL(MockCoordinatorEngineService.JOB_ID + (MockCoordinatorEngineService.coordJobs.size() + 1), params);
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
- assertEquals(HttpServletResponse.SC_BAD_REQUEST, conn.getResponseCode());
+ assertBadRequestOrInternalServerError(conn.getResponseCode());
assertEquals(RestConstants.JOB_SHOW_INFO, MockCoordinatorEngineService.did);
return null;
}
});
}
+ private void assertBadRequestOrInternalServerError(final int responseCode) {
+ assertTrue(String.format("HTTP response code [%d] is unexpected, should be one of [%d, %d]",
+ responseCode, HttpServletResponse.SC_BAD_REQUEST, HttpServletResponse.SC_INTERNAL_SERVER_ERROR),
+ HttpServletResponse.SC_BAD_REQUEST == responseCode
+ || HttpServletResponse.SC_INTERNAL_SERVER_ERROR == responseCode);
+ }
+
public void testGetCoordActionReruns() throws Exception {
runTest("/v2/job/*", V1JobServlet.class, IS_SECURITY_ENABLED, new Callable<Void>() {
@Override
@@ -180,7 +186,7 @@
conn.setRequestMethod("PUT");
conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE);
conn.setDoOutput(true);
- assertEquals(HttpServletResponse.SC_BAD_REQUEST, conn.getResponseCode());
+ assertBadRequestOrInternalServerError(conn.getResponseCode());
assertEquals(RestConstants.JOB_ACTION_CHANGE, MockCoordinatorEngineService.did);
return null;
@@ -219,7 +225,7 @@
conn.setRequestMethod("PUT");
conn.setRequestProperty("content-type", RestConstants.XML_CONTENT_TYPE);
conn.setDoOutput(true);
- assertEquals(HttpServletResponse.SC_BAD_REQUEST, conn.getResponseCode());
+ assertBadRequestOrInternalServerError(conn.getResponseCode());
assertEquals(RestConstants.JOB_ACTION_IGNORE, MockCoordinatorEngineService.did);
return null;
@@ -313,7 +319,7 @@
URL url = createURL(MockCoordinatorEngineService.JOB_ID + 1, params);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
- assertEquals(HttpServletResponse.SC_BAD_REQUEST, conn.getResponseCode());
+ assertBadRequestOrInternalServerError(conn.getResponseCode());
return null;
}
@@ -334,7 +340,7 @@
URL url = createURL(MockCoordinatorEngineService.JOB_ID + 1, params);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
- assertEquals(HttpServletResponse.SC_BAD_REQUEST, conn.getResponseCode());
+ assertBadRequestOrInternalServerError(conn.getResponseCode());
return null;
}
@@ -355,7 +361,7 @@
URL url = createURL(MockCoordinatorEngineService.JOB_ID + 1, params);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
- assertEquals(HttpServletResponse.SC_BAD_REQUEST, conn.getResponseCode());
+ assertBadRequestOrInternalServerError(conn.getResponseCode());
return null;
}
@@ -419,7 +425,7 @@
URL url = createURL(MockCoordinatorEngineService.JOB_ID + 1, params);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
- assertEquals(HttpServletResponse.SC_BAD_REQUEST, conn.getResponseCode());
+ assertBadRequestOrInternalServerError(conn.getResponseCode());
return null;
}
@@ -440,7 +446,8 @@
URL url = createURL(MockCoordinatorEngineService.JOB_ID + 1, params);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
- assertEquals(HttpServletResponse.SC_BAD_REQUEST, conn.getResponseCode());
+ assertBadRequestOrInternalServerError(conn.getResponseCode());
+
return null;
}
});
diff --git a/release-log.txt b/release-log.txt
index 4c209f4..7d0dfe7 100644
--- a/release-log.txt
+++ b/release-log.txt
@@ -1,5 +1,6 @@
-- Oozie 5.1.0 release
+OOZIE-3389 Getting input dependency list on the UI throws NPE (andras.piros via asalamon74, kmarton)
OOZIE-3386 Misleading error message when workflow application does not exist (kmarton)
OOZIE-3377 [docs] Remaining 5.1.0 documentation changes (andras.piros)
OOZIE-3376 [tests] TestGraphGenerator should assume JDK8 minor version at least 1.8.0_u40 (andras.piros)