View Javadoc
1   package org.synchronoss.cpo.core.meta;
2   
3   /*-
4    * [[
5    * core
6    * ==
7    * Copyright (C) 2003 - 2026 Exaxis LLC, Synchronoss Technologies Inc
8    * ==
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation, either version 3 of the
12   * License, or (at your option) any later version.
13   *
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Lesser Public License for more details.
18   *
19   * You should have received a copy of the GNU General Lesser Public
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22   * ]]
23   */
24  
25  import java.lang.reflect.InvocationTargetException;
26  import org.synchronoss.cpo.core.CpoException;
27  import org.synchronoss.cpo.core.meta.domain.CpoAttribute;
28  
29  /**
30   * Base {@link org.synchronoss.cpo.core.CpoData} implementation for reading a bound attribute's
31   * value out of a datastore result row (e.g. a JDBC {@code ResultSet} or Cassandra {@code Row}).
32   * Resolves the appropriate {@link MethodMapEntry} for the attribute's data type on first use and
33   * caches it, since it cannot change across invocations of the same bound instance; datastore
34   * implementations supply the actual reflective invocation via {@link #invokeGetterImpl}.
35   *
36   * @author dberry
37   */
38  public abstract class ResultSetCpoData extends AbstractBindableCpoData {
39  
40    private Object rs = null;
41    MethodMapper<?> methodMapper;
42    // resolved on first use; the attribute's data type never changes, so with one instance
43    // per column the mapper lookup happens once per query instead of once per cell
44    private MethodMapEntry<?, ?> resolvedMethodMapEntry = null;
45  
46    /**
47     * Creates an instance bound to a single column/attribute of the given result row.
48     *
49     * @param methodMapper the mapper used to resolve the getter method for the attribute's data type
50     * @param rs the datastore-specific result row object (e.g. {@code ResultSet} or {@code Row})
51     * @param cpoAttribute the attribute this instance reads
52     * @param index the positional index (e.g. column index) of this binding
53     */
54    public ResultSetCpoData(
55        MethodMapper<?> methodMapper, Object rs, CpoAttribute cpoAttribute, int index) {
56      super(cpoAttribute, index);
57      this.methodMapper = methodMapper;
58      this.rs = rs;
59    }
60  
61    /**
62     * Gets the datastore-specific result row object this instance reads from.
63     *
64     * @return the result row object (e.g. {@code ResultSet} or {@code Row})
65     */
66    public Object getRs() {
67      return rs;
68    }
69  
70    /**
71     * Invokes the datastore-specific getter method to read the raw value for the given attribute.
72     *
73     * @param cpoAttribute the attribute being read
74     * @param methodMapEntry the resolved method map entry for the attribute's data type
75     * @return the raw value read from the result row, before {@code transformIn} is applied
76     * @throws IllegalAccessException if the getter method cannot be accessed
77     * @throws InvocationTargetException if the getter method throws
78     */
79    protected abstract Object invokeGetterImpl(
80        CpoAttribute cpoAttribute, MethodMapEntry<?, ?> methodMapEntry)
81        throws IllegalAccessException, InvocationTargetException;
82  
83    /**
84     * {@inheritDoc}
85     *
86     * <p>Resolves (and caches) the {@link MethodMapEntry} for the attribute's data getter return
87     * type, invokes the datastore-specific getter via {@link #invokeGetterImpl}, and applies the
88     * attribute's transform.
89     */
90    @Override
91    public Object invokeGetter() throws CpoException {
92      Object javaObject;
93      MethodMapEntry<?, ?> methodMapEntry = resolvedMethodMapEntry;
94      if (methodMapEntry == null) {
95        methodMapEntry = methodMapper.getDataMethodMapEntry(getDataGetterReturnType());
96        if (methodMapEntry == null) {
97          if (Object.class.isAssignableFrom(getDataGetterReturnType())) {
98            methodMapEntry = methodMapper.getDataMethodMapEntry(Object.class);
99          }
100         if (methodMapEntry == null) {
101           throw new CpoException(
102               "Error Retrieving Jdbc Method for type: " + getDataGetterReturnType().getName());
103         }
104       }
105       resolvedMethodMapEntry = methodMapEntry;
106     }
107 
108     try {
109       // Get the getter for the Callable Statement
110       javaObject = transformIn(invokeGetterImpl(getCpoAttribute(), methodMapEntry));
111     } catch (IllegalAccessException iae) {
112       throw new CpoException("Error Invoking ResultSet Method: ", iae);
113     } catch (InvocationTargetException ite) {
114       throw new CpoException("Error Invoking ResultSet Method: ", ite);
115     }
116 
117     return javaObject;
118   }
119 }