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 }