]> review.fuel-infra Code Review - packages/trusty/mysql-wsrep-5.6.git/blob
751e342f2999d58997beec39be799c57240f80ce
[packages/trusty/mysql-wsrep-5.6.git] /
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
53 /** This instance manages a persistence-capable type.
54  * Currently, only interfaces can be persistence-capable. Persistent
55  * properties consist of a pair of bean-pattern methods for which the
56  * get method returns the same type as the parameter of the 
57  * similarly-named set method.
58  * @param T the class of the persistence-capable type
59  */
60 public class DomainTypeHandlerImpl<T> extends AbstractDomainTypeHandlerImpl<T> {
61
62     /** The domain class. */
63     Class<T> cls;
64
65     /** Dynamic class indicator */
66     boolean dynamic = false;
67
68     /** The methods of the properties. */
69     private Map<String, Method> unmatchedGetMethods = new HashMap<String, Method>();
70     private Map<String, Method> unmatchedSetMethods = new HashMap<String, Method>();
71
72     /** The Proxy class for the Domain Class. */
73     protected Class<T> proxyClass;
74
75     /** The constructor for the Proxy class. */
76     Constructor<T> ctor;
77
78     /** The PersistenceCapable annotation for this class. */
79     PersistenceCapable persistenceCapable;
80
81     /** Helper parameter for constructor. */
82     protected static final Class<?>[] invocationHandlerClassArray = 
83             new Class[]{InvocationHandler.class};
84
85     /** Initialize DomainTypeHandler for a class.
86      * 
87      * @param cls the domain class (this is the only class 
88      * known to the rest of the implementation)
89      * @param dictionary NdbDictionary instance used for metadata access
90      */
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
269     /** Is this type supported? */
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
377     /** Convert a method name to a javabeans property name.
378      * This is done by removing the leading "get" or "set" and upper-casing the
379      * result.
380      * @param methodName the name of a get or set method
381      * @return the property name
382      */
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
417     /** TODO: Protect with doPrivileged. */
418     protected Constructor<T> getConstructorForInvocationHandler(
419             Class<T> cls) {
420         try {
421             return cls.getConstructor(invocationHandlerClassArray);
422         } catch (NoSuchMethodException ex) {
423             throw new ClusterJFatalInternalException(
424                     local.message("ERR_Get_Constructor", cls), ex);
425         } catch (SecurityException ex) {
426             throw new ClusterJFatalInternalException(
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
463     /** Check that the key value matches the key type. Keys that are part
464      * of a compound key can be null as long as they are not part of the
465      * partition key.
466      *
467      * @param name the name of the field
468      * @param keyType the type of the key field
469      * @param keys the value for the key field
470      */
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 }