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 */
018package org.apache.oozie.service;
019
020import java.io.IOException;
021import java.util.ArrayList;
022import java.util.List;
023
024import javax.xml.XMLConstants;
025import javax.xml.transform.stream.StreamSource;
026import javax.xml.validation.Schema;
027import javax.xml.validation.SchemaFactory;
028
029import org.apache.hadoop.conf.Configuration;
030import org.apache.oozie.ErrorCode;
031import org.apache.oozie.util.IOUtils;
032import org.xml.sax.SAXException;
033
034/**
035 * Service that loads Oozie workflow definition schema and registered extension
036 * schemas.
037 */
038public class SchemaService implements Service {
039
040    public static final String CONF_PREFIX = Service.CONF_PREFIX + "SchemaService.";
041
042    public static final String WF_CONF_EXT_SCHEMAS = CONF_PREFIX + "wf.ext.schemas";
043
044    public static final String COORD_CONF_EXT_SCHEMAS = CONF_PREFIX + "coord.ext.schemas";
045
046    public static final String BUNDLE_CONF_EXT_SCHEMAS = CONF_PREFIX + "bundle.ext.schemas";
047
048    public static final String SLA_CONF_EXT_SCHEMAS = CONF_PREFIX + "sla.ext.schemas";
049
050    @Deprecated
051    public static final String SLA_NAME_SPACE_URI = "uri:oozie:sla:0.1";
052
053    public static final String SLA_NAMESPACE_URI_2 = "uri:oozie:sla:0.2";
054
055    public static final String COORDINATOR_NAMESPACE_URI_1 = "uri:oozie:coordinator:0.1";
056
057    private Schema wfSchema;
058
059    private Schema coordSchema;
060
061    private Schema bundleSchema;
062
063    private Schema slaSchema;
064
065    private static final String OOZIE_WORKFLOW_XSD[] = {
066        "oozie-workflow-0.1.xsd",
067        "oozie-workflow-0.2.xsd",
068        "oozie-workflow-0.2.5.xsd",
069        "oozie-workflow-0.3.xsd",
070        "oozie-workflow-0.4.xsd",
071        "oozie-workflow-0.4.5.xsd",
072        "oozie-workflow-0.5.xsd"};
073    private static final String OOZIE_COORDINATOR_XSD[] = { "oozie-coordinator-0.1.xsd", "oozie-coordinator-0.2.xsd",
074        "oozie-coordinator-0.3.xsd", "oozie-coordinator-0.4.xsd"};
075    private static final String OOZIE_BUNDLE_XSD[] = { "oozie-bundle-0.1.xsd", "oozie-bundle-0.2.xsd" };
076    private static final String OOZIE_SLA_SEMANTIC_XSD[] = { "gms-oozie-sla-0.1.xsd", "oozie-sla-0.2.xsd" };
077
078    private Schema loadSchema(Configuration conf, String[] baseSchemas, String extSchema) throws SAXException,
079    IOException {
080        List<StreamSource> sources = new ArrayList<StreamSource>();
081        for (String baseSchema : baseSchemas) {
082            sources.add(new StreamSource(IOUtils.getResourceAsStream(baseSchema, -1)));
083        }
084        String[] schemas = conf.getStrings(extSchema);
085        if (schemas != null) {
086            for (String schema : schemas) {
087                schema = schema.trim();
088                if (!schema.isEmpty()) {
089                    sources.add(new StreamSource(IOUtils.getResourceAsStream(schema, -1)));
090                }
091            }
092        }
093        SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
094        return factory.newSchema(sources.toArray(new StreamSource[sources.size()]));
095    }
096
097    /**
098     * Initialize the service.
099     *
100     * @param services services instance.
101     * @throws ServiceException thrown if the service could not be initialized.
102     */
103    public void init(Services services) throws ServiceException {
104        try {
105            wfSchema = loadSchema(services.getConf(), OOZIE_WORKFLOW_XSD, WF_CONF_EXT_SCHEMAS);
106            coordSchema = loadSchema(services.getConf(), OOZIE_COORDINATOR_XSD, COORD_CONF_EXT_SCHEMAS);
107            bundleSchema = loadSchema(services.getConf(), OOZIE_BUNDLE_XSD, BUNDLE_CONF_EXT_SCHEMAS);
108            slaSchema = loadSchema(services.getConf(), OOZIE_SLA_SEMANTIC_XSD, SLA_CONF_EXT_SCHEMAS);
109            bundleSchema = loadSchema(services.getConf(), OOZIE_BUNDLE_XSD, BUNDLE_CONF_EXT_SCHEMAS);
110        }
111        catch (SAXException ex) {
112            throw new ServiceException(ErrorCode.E0130, ex.getMessage(), ex);
113        }
114        catch (IOException ex) {
115            throw new ServiceException(ErrorCode.E0131, ex.getMessage(), ex);
116        }
117    }
118
119    /**
120     * Return the public interface of the service.
121     *
122     * @return {@link SchemaService}.
123     */
124    public Class<? extends Service> getInterface() {
125        return SchemaService.class;
126    }
127
128    /**
129     * Destroy the service.
130     */
131    public void destroy() {
132        wfSchema = null;
133        bundleSchema = null;
134        slaSchema = null;
135        coordSchema = null;
136    }
137
138    /**
139     * Return the schema for XML validation of application definitions.
140     *
141     * @param schemaName: Name of schema definition (i.e.
142     *        WORKFLOW/COORDINATOR/BUNDLE)
143     * @return the schema for XML validation of application definitions.
144     */
145    public Schema getSchema(SchemaName schemaName) {
146        Schema returnSchema = null;
147        if (schemaName == SchemaName.WORKFLOW) {
148            returnSchema = wfSchema;
149        }
150        else if (schemaName == SchemaName.COORDINATOR) {
151            returnSchema = coordSchema;
152        }
153        else if (schemaName == SchemaName.BUNDLE) {
154            returnSchema = bundleSchema;
155        }
156        else if (schemaName == SchemaName.SLA_ORIGINAL) {
157            returnSchema = slaSchema;
158        }
159        else {
160            throw new RuntimeException("No schema found with name " + schemaName);
161        }
162        return returnSchema;
163    }
164
165    public enum SchemaName {
166        WORKFLOW(1), COORDINATOR(2), SLA_ORIGINAL(3), BUNDLE(4);
167        private final int id;
168
169        private SchemaName(int id) {
170            this.id = id;
171        }
172
173        public int getId() {
174            return id;
175        }
176    }
177}