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:hsqldb:mem:oozie;create=true");
134 String driver = conf.get(CONF_DRIVER, "org.hsqldb.jdbcDriver");
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 }