MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
DomainTypeHandlerImpl.java
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 as published by
6  the Free Software Foundation; version 2 of the License.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17 
18 package com.mysql.clusterj.core.metadata;
19 
20 import com.mysql.clusterj.core.spi.DomainFieldHandler;
21 import com.mysql.clusterj.core.spi.ValueHandler;
22 import com.mysql.clusterj.ClusterJException;
23 import com.mysql.clusterj.ClusterJFatalInternalException;
24 import com.mysql.clusterj.ClusterJUserException;
25 import com.mysql.clusterj.DynamicObject;
26 import com.mysql.clusterj.ColumnMetadata;
27 import com.mysql.clusterj.DynamicObjectDelegate;
28 
29 import com.mysql.clusterj.annotation.PersistenceCapable;
30 
31 import com.mysql.clusterj.core.CacheManager;
32 
33 import com.mysql.clusterj.core.store.Column;
34 import com.mysql.clusterj.core.store.Index;
35 import com.mysql.clusterj.core.store.Dictionary;
36 import com.mysql.clusterj.core.store.Operation;
37 
38 
39 
40 import java.lang.reflect.Constructor;
41 import java.lang.reflect.InvocationHandler;
42 import java.lang.reflect.InvocationTargetException;
43 import java.lang.reflect.Method;
44 import java.lang.reflect.Proxy;
45 
46 import java.util.ArrayList;
47 import java.util.HashMap;
48 import java.util.HashSet;
49 import java.util.List;
50 import java.util.Map;
51 import java.util.Set;
52 
60 public class DomainTypeHandlerImpl<T> extends AbstractDomainTypeHandlerImpl<T> {
61 
63  Class<T> cls;
64 
66  boolean dynamic = false;
67 
69  private Map<String, Method> unmatchedGetMethods = new HashMap<String, Method>();
70  private Map<String, Method> unmatchedSetMethods = new HashMap<String, Method>();
71 
73  protected Class<T> proxyClass;
74 
76  Constructor<T> ctor;
77 
79  PersistenceCapable persistenceCapable;
80 
82  protected static final Class<?>[] invocationHandlerClassArray =
83  new Class[]{InvocationHandler.class};
84 
91  @SuppressWarnings( "unchecked" )
92  public DomainTypeHandlerImpl(Class<T> cls, Dictionary dictionary) {
93  this.cls = cls;
94  this.name = cls.getName();
95  this.dynamic = DynamicObject.class.isAssignableFrom(cls);
96  if (dynamic) {
97  // Dynamic object has a handler but no proxy
98  this.tableName = getTableNameForDynamicObject((Class<DynamicObject>)cls);
99  } else {
100  // Create a proxy class for the domain class
101  proxyClass = (Class<T>)Proxy.getProxyClass(
102  cls.getClassLoader(), new Class[]{cls});
103  ctor = getConstructorForInvocationHandler (proxyClass);
104  persistenceCapable = cls.getAnnotation(PersistenceCapable.class);
105  if (persistenceCapable == null) {
106  throw new ClusterJUserException(local.message(
107  "ERR_No_Persistence_Capable_Annotation", name));
108  }
109  this.tableName = persistenceCapable.table();
110  }
111  this.table = getTable(dictionary);
112  if (table == null) {
113  throw new ClusterJUserException(local.message("ERR_Get_NdbTable", name, tableName));
114  }
115  if (logger.isDebugEnabled()) logger.debug("Found Table for " + tableName);
116 
117  // the id field handlers will be initialized via registerPrimaryKeyColumn
118  this.primaryKeyColumnNames = table.getPrimaryKeyColumnNames();
119  this.numberOfIdFields = primaryKeyColumnNames.length;
120  this.idFieldHandlers = new DomainFieldHandlerImpl[numberOfIdFields];
121  this.idFieldNumbers = new int[numberOfIdFields];
122 
123  // the partition key field handlers will be initialized via registerPrimaryKeyColumn
124  this.partitionKeyColumnNames = table.getPartitionKeyColumnNames();
125  this.numberOfPartitionKeyColumns = partitionKeyColumnNames.length;
126  this.partitionKeyFieldHandlers = new DomainFieldHandlerImpl[numberOfPartitionKeyColumns];
127 
128  // Process indexes for the table. There might not be a field associated with the index.
129  // The first entry in indexHandlerImpls is for the mandatory hash primary key,
130  // which is not really an index but is treated as an index by query.
131  Index primaryIndex = dictionary.getIndex("PRIMARY$KEY", tableName, "PRIMARY");
132  IndexHandlerImpl primaryIndexHandler =
133  new IndexHandlerImpl(this, dictionary, primaryIndex, primaryKeyColumnNames);
134  indexHandlerImpls.add(primaryIndexHandler);
135 
136  String[] indexNames = table.getIndexNames();
137  for (String indexName: indexNames) {
138  // the index alias is the name as known by the user (without the $unique suffix)
139  String indexAlias = removeUniqueSuffix(indexName);
140  Index index = dictionary.getIndex(indexName, tableName, indexAlias);
141  String[] columnNames = index.getColumnNames();
142  IndexHandlerImpl imd = new IndexHandlerImpl(this, dictionary, index, columnNames);
143  indexHandlerImpls.add(imd);
144  }
145 
146  if (dynamic) {
147  // for each column in the database, create a field
148  List<String> fieldNameList = new ArrayList<String>();
149  for (String columnName: table.getColumnNames()) {
150  Column storeColumn = table.getColumn(columnName);
151  DomainFieldHandlerImpl domainFieldHandler = null;
152  domainFieldHandler =
153  new DomainFieldHandlerImpl(this, table, numberOfFields++, storeColumn);
154  String fieldName = domainFieldHandler.getName();
155  fieldNameList.add(fieldName);
156  fieldNameToNumber.put(domainFieldHandler.getName(), domainFieldHandler.getFieldNumber());
157  persistentFieldHandlers.add(domainFieldHandler);
158  if (!storeColumn.isPrimaryKey()) {
159  nonPKFieldHandlers.add(domainFieldHandler);
160  }
161  }
162  fieldNames = fieldNameList.toArray(new String[fieldNameList.size()]);
163  } else {
164  // Iterate the fields (names and types based on get/set methods) in the class
165  List<String> fieldNameList = new ArrayList<String>();
166  Method[] methods = cls.getMethods();
167  for (Method method: methods) {
168  // remember get methods
169  String methodName = method.getName();
170  String name = convertMethodName(methodName);
171  Class type = getType(method);
172  DomainFieldHandlerImpl domainFieldHandler = null;
173  if (methodName.startsWith("get")) {
174  Method unmatched = unmatchedSetMethods.get(name);
175  if (unmatched == null) {
176  // get is first of the pair; put it into the unmatched map
177  unmatchedGetMethods.put(name, method);
178  } else {
179  // found the potential match
180  if (getType(unmatched).equals(type)) {
181  // method names and types match
182  unmatchedSetMethods.remove(name);
183  domainFieldHandler = new DomainFieldHandlerImpl(this, table,
184  numberOfFields++, name, type, method, unmatched);
185  } else {
186  // both unmatched because of type mismatch
187  unmatchedGetMethods.put(name, method);
188  }
189  }
190  } else if (methodName.startsWith("set")) {
191  Method unmatched = unmatchedGetMethods.get(name);
192  if (unmatched == null) {
193  // set is first of the pair; put it into the unmatched map
194  unmatchedSetMethods.put(name, method);
195  } else {
196  // found the potential match
197  if (getType(unmatched).equals(type)) {
198  // method names and types match
199  unmatchedGetMethods.remove(name);
200  domainFieldHandler = new DomainFieldHandlerImpl(this, table,
201  numberOfFields++, name, type, unmatched, method);
202  } else {
203  // both unmatched because of type mismatch
204  unmatchedSetMethods.put(name, method);
205  }
206  }
207  }
208  if (domainFieldHandler != null) {
209  // found matching methods
210  // set up field name to number map
211  String fieldName = domainFieldHandler.getName();
212  fieldNameList.add(fieldName);
213  fieldNameToNumber.put(domainFieldHandler.getName(), domainFieldHandler.getFieldNumber());
214  // put field into either persistent or not persistent list
215  if (domainFieldHandler.isPersistent()) {
216  persistentFieldHandlers.add(domainFieldHandler);
217  if (!domainFieldHandler.isPrimaryKey()) {
218  nonPKFieldHandlers.add(domainFieldHandler);
219  }
220  }
221  if (domainFieldHandler.isPrimitive()) {
222  primitiveFieldHandlers.add(domainFieldHandler);
223  }
224  }
225  }
226  fieldNames = fieldNameList.toArray(new String[fieldNameList.size()]);
227  // done with methods; if anything in unmatched we have a problem
228  if ((!unmatchedGetMethods.isEmpty()) || (!unmatchedSetMethods.isEmpty())) {
229  throw new ClusterJUserException(
230  local.message("ERR_Unmatched_Methods",
231  unmatchedGetMethods, unmatchedSetMethods));
232  }
233 
234  }
235  // Check that all index columnNames have corresponding fields
236  // indexes without fields will be unusable for query
237  for (IndexHandlerImpl indexHandler:indexHandlerImpls) {
238  indexHandler.assertAllColumnsHaveFields();
239  }
240 
241  if (logger.isDebugEnabled()) {
242  logger.debug(toString());
243  logger.debug("DomainTypeHandlerImpl " + name + "Indices " + indexHandlerImpls);
244  }
245  }
246 
247  protected <O extends DynamicObject> String getTableNameForDynamicObject(Class<O> cls) {
248  DynamicObject dynamicObject;
249  PersistenceCapable persistenceCapable = cls.getAnnotation(PersistenceCapable.class);
250  String tableName = null;
251  try {
252  dynamicObject = cls.newInstance();
253  tableName = dynamicObject.table();
254  if (tableName == null && persistenceCapable != null) {
255  tableName = persistenceCapable.table();
256  }
257  } catch (InstantiationException e) {
258  throw new ClusterJUserException(local.message("ERR_Dynamic_Object_Instantiation", cls.getName()), e);
259  } catch (IllegalAccessException e) {
260  throw new ClusterJUserException(local.message("ERR_Dynamic_Object_Illegal_Access", cls.getName()), e);
261  }
262  if (tableName == null) {
263  throw new ClusterJUserException(local.message("ERR_Dynamic_Object_Null_Table_Name",
264  cls.getName()));
265  }
266  return tableName;
267  }
268 
270  public boolean isSupportedType() {
271  // if unsupported, throw an exception
272  return true;
273  }
274 
275  public ValueHandler getValueHandler(Object instance)
276  throws IllegalArgumentException {
277  if (instance instanceof ValueHandler) {
278  return (ValueHandler)instance;
279  } else if (instance instanceof DynamicObject) {
280  return (ValueHandler)((DynamicObject)instance).delegate();
281  } else {
282  ValueHandler handler = (ValueHandler)
283  Proxy.getInvocationHandler(instance);
284  return handler;
285  }
286  }
287 
288  public void objectMarkModified(ValueHandler handler, String fieldName) {
289  int fieldNumber = fieldNameToNumber.get(fieldName);
290  handler.markModified(fieldNumber);
291  }
292 
293  public Class<T> getProxyClass() {
294  return proxyClass;
295  }
296 
297  public Class<T> getDomainClass() {
298  return cls;
299  }
300 
301  public void operationSetValues(Object instance, Operation op) {
302  ValueHandler handler = getValueHandler(instance);
303  for (DomainFieldHandler fmd: persistentFieldHandlers) {
304  fmd.operationSetValue(handler, op);
305  }
306  }
307 
308  public void objectSetKeys(Object keys, Object instance) {
309  ValueHandler handler = getValueHandler(instance);
310  int size = idFieldHandlers.length;
311  if (size == 1) {
312  // single primary key; store value in key field
313  for (DomainFieldHandler fmd: idFieldHandlers) {
314  fmd.objectSetKeyValue(keys, handler);
315  }
316  } else if (keys instanceof java.lang.Object[]) {
317  if (logger.isDetailEnabled()) logger.detail(keys.toString());
318  // composite primary key; store values in key fields
319  for (int i = 0; i < idFieldHandlers.length; ++i) {
320  idFieldHandlers[i].objectSetKeyValue(((Object[])keys)[i], handler);
321  }
322  } else {
323  // composite key but parameter is not Object[]
324  throw new ClusterJUserException(
325  local.message("ERR_Composite_Key_Parameter"));
326  }
327  }
328 
329  public void objectResetModified(ValueHandler handler) {
330  handler.resetModified();
331  }
332 
333  @SuppressWarnings("unchecked")
334  public void objectSetCacheManager(CacheManager cm, Object instance) {
335  InvocationHandlerImpl<T> handler =
336  (InvocationHandlerImpl<T>)getValueHandler(instance);
337  handler.setCacheManager(cm);
338  }
339 
340  public T newInstance() {
341  T instance;
342  try {
343  InvocationHandlerImpl<T> handler = new InvocationHandlerImpl<T>(this);
344  if (dynamic) {
345  instance = cls.newInstance();
346  ((DynamicObject)instance).delegate((DynamicObjectDelegate)handler);
347  } else {
348  instance = ctor.newInstance(new Object[] {handler});
349  handler.setProxy(instance);
350  }
351  return instance;
352  } catch (InstantiationException ex) {
353  throw new ClusterJException(
354  local.message("ERR_Create_Instance", cls.getName()), ex);
355  } catch (IllegalAccessException ex) {
356  throw new ClusterJException(
357  local.message("ERR_Create_Instance", cls.getName()), ex);
358  } catch (IllegalArgumentException ex) {
359  throw new ClusterJException(
360  local.message("ERR_Create_Instance", cls.getName()), ex);
361  } catch (InvocationTargetException ex) {
362  throw new ClusterJException(
363  local.message("ERR_Create_Instance", cls.getName()), ex);
364  } catch (SecurityException ex) {
365  throw new ClusterJException(
366  local.message("ERR_Create_Instance", cls.getName()), ex);
367  }
368  }
369 
370 
371  public void initializeNotPersistentFields(InvocationHandlerImpl<T> handler) {
372  for (DomainFieldHandler fmd:primitiveFieldHandlers) {
373  ((AbstractDomainFieldHandlerImpl) fmd).objectSetDefaultValue(handler);
374  }
375  }
376 
383  private String convertMethodName(String methodName) {
384  String head = methodName.substring(3, 4).toLowerCase();
385  String tail = methodName.substring(4);
386  return head + tail;
387  }
388 
389  @SuppressWarnings( "unchecked" )
390  public T getInstance(ValueHandler handler) {
391  return (T)((InvocationHandlerImpl)handler).getProxy();
392  }
393 
394  private Class<?> getType(Method method) {
395  Class<?> result = null;
396  if (method.getName().startsWith("get")) {
397  result = method.getReturnType();
398  } else if (method.getName().startsWith("set")) {
399  Class<?>[] types = method.getParameterTypes();
400  if (types.length != 1) {
401  throw new ClusterJUserException(
402  local.message("ERR_Set_Method_Parameters",
403  method.getName(), types.length));
404  }
405  result = types[0];
406  } else {
407  throw new ClusterJFatalInternalException(
408  local.message("ERR_Method_Name", method.getName()));
409  }
410  if (result == null) {
411  throw new ClusterJUserException(
412  local.message("ERR_Unmatched_Method" + method.getName()));
413  }
414  return result;
415  }
416 
418  protected Constructor<T> getConstructorForInvocationHandler(
419  Class<T> cls) {
420  try {
421  return cls.getConstructor(invocationHandlerClassArray);
422  } catch (NoSuchMethodException ex) {
424  local.message("ERR_Get_Constructor", cls), ex);
425  } catch (SecurityException ex) {
427  local.message("ERR_Get_Constructor", cls), ex);
428  }
429  }
430 
431  public ValueHandler createKeyValueHandler(Object keys) {
432  if (keys == null) {
433  throw new ClusterJUserException(
434  local.message("ERR_Key_Must_Not_Be_Null", getName(), "unknown"));
435  }
436  Object[] keyValues = new Object[numberOfFields];
437  // check the cardinality of the keys with the number of key fields
438  if (numberOfIdFields == 1) {
439  Class<?> keyType = idFieldHandlers[0].getType();
440  DomainFieldHandler fmd = idFieldHandlers[0];
441  checkKeyType(fmd.getName(), keyType, keys);
442  int keyFieldNumber = fmd.getFieldNumber();
443  keyValues[keyFieldNumber] = keys;
444  } else {
445  if (!(keys.getClass().isArray())) {
446  throw new ClusterJUserException(
447  local.message("ERR_Key_Must_Be_An_Object_Array",
448  numberOfIdFields));
449  }
450  Object[]keyObjects = (Object[])keys;
451  for (int i = 0; i < numberOfIdFields; ++i) {
452  DomainFieldHandler fmd = idFieldHandlers[i];
453  int index = fmd.getFieldNumber();
454  Object keyObject = keyObjects[i];
455  Class<?> keyType = fmd.getType();
456  checkKeyType(fmd.getName(), keyType, keyObject);
457  keyValues[index] = keyObjects[i];
458  }
459  }
460  return new KeyValueHandlerImpl(keyValues);
461  }
462 
471  public void checkKeyType(String name, Class<?> keyType, Object keys)
472  throws ClusterJUserException {
473  if (keys == null) {
474  return;
475  }
476  Class<?> valueType = keys.getClass();
477  if (keyType.isAssignableFrom(valueType) ||
478  (keyType == int.class && valueType == Integer.class) ||
479  (keyType == Integer.class & valueType == int.class) ||
480  (keyType == Long.class & valueType == long.class) ||
481  (keyType == long.class & valueType == Long.class)) {
482  return;
483  } else {
484  throw new ClusterJUserException(
485  local.message("ERR_Incorrect_Key_Type",
486  name, valueType.getName(), keyType.getName()));
487  }
488  }
489 
490  public Class<?> getOidClass() {
491  throw new ClusterJFatalInternalException(local.message("ERR_Implementation_Should_Not_Occur"));
492  }
493 
494  protected ColumnMetadata[] columnMetadata() {
495  ColumnMetadata[] result = new ColumnMetadata[numberOfFields];
496  return persistentFieldHandlers.toArray(result);
497  }
498 
499 }