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.servlet;
020
021import org.apache.hadoop.conf.Configuration;
022import org.apache.hadoop.fs.FileSystem;
023import org.apache.hadoop.fs.Path;
024import org.apache.oozie.ErrorCode;
025import org.apache.oozie.cli.OozieCLIException;
026import org.apache.oozie.client.rest.JsonTags;
027import org.apache.oozie.client.rest.RestConstants;
028import org.apache.oozie.service.HadoopAccessorService;
029import org.apache.oozie.service.SchemaService;
030import org.apache.oozie.service.Services;
031import org.apache.oozie.util.IOUtils;
032import org.json.simple.JSONObject;
033import org.xml.sax.SAXException;
034
035import javax.servlet.ServletException;
036import javax.servlet.http.HttpServletRequest;
037import javax.servlet.http.HttpServletResponse;
038import javax.xml.transform.stream.StreamSource;
039import javax.xml.validation.Schema;
040import javax.xml.validation.Validator;
041import java.io.IOException;
042import java.io.InputStreamReader;
043import java.io.Reader;
044import java.io.StringReader;
045import java.io.StringWriter;
046import java.net.URI;
047import java.util.Arrays;
048
049public class V2ValidateServlet extends JsonRestServlet {
050    private static final String INSTRUMENTATION_NAME = "v2validate";
051
052    private static final ResourceInfo RESOURCE_INFO =
053            new ResourceInfo("", Arrays.asList("POST"), Arrays.asList(
054                    new ParameterInfo(RestConstants.FILE_PARAM, String.class, true, Arrays.asList("POST")),
055                    new ParameterInfo(RestConstants.USER_PARAM, String.class, true, Arrays.asList("POST"))));
056
057
058    public V2ValidateServlet() {
059        super(INSTRUMENTATION_NAME, RESOURCE_INFO);
060    }
061
062    /**
063     * Validate workflow definition.
064     */
065    @Override
066    @SuppressWarnings("unchecked")
067    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
068        validateContentType(request, RestConstants.XML_CONTENT_TYPE);
069
070        String file = request.getParameter(RestConstants.FILE_PARAM);
071        String user = request.getParameter(RestConstants.USER_PARAM);
072
073        stopCron();
074
075        StringWriter stringWriter = new StringWriter();
076        if (file.startsWith("hdfs://")) {
077            try {
078                URI uri = new URI(file);
079                HadoopAccessorService has = Services.get().get(HadoopAccessorService.class);
080                Configuration fsConf = has.createConfiguration(uri.getAuthority());
081                FileSystem fs = has.createFileSystem(user, uri, fsConf);
082
083                Path path = new Path(uri.getPath());
084                IOUtils.copyCharStream(new InputStreamReader(fs.open(path)), stringWriter);
085
086            } catch (Exception e) {
087                throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, ErrorCode.E0505,
088                        "File does not exist, "+ file);
089            }
090        }
091        else {
092            IOUtils.copyCharStream(new InputStreamReader(request.getInputStream()), stringWriter);
093        }
094        try {
095            validate(stringWriter.toString());
096        } catch (Exception e) {
097            throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, ErrorCode.E0701,
098                    file + ", " + e.toString());
099        }
100
101        JSONObject json = createJSON("Valid workflow-app");
102        startCron();
103        sendJsonResponse(response, HttpServletResponse.SC_OK, json);
104    }
105
106    private void validate(String xml) throws Exception{
107        SchemaService schemaService = Services.get().get(SchemaService.class);
108        Schema[] schemas = {schemaService.getSchema(SchemaService.SchemaName.WORKFLOW),
109                schemaService.getSchema(SchemaService.SchemaName.COORDINATOR),
110                schemaService.getSchema(SchemaService.SchemaName.BUNDLE),
111                schemaService.getSchema(SchemaService.SchemaName.SLA_ORIGINAL)};
112
113        Exception exception = null;
114        for (int i = 0; i < schemas.length; i++) {
115            try{
116                validateSchema(schemas[i], new StringReader(xml));
117                exception = null;
118                break;
119            } catch (SAXException e) {
120                if (i == 0) {
121                    exception = e;
122                }
123                // Check the root element declaration(workflow-app, coordinator-app, bundle-app).
124                // If invalid, move to next schema validation.
125                if (!e.getMessage().contains("cvc-elt.1")) {
126                    exception = e;
127                    break;
128                }
129            } catch (Exception e) {
130                exception = e;
131                break;
132            }
133        }
134        if (exception !=  null) {
135            throw exception;
136        }
137    }
138
139    private void validateSchema(Schema schema, Reader src) throws SAXException, IOException, OozieCLIException{
140            Validator validator = schema.newValidator();
141            validator.validate(new StreamSource(src));
142    }
143
144    private JSONObject createJSON(String content) {
145        JSONObject jsonObject = new JSONObject();
146        jsonObject.put(JsonTags.VALIDATE, content);
147        return jsonObject;
148    }
149
150}