001    /**
002     * Copyright (c) 2010 Yahoo! Inc. All rights reserved.
003     * Licensed under the Apache License, Version 2.0 (the "License");
004     * you may not use this file except in compliance with the License.
005     * You may obtain a copy of the License at
006     *
007     *   http://www.apache.org/licenses/LICENSE-2.0
008     *
009     *  Unless required by applicable law or agreed to in writing, software
010     *  distributed under the License is distributed on an "AS IS" BASIS,
011     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012     *  See the License for the specific language governing permissions and
013     *  limitations under the License. See accompanying LICENSE file.
014     */
015    package org.apache.oozie.service;
016    
017    import java.io.IOException;
018    import java.util.ArrayList;
019    import java.util.List;
020    
021    import javax.xml.XMLConstants;
022    import javax.xml.transform.stream.StreamSource;
023    import javax.xml.validation.Schema;
024    import javax.xml.validation.SchemaFactory;
025    
026    import org.apache.hadoop.conf.Configuration;
027    import org.apache.oozie.ErrorCode;
028    import org.apache.oozie.util.IOUtils;
029    import org.xml.sax.SAXException;
030    
031    /**
032     * Service that loads Oozie workflow definition schema and registered extension
033     * schemas.
034     */
035    public class SchemaService implements Service {
036    
037        public static final String CONF_PREFIX = Service.CONF_PREFIX + "SchemaService.";
038    
039        public static final String WF_CONF_EXT_SCHEMAS = CONF_PREFIX + "wf.ext.schemas";
040    
041        public static final String COORD_CONF_EXT_SCHEMAS = CONF_PREFIX + "coord.ext.schemas";
042    
043        public static final String BUNDLE_CONF_EXT_SCHEMAS = CONF_PREFIX + "bundle.ext.schemas";
044    
045        public static final String SLA_CONF_EXT_SCHEMAS = CONF_PREFIX + "sla.ext.schemas";
046    
047        public static final String SLA_NAME_SPACE_URI = "uri:oozie:sla:0.1";
048    
049        public static final String COORDINATOR_NAMESPACE_URI_1 = "uri:oozie:coordinator:0.1";
050    
051        private Schema wfSchema;
052    
053        private Schema coordSchema;
054    
055        private Schema bundleSchema;
056    
057        private Schema slaSchema;
058    
059        private static final String OOZIE_WORKFLOW_XSD[] = { "oozie-workflow-0.1.xsd", "oozie-workflow-0.2.xsd",
060                "oozie-workflow-0.2.5.xsd" };
061        private static final String OOZIE_COORDINATOR_XSD[] = { "oozie-coordinator-0.1.xsd", "oozie-coordinator-0.2.xsd" };
062        private static final String OOZIE_BUNDLE_XSD[] = { "oozie-bundle-0.1.xsd" };
063        private static final String OOZIE_SLA_SEMANTIC_XSD[] = { "gms-oozie-sla-0.1.xsd" };
064    
065        private Schema loadSchema(Configuration conf, String[] baseSchemas, String extSchema) throws SAXException,
066        IOException {
067            List<StreamSource> sources = new ArrayList<StreamSource>();
068            for (String baseSchema : baseSchemas) {
069                sources.add(new StreamSource(IOUtils.getResourceAsStream(baseSchema, -1)));
070            }
071            String[] schemas = conf.getStrings(extSchema);
072            if (schemas != null) {
073                for (String schema : schemas) {
074                    sources.add(new StreamSource(IOUtils.getResourceAsStream(schema, -1)));
075                }
076            }
077            SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
078            return factory.newSchema(sources.toArray(new StreamSource[sources.size()]));
079        }
080    
081        /**
082         * Initialize the service.
083         *
084         * @param services services instance.
085         * @throws ServiceException thrown if the service could not be initialized.
086         */
087        public void init(Services services) throws ServiceException {
088            try {
089                wfSchema = loadSchema(services.getConf(), OOZIE_WORKFLOW_XSD, WF_CONF_EXT_SCHEMAS);
090                coordSchema = loadSchema(services.getConf(), OOZIE_COORDINATOR_XSD, COORD_CONF_EXT_SCHEMAS);
091                bundleSchema = loadSchema(services.getConf(), OOZIE_BUNDLE_XSD, BUNDLE_CONF_EXT_SCHEMAS);
092                slaSchema = loadSchema(services.getConf(), OOZIE_SLA_SEMANTIC_XSD, SLA_CONF_EXT_SCHEMAS);
093                bundleSchema = loadSchema(services.getConf(), OOZIE_BUNDLE_XSD, BUNDLE_CONF_EXT_SCHEMAS);
094            }
095            catch (SAXException ex) {
096                throw new ServiceException(ErrorCode.E0130, ex.getMessage(), ex);
097            }
098            catch (IOException ex) {
099                throw new ServiceException(ErrorCode.E0131, ex.getMessage(), ex);
100            }
101        }
102    
103        /**
104         * Return the public interface of the service.
105         *
106         * @return {@link SchemaService}.
107         */
108        public Class<? extends Service> getInterface() {
109            return SchemaService.class;
110        }
111    
112        /**
113         * Destroy the service.
114         */
115        public void destroy() {
116            wfSchema = null;
117            bundleSchema = null;
118            slaSchema = null;
119            coordSchema = null;
120        }
121    
122        /**
123         * Return the schema for XML validation of application definitions.
124         *
125         * @param schemaName: Name of schema definition (i.e.
126         *        WORKFLOW/COORDINATOR/BUNDLE)
127         * @return the schema for XML validation of application definitions.
128         */
129        public Schema getSchema(SchemaName schemaName) {
130            Schema returnSchema = null;
131            if (schemaName == SchemaName.WORKFLOW) {
132                returnSchema = wfSchema;
133            }
134            else if (schemaName == SchemaName.COORDINATOR) {
135                returnSchema = coordSchema;
136            }
137            else if (schemaName == SchemaName.BUNDLE) {
138                returnSchema = bundleSchema;
139            }
140            else if (schemaName == SchemaName.SLA_ORIGINAL) {
141                returnSchema = slaSchema;
142            }
143            else {
144                throw new RuntimeException("No schema found with name " + schemaName);
145            }
146            return returnSchema;
147        }
148    
149        public enum SchemaName {
150            WORKFLOW(1), COORDINATOR(2), SLA_ORIGINAL(3), BUNDLE(4);
151            private final int id;
152    
153            private SchemaName(int id) {
154                this.id = id;
155            }
156    
157            public int getId() {
158                return id;
159            }
160        }
161    }