1 /* 2 Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of the GNU General Public License, version 2.0, 6 as published by the Free Software Foundation. 7 8 This program is also distributed with certain software (including 9 but not limited to OpenSSL) that is licensed under separate terms, 10 as designated in a particular file or component or in included license 11 documentation. The authors of MySQL hereby grant you an additional 12 permission to link the program and your derivative works with the 13 separately licensed software that they have included with MySQL. 14 15 This program is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU General Public License, version 2.0, for more details. 19 20 You should have received a copy of the GNU General Public License 21 along with this program; if not, write to the Free Software 22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 23 */ 24 25 package com.mysql.clusterj.openjpa; 26 27 import com.mysql.clusterj.ClusterJFatalInternalException; 28 import com.mysql.clusterj.ClusterJHelper; 29 import com.mysql.clusterj.SessionFactory; 30 31 import com.mysql.clusterj.core.SessionFactoryImpl; 32 33 import com.mysql.clusterj.core.spi.DomainTypeHandler; 34 import com.mysql.clusterj.core.spi.DomainTypeHandlerFactory; 35 import com.mysql.clusterj.core.store.Dictionary; 36 import com.mysql.clusterj.core.util.I18NHelper; 37 import com.mysql.clusterj.core.util.Logger; 38 import com.mysql.clusterj.core.util.LoggerFactoryService; 39 40 import java.lang.reflect.InvocationTargetException; 41 import java.lang.reflect.Method; 42 import java.util.HashMap; 43 import java.util.Map; 44 import java.util.Properties; 45 46 import org.apache.openjpa.jdbc.conf.JDBCConfigurationImpl; 47 import org.apache.openjpa.jdbc.meta.ClassMapping; 48 import org.apache.openjpa.lib.conf.BooleanValue; 49 import org.apache.openjpa.lib.conf.IntValue; 50 import org.apache.openjpa.lib.conf.ProductDerivations; 51 import org.apache.openjpa.lib.conf.StringValue; 52 53 /** 54 * Default implementation of the {@link NdbOpenJPAConfiguration} interface. 55 * This implementation extends the JDBCConfiguration so both access 56 * to MySQLd via JDBC and access to Ndb via ClusterJ are supported. 57 * Type safety: The return type Map for getCacheMarshallerInstances() 58 * from the type OpenJPAConfigurationImpl needs unchecked conversion 59 * to conform to Map<String,CacheMarshaller> from the type OpenJPAConfiguration 60 */ 61 @SuppressWarnings("unchecked") 62 public class NdbOpenJPAConfigurationImpl extends JDBCConfigurationImpl 63 implements NdbOpenJPAConfiguration, com.mysql.clusterj.Constants, DomainTypeHandlerFactory { 64 65 /** My message translator */ 66 static final I18NHelper local = I18NHelper.getInstance(NdbOpenJPAConfigurationImpl.class); 67 68 /** My logger */ 69 static final Logger logger = LoggerFactoryService.getFactory().getInstance(NdbOpenJPAConfigurationImpl.class); 70 71 public StringValue connectString; 72 public IntValue connectRetries; 73 public IntValue connectDelay; 74 public IntValue connectVerbose; 75 public IntValue connectTimeoutBefore; 76 public IntValue connectTimeoutAfter; 77 public StringValue username; 78 public StringValue password; 79 public StringValue database; 80 public IntValue maxTransactions; 81 public BooleanValue failOnJDBCPath; 82 public SessionFactoryImpl sessionFactory; 83 private final Map<Class<?>, NdbOpenJPADomainTypeHandlerImpl<?>> domainTypeHandlerMap = 84 new HashMap<Class<?>, NdbOpenJPADomainTypeHandlerImpl<?>>(); 85 86 /** 87 * These are to bridge an incompatibility between OpenJPA 1.x and 2.x in the handling of configuration 88 * values. In 1.x, the IntValue.get() method returns int and in 2.x it returns Integer. 89 * Similarly, in 1.x BooleanValue.get() returns boolean and in 2.x it returns Boolean. 90 */ 91 static private Method intValueMethod; 92 static private Method booleanValueMethod; 93 static { 94 try { 95 intValueMethod = IntValue.class.getMethod("get", (Class[])null); 96 booleanValueMethod = BooleanValue.class.getMethod("get", (Class[])null); 97 } catch (SecurityException e) { 98 throw new ClusterJFatalInternalException(e); 99 } catch (NoSuchMethodException e) { 100 throw new ClusterJFatalInternalException(e); 101 } 102 } 103 104 /** Return the int value from a configuration IntValue. 105 * 106 */ getIntValue(IntValue value)107 int getIntValue(IntValue value) { 108 try { 109 return (Integer)intValueMethod.invoke(value); 110 } catch (IllegalArgumentException e) { 111 throw new ClusterJFatalInternalException(e); 112 } catch (IllegalAccessException e) { 113 throw new ClusterJFatalInternalException(e); 114 } catch (InvocationTargetException e) { 115 throw new ClusterJFatalInternalException(e); 116 } 117 } 118 /** Return the boolean value from a configuration BooleanValue. 119 * 120 */ getBooleanValue(BooleanValue value)121 boolean getBooleanValue(BooleanValue value) { 122 try { 123 return (Boolean) booleanValueMethod.invoke(value); 124 } catch (IllegalArgumentException e) { 125 throw new ClusterJFatalInternalException(e); 126 } catch (IllegalAccessException e) { 127 throw new ClusterJFatalInternalException(e); 128 } catch (InvocationTargetException e) { 129 throw new ClusterJFatalInternalException(e); 130 } 131 } 132 /** 133 * Default constructor. Attempts to load default properties. 134 */ NdbOpenJPAConfigurationImpl()135 public NdbOpenJPAConfigurationImpl() { 136 this(true); 137 } 138 139 /** 140 * Constructor. 141 * 142 * @param loadGlobals whether to attempt to load the global properties 143 */ NdbOpenJPAConfigurationImpl(boolean loadGlobals)144 public NdbOpenJPAConfigurationImpl(boolean loadGlobals) { 145 this(true, loadGlobals); 146 } 147 148 /** 149 * Constructor. 150 * 151 * @param derivations whether to apply product derivations 152 * @param loadGlobals whether to attempt to load the global properties 153 */ NdbOpenJPAConfigurationImpl(boolean derivations, boolean loadGlobals)154 public NdbOpenJPAConfigurationImpl(boolean derivations, boolean loadGlobals) { 155 super(false, false); 156 157 connectString = addString("ndb.connectString"); 158 connectRetries = addInt("ndb.connectRetries"); 159 connectRetries.set(4); 160 connectDelay = addInt("ndb.connectDelay"); 161 connectDelay.set(5); 162 connectVerbose = addInt("ndb.connectVerbose"); 163 connectVerbose.set(0); 164 connectTimeoutBefore = addInt("ndb.connectTimeoutBefore"); 165 connectTimeoutBefore.set(30); 166 connectTimeoutAfter = addInt("ndb.connectTimeoutAfter"); 167 connectTimeoutAfter.set(20); 168 username = addString("ndb.username"); 169 password = addString("ndb.password"); 170 database = addString("ndb.database"); 171 database.set("test"); 172 maxTransactions = addInt("ndb.maxTransactions"); 173 maxTransactions.set(1024); 174 failOnJDBCPath = addBoolean("ndb.failOnJDBCPath"); 175 failOnJDBCPath.set(false); 176 177 sessionFactory = null; 178 179 if (derivations) 180 ProductDerivations.beforeConfigurationLoad(this); 181 if (loadGlobals) 182 loadGlobals(); 183 } 184 185 /** 186 * Copy constructor 187 */ NdbOpenJPAConfigurationImpl(NdbOpenJPAConfiguration conf)188 public NdbOpenJPAConfigurationImpl(NdbOpenJPAConfiguration conf) { 189 this(true, false); 190 if (conf != null) 191 fromProperties(conf.toProperties(false)); 192 } 193 getConnectString()194 public String getConnectString() { 195 return connectString.get(); 196 } 197 setConnectString(String value)198 public void setConnectString(String value) { 199 connectString.set(value); 200 } 201 getConnectRetries()202 public int getConnectRetries() { 203 return getIntValue(connectRetries); 204 } 205 setConnectRetries(int value)206 public void setConnectRetries(int value) { 207 connectRetries.set(value); 208 } 209 getConnectDelay()210 public int getConnectDelay() { 211 return getIntValue(connectDelay); 212 } 213 setConnectDelay(int value)214 public void setConnectDelay(int value) { 215 connectDelay.set(value); 216 } 217 getConnectVerbose()218 public int getConnectVerbose() { 219 return getIntValue(connectVerbose); 220 } 221 setConnectVerbose(int value)222 public void setConnectVerbose(int value) { 223 connectVerbose.set(value); 224 } 225 getConnectTimeoutBefore()226 public int getConnectTimeoutBefore() { 227 return getIntValue(connectTimeoutBefore); 228 } 229 setConnectTimeoutBefore(int value)230 public void setConnectTimeoutBefore(int value) { 231 connectTimeoutBefore.set(value); 232 } 233 getConnectTimeoutAfter()234 public int getConnectTimeoutAfter() { 235 return getIntValue(connectTimeoutAfter); 236 } 237 setConnectTimeoutAfter(int value)238 public void setConnectTimeoutAfter(int value) { 239 connectTimeoutAfter.set(value); 240 } 241 getUsername()242 public String getUsername() { 243 return username.getString(); 244 } 245 setUsername(String value)246 public void setUsername(String value) { 247 username.setString(value); 248 } 249 getPassword()250 public String getPassword() { 251 return password.getString(); 252 } 253 setPassword(String value)254 public void setPassword(String value) { 255 password.setString(value); 256 } 257 getDatabase()258 public String getDatabase() { 259 return database.getString(); 260 } 261 setDatabase(String value)262 public void setDatabase(String value) { 263 database.setString(value); 264 } 265 getMaxTransactions()266 public int getMaxTransactions() { 267 return getIntValue(maxTransactions); 268 } 269 setMaxTransactions(int value)270 public void setMaxTransactions(int value) { 271 maxTransactions.set(value); 272 } 273 getFailOnJDBCPath()274 public boolean getFailOnJDBCPath() { 275 return getBooleanValue(failOnJDBCPath); 276 } 277 setFailOnJDBCPath(boolean value)278 public void setFailOnJDBCPath(boolean value) { 279 failOnJDBCPath.set(value); 280 } 281 getSessionFactory()282 public SessionFactoryImpl getSessionFactory() { 283 if (sessionFactory == null) { 284 sessionFactory = createSessionFactory(); 285 } 286 return (SessionFactoryImpl) sessionFactory; 287 } 288 setSessionFactory(SessionFactory value)289 public void setSessionFactory(SessionFactory value) { 290 sessionFactory = (SessionFactoryImpl) value; 291 } 292 createSessionFactory()293 public SessionFactoryImpl createSessionFactory() { 294 // require connectString to be specified 295 if (connectString.get() == null) { 296 throw new IllegalStateException( 297 local.message("ERR_Missing_Connect_String")); 298 } 299 // map OpenJPA properties to ClusterJ properties 300 Properties props = new Properties(); 301 props.put(PROPERTY_CLUSTER_CONNECTSTRING, 302 connectString.get()); 303 props.put(PROPERTY_CLUSTER_CONNECT_RETRIES, 304 connectRetries.getString()); 305 props.put(PROPERTY_CLUSTER_CONNECT_DELAY, 306 connectDelay.getString()); 307 props.put(PROPERTY_CLUSTER_CONNECT_VERBOSE, 308 connectVerbose.getString()); 309 props.put(PROPERTY_CLUSTER_CONNECT_TIMEOUT_BEFORE, 310 connectTimeoutBefore.getString()); 311 props.put(PROPERTY_CLUSTER_CONNECT_TIMEOUT_AFTER, 312 connectTimeoutAfter.getString()); 313 props.put(PROPERTY_CLUSTER_DATABASE, 314 database.getString()); 315 props.put(PROPERTY_CLUSTER_MAX_TRANSACTIONS, 316 maxTransactions.getString()); 317 SessionFactoryImpl factory = (SessionFactoryImpl) 318 ClusterJHelper.getSessionFactory(props); 319 factory.setDomainTypeHandlerFactory(this); 320 return factory; 321 } 322 323 /** Get the domain type handler for this class mapping. A cached handler is 324 * returned if possible. Synchronize on the class-to-handler map. 325 * @param cmd the openjpa class mapping 326 * @return the domain type handler 327 */ getDomainTypeHandler( ClassMapping cmd, Dictionary dictionary)328 public NdbOpenJPADomainTypeHandlerImpl<?> getDomainTypeHandler( 329 ClassMapping cmd, Dictionary dictionary) { 330 NdbOpenJPADomainTypeHandlerImpl<?> result; 331 Class<?> domainClass = cmd.getDescribedType(); 332 synchronized(domainTypeHandlerMap) { 333 result = domainTypeHandlerMap.get(domainClass); 334 if (result == null) { 335 if (logger.isDebugEnabled()) logger.debug("domain class: " + domainClass.getName()); 336 result = createDomainTypeHandler(cmd, dictionary); 337 domainTypeHandlerMap.put(domainClass, result); 338 result.initializeRelations(); 339 logger.info("New domain type " + result.getName() 340 + (result.isSupportedType()?" is supported.": 341 " is not known to be supported because " + result.getReasons())); 342 } 343 } 344 return result; 345 } 346 347 /** Create a new domain type handler for the class mapping. 348 * 349 * @param classMapping the openjpa class mapping 350 * @return the domain type handler 351 */ createDomainTypeHandler( ClassMapping classMapping, Dictionary dictionary)352 private NdbOpenJPADomainTypeHandlerImpl<?> createDomainTypeHandler( 353 ClassMapping classMapping, Dictionary dictionary) { 354 return new NdbOpenJPADomainTypeHandlerImpl<Object>(dictionary, classMapping, this); 355 } 356 357 /** 358 * Free the data sources. 359 */ 360 @Override preClose()361 protected void preClose() { 362 if (sessionFactory != null) { 363 sessionFactory.close(); 364 } 365 super.preClose(); 366 } 367 368 @Override isInvalidProperty(String propName)369 protected boolean isInvalidProperty(String propName) { 370 if (super.isInvalidProperty(propName)) 371 return true; 372 373 // handle openjpa.ndb.SomeMisspelledProperty, but not 374 // openjpa.someotherimplementation.SomeProperty 375 String lowerCasePropName = propName.toLowerCase(); 376 String[] prefixes = ProductDerivations.getConfigurationPrefixes(); 377 for (int i = 0; i < prefixes.length; i++) 378 if (lowerCasePropName.startsWith(prefixes[i] + ".ndb")) 379 return true; 380 return false; 381 } 382 383 /** Get the domain type handler for the class. This method is called by the query 384 * handler when performing a clusterj query for an openjpa entity. The class 385 * must have already been registered via the openjpa clusterj path. 386 */ createDomainTypeHandler( Class<T> domainClass, Dictionary dictionary)387 public <T> DomainTypeHandler<T> createDomainTypeHandler( 388 Class<T> domainClass, Dictionary dictionary) { 389 DomainTypeHandler<T> result = (DomainTypeHandler<T>) domainTypeHandlerMap.get(domainClass); 390 if (result == null) { 391 throw new ClusterJFatalInternalException( 392 local.message("ERR_Create_Domain_Type_Handler_First", domainClass.getName())); 393 } 394 return result; 395 } 396 397 } 398