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.5.xsd"};
072    private static final String OOZIE_COORDINATOR_XSD[] = { "oozie-coordinator-0.1.xsd", "oozie-coordinator-0.2.xsd",
073        "oozie-coordinator-0.3.xsd", "oozie-coordinator-0.4.xsd"};
074    private static final String OOZIE_BUNDLE_XSD[] = { "oozie-bundle-0.1.xsd", "oozie-bundle-0.2.xsd" };
075    private static final String OOZIE_SLA_SEMANTIC_XSD[] = { "gms-oozie-sla-0.1.xsd", "oozie-sla-0.2.xsd" };
076
077    private Schema loadSchema(Configuration conf, String[] baseSchemas, String extSchema) throws SAXException,
078    IOException {
079        List<StreamSource> sources = new ArrayList<StreamSource>();
080        for (String baseSchema : baseSchemas) {
081            sources.add(new StreamSource(IOUtils.getResourceAsStream(baseSchema, -1)));
082        }
083        String[] schemas = conf.getStrings(extSchema);
084        if (schemas != null) {
085            for (String schema : schemas) {
086                schema = schema.trim();
087                if (!schema.isEmpty()) {
088                    sources.add(new StreamSource(IOUtils.getResourceAsStream(schema, -1)));
089                }
090            }
091        }
092        SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
093        return factory.newSchema(sources.toArray(new StreamSource[sources.size()]));
094    }
095
096    /**
097     * Initialize the service.
098     *
099     * @param services services instance.
100     * @throws ServiceException thrown if the service could not be initialized.
101     */
102    public void init(Services services) throws ServiceException {
103        try {
104            wfSchema = loadSchema(services.getConf(), OOZIE_WORKFLOW_XSD, WF_CONF_EXT_SCHEMAS);
105            coordSchema = loadSchema(services.getConf(), OOZIE_COORDINATOR_XSD, COORD_CONF_EXT_SCHEMAS);
106            bundleSchema = loadSchema(services.getConf(), OOZIE_BUNDLE_XSD, BUNDLE_CONF_EXT_SCHEMAS);
107            slaSchema = loadSchema(services.getConf(), OOZIE_SLA_SEMANTIC_XSD, SLA_CONF_EXT_SCHEMAS);
108            bundleSchema = loadSchema(services.getConf(), OOZIE_BUNDLE_XSD, BUNDLE_CONF_EXT_SCHEMAS);
109        }
110        catch (SAXException ex) {
111            throw new ServiceException(ErrorCode.E0130, ex.getMessage(), ex);
112        }
113        catch (IOException ex) {
114            throw new ServiceException(ErrorCode.E0131, ex.getMessage(), ex);
115        }
116    }
117
118    /**
119     * Return the public interface of the service.
120     *
121     * @return {@link SchemaService}.
122     */
123    public Class<? extends Service> getInterface() {
124        return SchemaService.class;
125    }
126
127    /**
128     * Destroy the service.
129     */
130    public void destroy() {
131        wfSchema = null;
132        bundleSchema = null;
133        slaSchema = null;
134        coordSchema = null;
135    }
136
137    /**
138     * Return the schema for XML validation of application definitions.
139     *
140     * @param schemaName: Name of schema definition (i.e.
141     *        WORKFLOW/COORDINATOR/BUNDLE)
142     * @return the schema for XML validation of application definitions.
143     */
144    public Schema getSchema(SchemaName schemaName) {
145        Schema returnSchema = null;
146        if (schemaName == SchemaName.WORKFLOW) {
147            returnSchema = wfSchema;
148        }
149        else if (schemaName == SchemaName.COORDINATOR) {
150            returnSchema = coordSchema;
151        }
152        else if (schemaName == SchemaName.BUNDLE) {
153            returnSchema = bundleSchema;
154        }
155        else if (schemaName == SchemaName.SLA_ORIGINAL) {
156            returnSchema = slaSchema;
157        }
158        else {
159            throw new RuntimeException("No schema found with name " + schemaName);
160        }
161        return returnSchema;
162    }
163
164    public enum SchemaName {
165        WORKFLOW(1), COORDINATOR(2), SLA_ORIGINAL(3), BUNDLE(4);
166        private final int id;
167
168        private SchemaName(int id) {
169            this.id = id;
170        }
171
172        public int getId() {
173            return id;
174        }
175    }
176}