001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.oozie.command.wf; 020 021import org.apache.hadoop.conf.Configuration; 022import org.apache.oozie.WorkflowJobBean; 023import org.apache.oozie.ErrorCode; 024import org.apache.oozie.service.JPAService; 025import org.apache.oozie.service.WorkflowStoreService; 026import org.apache.oozie.service.WorkflowAppService; 027import org.apache.oozie.service.Services; 028import org.apache.oozie.service.DagXLogInfoService; 029import org.apache.oozie.util.InstrumentUtils; 030import org.apache.oozie.util.LogUtils; 031import org.apache.oozie.util.XLog; 032import org.apache.oozie.util.ParamChecker; 033import org.apache.oozie.util.XConfiguration; 034import org.apache.oozie.util.XmlUtils; 035import org.apache.oozie.command.CommandException; 036import org.apache.oozie.executor.jpa.WorkflowJobInsertJPAExecutor; 037import org.apache.oozie.store.StoreException; 038import org.apache.oozie.workflow.WorkflowApp; 039import org.apache.oozie.workflow.WorkflowException; 040import org.apache.oozie.workflow.WorkflowInstance; 041import org.apache.oozie.workflow.WorkflowLib; 042import org.apache.oozie.util.PropertiesUtils; 043import org.apache.oozie.client.OozieClient; 044import org.apache.oozie.client.WorkflowJob; 045import org.apache.oozie.client.XOozieClient; 046import org.jdom.Element; 047import org.jdom.Namespace; 048 049import com.google.common.collect.ImmutableSet; 050 051import java.util.Date; 052import java.util.List; 053import java.util.Map; 054import java.util.Set; 055import java.util.HashSet; 056 057public abstract class SubmitHttpXCommand extends WorkflowXCommand<String> { 058 059 static final Set<String> MANDATORY_OOZIE_CONFS = ImmutableSet.of(XOozieClient.RM, XOozieClient.NN, OozieClient.LIBPATH); 060 static final Set<String> OPTIONAL_OOZIE_CONFS = ImmutableSet.of(XOozieClient.FILES, XOozieClient.ARCHIVES); 061 062 private Configuration conf; 063 064 public SubmitHttpXCommand(String name, String type, Configuration conf) { 065 super(name, type, 1); 066 this.conf = ParamChecker.notNull(conf, "conf"); 067 } 068 069 private static final Set<String> DISALLOWED_DEFAULT_PROPERTIES = new HashSet<String>(); 070 private static final Set<String> DISALLOWED_USER_PROPERTIES = new HashSet<String>(); 071 072 static { 073 String[] badUserProps = { PropertiesUtils.DAYS, PropertiesUtils.HOURS, PropertiesUtils.MINUTES, 074 PropertiesUtils.KB, PropertiesUtils.MB, PropertiesUtils.GB, PropertiesUtils.TB, PropertiesUtils.PB, 075 PropertiesUtils.RECORDS, PropertiesUtils.MAP_IN, PropertiesUtils.MAP_OUT, PropertiesUtils.REDUCE_IN, 076 PropertiesUtils.REDUCE_OUT, PropertiesUtils.GROUPS }; 077 PropertiesUtils.createPropertySet(badUserProps, DISALLOWED_USER_PROPERTIES); 078 079 String[] badDefaultProps = { PropertiesUtils.HADOOP_USER}; 080 PropertiesUtils.createPropertySet(badUserProps, DISALLOWED_DEFAULT_PROPERTIES); 081 PropertiesUtils.createPropertySet(badDefaultProps, DISALLOWED_DEFAULT_PROPERTIES); 082 } 083 084 abstract protected Element generateSection(Configuration conf, Namespace ns); 085 086 abstract protected Namespace getSectionNamespace(); 087 088 abstract protected String getWorkflowName(); 089 090 protected void checkMandatoryConf(Configuration conf) { 091 for (String key : MANDATORY_OOZIE_CONFS) { 092 String value = conf.get(key); 093 if (value == null) { 094 throw new RuntimeException(key + " is not specified"); 095 } 096 } 097 } 098 099 protected Namespace getWorkflowNamespace() { 100 return Namespace.getNamespace("uri:oozie:workflow:0.2"); 101 } 102 /** 103 * Generate workflow xml from conf object 104 * 105 * @param conf the configuration object 106 * @return workflow xml def string representation 107 */ 108 protected String getWorkflowXml(Configuration conf) { 109 checkMandatoryConf(conf); 110 111 Namespace ns = getWorkflowNamespace(); 112 Element root = new Element("workflow-app", ns); 113 String name = getWorkflowName(); 114 root.setAttribute("name", "oozie-" + name); 115 116 Element start = new Element("start", ns); 117 String nodeName = name + "1"; 118 start.setAttribute("to", nodeName); 119 root.addContent(start); 120 121 Element action = new Element("action", ns); 122 action.setAttribute("name", nodeName); 123 124 Element ele = generateSection(conf, getSectionNamespace()); 125 action.addContent(ele); 126 127 Element ok = new Element("ok", ns); 128 ok.setAttribute("to", "end"); 129 action.addContent(ok); 130 131 Element error = new Element("error", ns); 132 error.setAttribute("to", "fail"); 133 action.addContent(error); 134 135 root.addContent(action); 136 137 Element kill = new Element("kill", ns); 138 kill.setAttribute("name", "fail"); 139 Element message = new Element("message", ns); 140 message.addContent(name + " failed, error message[${wf:errorMessage(wf:lastErrorNode())}]"); 141 kill.addContent(message); 142 root.addContent(kill); 143 144 Element end = new Element("end", ns); 145 end.setAttribute("name", "end"); 146 root.addContent(end); 147 148 return XmlUtils.prettyPrint(root).toString(); 149 }; 150 151 protected Element generateConfigurationSection(List<String> Dargs, Namespace ns) { 152 Element configuration = new Element("configuration", ns); 153 for (String arg : Dargs) { 154 String name = null, value = null; 155 int pos = arg.indexOf("="); 156 if (pos == -1) { // "-D<name>" or "-D" only 157 name = arg.substring(2, arg.length()); 158 value = ""; 159 } 160 else { // "-D<name>=<value>" 161 name = arg.substring(2, pos); 162 value = arg.substring(pos + 1, arg.length()); 163 } 164 165 Element property = new Element("property", ns); 166 Element nameElement = new Element("name", ns); 167 nameElement.addContent(name); 168 property.addContent(nameElement); 169 Element valueElement = new Element("value", ns); 170 valueElement.addContent(value); 171 property.addContent(valueElement); 172 configuration.addContent(property); 173 } 174 175 return configuration; 176 } 177 178 /* (non-Javadoc) 179 * @see org.apache.oozie.command.XCommand#execute() 180 */ 181 @Override 182 protected String execute() throws CommandException { 183 InstrumentUtils.incrJobCounter(getName(), 1, getInstrumentation()); 184 WorkflowAppService wps = Services.get().get(WorkflowAppService.class); 185 try { 186 XLog.Info.get().setParameter(DagXLogInfoService.TOKEN, conf.get(OozieClient.LOG_TOKEN)); 187 String wfXml = getWorkflowXml(conf); 188 LOG.debug("workflow xml created on the server side is :\n"); 189 LOG.debug(wfXml); 190 WorkflowApp app = wps.parseDef(wfXml, conf); 191 XConfiguration protoActionConf = wps.createProtoActionConf(conf, false); 192 WorkflowLib workflowLib = Services.get().get(WorkflowStoreService.class).getWorkflowLibWithNoDB(); 193 194 PropertiesUtils.checkDisallowedProperties(conf, DISALLOWED_USER_PROPERTIES); 195 196 // Resolving all variables in the job properties. 197 // This ensures the Hadoop Configuration semantics is preserved. 198 XConfiguration resolvedVarsConf = new XConfiguration(); 199 for (Map.Entry<String, String> entry : conf) { 200 resolvedVarsConf.set(entry.getKey(), conf.get(entry.getKey())); 201 } 202 conf = resolvedVarsConf; 203 204 WorkflowInstance wfInstance; 205 try { 206 wfInstance = workflowLib.createInstance(app, conf); 207 } 208 catch (WorkflowException e) { 209 throw new StoreException(e); 210 } 211 212 Configuration conf = wfInstance.getConf(); 213 214 WorkflowJobBean workflow = new WorkflowJobBean(); 215 workflow.setId(wfInstance.getId()); 216 workflow.setAppName(app.getName()); 217 workflow.setAppPath(conf.get(OozieClient.APP_PATH)); 218 workflow.setConf(XmlUtils.prettyPrint(conf).toString()); 219 workflow.setProtoActionConf(protoActionConf.toXmlString()); 220 workflow.setCreatedTime(new Date()); 221 workflow.setLastModifiedTime(new Date()); 222 workflow.setLogToken(conf.get(OozieClient.LOG_TOKEN, "")); 223 workflow.setStatus(WorkflowJob.Status.PREP); 224 workflow.setRun(0); 225 workflow.setUser(conf.get(OozieClient.USER_NAME)); 226 workflow.setGroup(conf.get(OozieClient.GROUP_NAME)); 227 workflow.setWorkflowInstance(wfInstance); 228 workflow.setExternalId(conf.get(OozieClient.EXTERNAL_ID)); 229 230 LogUtils.setLogInfo(workflow); 231 JPAService jpaService = Services.get().get(JPAService.class); 232 if (jpaService != null) { 233 jpaService.execute(new WorkflowJobInsertJPAExecutor(workflow)); 234 } 235 else { 236 LOG.error(ErrorCode.E0610); 237 return null; 238 } 239 240 return workflow.getId(); 241 } 242 catch (WorkflowException ex) { 243 throw new CommandException(ex); 244 } 245 catch (Exception ex) { 246 throw new CommandException(ErrorCode.E0803, ex.getMessage(), ex); 247 } 248 } 249 250 static private void addSection(Element X, Namespace ns, String filesStr, String tagName) { 251 if (filesStr != null) { 252 String[] files = filesStr.split(","); 253 for (String f : files) { 254 Element tagElement = new Element(tagName, ns); 255 if (f.contains("#")) { 256 tagElement.addContent(f); 257 } 258 else { 259 String filename = f.substring(f.lastIndexOf("/") + 1, f.length()); 260 if (filename == null || filename.isEmpty()) { 261 tagElement.addContent(f); 262 } 263 else { 264 tagElement.addContent(f + "#" + filename); 265 } 266 } 267 X.addContent(tagElement); 268 } 269 } 270 } 271 272 /** 273 * Add file section in X. 274 * 275 * @param parent XML element to be appended 276 * @param conf Configuration object 277 * @param ns XML element namespace 278 */ 279 static void addFileSection(Element X, Configuration conf, Namespace ns) { 280 String filesStr = conf.get(XOozieClient.FILES); 281 addSection(X, ns, filesStr, "file"); 282 } 283 284 /** 285 * Add archive section in X. 286 * 287 * @param parent XML element to be appended 288 * @param conf Configuration object 289 * @param ns XML element namespace 290 */ 291 static void addArchiveSection(Element X, Configuration conf, Namespace ns) { 292 String archivesStr = conf.get(XOozieClient.ARCHIVES); 293 addSection(X, ns, archivesStr, "archive"); 294 } 295}