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.text.MessageFormat;
019    import java.util.Properties;
020    
021    import org.apache.oozie.util.IOUtils;
022    import org.apache.oozie.util.Instrumentation;
023    import org.apache.oozie.util.XLog;
024    import org.apache.oozie.store.StoreException;
025    import org.apache.oozie.service.Service;
026    import org.apache.oozie.service.Services;
027    import org.apache.oozie.store.SLAStore;
028    import org.apache.oozie.store.Store;
029    import org.apache.oozie.store.WorkflowStore;
030    import org.apache.oozie.store.CoordinatorStore;
031    import org.apache.oozie.util.Instrumentable;
032    import org.apache.oozie.ErrorCode;
033    import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI;
034    import org.apache.hadoop.conf.Configuration;
035    
036    import java.sql.Connection;
037    import java.sql.DriverManager;
038    import java.sql.SQLException;
039    
040    import javax.persistence.EntityManager;
041    import javax.persistence.EntityManagerFactory;
042    import javax.persistence.Persistence;
043    
044    import org.apache.oozie.CoordinatorActionBean;
045    import org.apache.oozie.CoordinatorJobBean;
046    import org.apache.oozie.WorkflowActionBean;
047    import org.apache.oozie.WorkflowJobBean;
048    import org.apache.oozie.SLAEventBean;
049    import org.apache.oozie.client.rest.JsonCoordinatorAction;
050    import org.apache.oozie.client.rest.JsonCoordinatorJob;
051    import org.apache.oozie.client.rest.JsonWorkflowAction;
052    import org.apache.oozie.client.rest.JsonWorkflowJob;
053    import org.apache.oozie.client.rest.JsonSLAEvent;
054    
055    /**
056     * Base service for persistency of jobs and actions.
057     */
058    public class StoreService implements Service {
059    
060        public static final String CONF_PREFIX = Service.CONF_PREFIX + "StoreService.";
061        public static final String CONF_URL = CONF_PREFIX + "jdbc.url";
062        public static final String CONF_DRIVER = CONF_PREFIX + "jdbc.driver";
063        public static final String CONF_USERNAME = CONF_PREFIX + "jdbc.username";
064        public static final String CONF_PASSWORD = CONF_PREFIX + "jdbc.password";
065        public static final String CONF_MAX_ACTIVE_CONN = CONF_PREFIX + "pool.max.active.conn";
066        public static final String CONF_CREATE_DB_SCHEMA = CONF_PREFIX + "create.db.schema";
067    
068        private EntityManagerFactory factory;
069    
070        /**
071         * Return instance of store.
072         *
073         * @return {@link Store}.
074         */
075        @SuppressWarnings("unchecked")
076        public <S extends Store> S getStore(Class<S> klass) throws StoreException {
077            if (WorkflowStore.class.equals(klass)) {
078                return (S) Services.get().get(WorkflowStoreService.class).create();
079            }
080            else {
081                if (CoordinatorStore.class.equals(klass)) {
082                    return (S) Services.get().get(CoordinatorStoreService.class).create();
083                }
084                else {
085                    if (SLAStore.class.equals(klass)) {
086                        return (S) Services.get().get(SLAStoreService.class).create();
087                    }
088                }
089            }
090            // to do add checks for other stores - coordinator and SLA stores
091            throw new StoreException(ErrorCode.E0607, " can not get store StoreService.getStore(Class)");
092        }
093    
094        /**
095         * Return instance of store with an EntityManager pointing to an existing Store.
096         *
097         * @return {@link Store}.
098         */
099        @SuppressWarnings("unchecked")
100        public <S extends Store, T extends Store> S getStore(Class<S> klass, T store) throws StoreException {
101            if (WorkflowStore.class.equals(klass)) {
102                return (S) Services.get().get(WorkflowStoreService.class).create(store);
103            }
104            else {
105                if (CoordinatorStore.class.equals(klass)) {
106                    return (S) Services.get().get(CoordinatorStoreService.class).create(store);
107                }
108                else {
109                    if (SLAStore.class.equals(klass)) {
110                        return (S) Services.get().get(SLAStoreService.class).create(store);
111                    }
112                }
113            }
114            throw new StoreException(ErrorCode.E0607, " StoreService.getStore(Class, store)");
115        }
116    
117        /**
118         * Return the public interface of the service.
119         *
120         * @return {@link StoreService}.
121         */
122        public Class<? extends Service> getInterface() {
123            return StoreService.class;
124        }
125    
126        /**
127         * Initializes the {@link StoreService}.
128         *
129         * @param services services instance.
130         */
131        public void init(Services services) throws ServiceException {
132            Configuration conf = services.getConf();
133            String url = conf.get(CONF_URL, "jdbc:derby:${oozie.home.dir}/${oozie.db.schema.name}-db;create=true");
134            String driver = conf.get(CONF_DRIVER, "org.apache.derby.jdbc.EmbeddedDriver");
135            String user = conf.get(CONF_USERNAME, "sa");
136            String password = conf.get(CONF_PASSWORD, "").trim();
137            String maxConn = conf.get(CONF_MAX_ACTIVE_CONN, "10").trim();
138            boolean autoSchemaCreation = conf.getBoolean(CONF_CREATE_DB_SCHEMA, true);
139    
140            if (!url.startsWith("jdbc:")) {
141                throw new ServiceException(ErrorCode.E0608, url, "invalid JDBC URL, must start with 'jdbc:'");
142            }
143            String dbType = url.substring("jdbc:".length());
144            if (dbType.indexOf(":") <= 0) {
145                throw new ServiceException(ErrorCode.E0608, url, "invalid JDBC URL, missing vendor 'jdbc:[VENDOR]:...'");
146            }
147            dbType = dbType.substring(0, dbType.indexOf(":"));
148    
149            String persistentUnit = "oozie-" + dbType;
150    
151            //Checking existince of ORM file for DB type
152            String ormFile = "META-INF/" + persistentUnit + "-orm.xml";
153            try {
154                IOUtils.getResourceAsStream(ormFile, -1);
155            }
156            catch (IOException ex) {
157                throw new ServiceException(ErrorCode.E0609, dbType, ormFile);
158            }
159    
160            String connProps = "DriverClassName={0},Url={1},Username={2},Password={3},MaxActive={4}";
161            connProps = MessageFormat.format(connProps, driver, url, user, password, maxConn);
162            Properties props = new Properties();
163            props.setProperty("openjpa.ConnectionProperties", connProps);
164            if (autoSchemaCreation) {
165                props.setProperty("openjpa.jdbc.SynchronizeMappings", "buildSchema(ForeignKeys=true)");
166            }
167    
168            factory = Persistence.createEntityManagerFactory(persistentUnit, props);
169    
170            EntityManager entityManager = getEntityManager();
171            entityManager.find(WorkflowActionBean.class, 1);
172            entityManager.find(WorkflowJobBean.class, 1);
173            entityManager.find(CoordinatorActionBean.class, 1);
174            entityManager.find(CoordinatorJobBean.class, 1);
175            entityManager.find(JsonWorkflowAction.class, 1);
176            entityManager.find(JsonWorkflowJob.class, 1);
177            entityManager.find(JsonCoordinatorAction.class, 1);
178            entityManager.find(JsonCoordinatorJob.class, 1);
179            entityManager.find(SLAEventBean.class, 1);
180            entityManager.find(JsonSLAEvent.class, 1);
181    
182            XLog.getLog(getClass()).info(XLog.STD, "All entities initialized");
183            // need to use a pseudo no-op transaction so all entities, datasource
184            // and connection pool are initialized
185            // one time only
186            entityManager.getTransaction().begin();
187            OpenJPAEntityManagerFactorySPI spi = (OpenJPAEntityManagerFactorySPI) factory;
188            XLog.getLog(getClass()).info("JPA configuration: {0}", spi.getConfiguration().getConnectionProperties());
189            entityManager.getTransaction().commit();
190            entityManager.close();
191        }
192    
193        /**
194         * Destroy the StoreService
195         */
196        public void destroy() {
197            factory.close();
198        }
199    
200        /**
201         * Return EntityManager
202         */
203        public EntityManager getEntityManager() {
204            return factory.createEntityManager();
205        }
206    }