View Javadoc
1   package org.synchronoss.cpo.cassandra;
2   
3   /*-
4    * [[
5    * cassandra
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 com.datastax.oss.driver.api.core.CqlSession;
26  import com.datastax.oss.driver.api.core.cql.BoundStatement;
27  import com.datastax.oss.driver.api.core.cql.PreparedStatement;
28  import java.util.Collection;
29  import java.util.List;
30  import org.slf4j.Logger;
31  import org.slf4j.LoggerFactory;
32  import org.synchronoss.cpo.cassandra.meta.CassandraMethodMapper;
33  import org.synchronoss.cpo.core.*;
34  import org.synchronoss.cpo.core.helper.ExceptionHelper;
35  import org.synchronoss.cpo.core.meta.MethodMapper;
36  import org.synchronoss.cpo.core.meta.domain.CpoAttribute;
37  import org.synchronoss.cpo.core.meta.domain.CpoClass;
38  import org.synchronoss.cpo.core.meta.domain.CpoFunction;
39  
40  /**
41   * CassandraBoundStatementFactory is the object that encapsulates the creation of the actual
42   * Cassandra {@link BoundStatement} used to execute a CpoFunction against the datastax driver.
43   *
44   * @author david berry
45   */
46  public class CassandraBoundStatementFactory extends CpoStatementFactory implements CpoReleasable {
47  
48    /** Version Id for this class. */
49    private static final long serialVersionUID = 1L;
50  
51    private static final Logger logger =
52        LoggerFactory.getLogger(CassandraBoundStatementFactory.class);
53    private final PreparedStatement preparedStatement;
54    private BoundStatement boundStatement;
55  
56    /**
57     * Used to build the Cassandra {@link BoundStatement} that CPO executes for a given CpoFunction.
58     * The constructor is called by the internal CPO framework. This is not to be used by users of
59     * CPO. Programmers that build Transforms may need to use this object to get access to the actual
60     * bound statement.
61     *
62     * @param <T> The type of the object being bound
63     * @param sess The Cassandra session that will be used to prepare and bind the statement.
64     * @param cassandraCpoAdapter The CassandraCpoAdapter that is controlling this transaction
65     * @param criteria The object that will be used to look up the cpo metadata
66     * @param function The CpoFunction that is being executed
67     * @param bean The bean that is being acted upon
68     * @param wheres A collection of wheres to find the object
69     * @param orderBy A collection of orderbys to sort the objects
70     * @param nativeQueries Additional CQL to be embedded into the CpoFunction CQL that is used to
71     *     create the actual Cassandra BoundStatement
72     * @throws CpoException if a CPO error occurs
73     */
74    public <T> CassandraBoundStatementFactory(
75        CqlSession sess,
76        CassandraCpoAdapter cassandraCpoAdapter,
77        CpoClass criteria,
78        CpoFunction function,
79        T bean,
80        Collection<CpoWhere> wheres,
81        Collection<CpoOrderBy> orderBy,
82        Collection<CpoNativeFunction> nativeQueries)
83        throws CpoException {
84      super(bean == null ? logger : LoggerFactory.getLogger(bean.getClass()));
85      // get the list of bindValues from the function parameters
86      List<BindAttribute> bindValues = getBindValues(function, bean);
87  
88      String sql =
89          buildSql(criteria, function.getExpression(), wheres, orderBy, nativeQueries, bindValues);
90  
91      getLocalLogger().debug("CpoFunction SQL = <" + sql + ">");
92      try {
93        preparedStatement = sess.prepare(sql);
94        setBindValues(bindValues);
95        boundStatement = boundStatement.setPageSize(cassandraCpoAdapter.getFetchSize());
96      } catch (Throwable t) {
97        getLocalLogger()
98            .error(
99                "Error Instantiating CassandraBoundStatementFactory SQL=<"
100                   + sql
101                   + ">"
102                   + ExceptionHelper.getLocalizedMessage(t));
103       throw new CpoException(t);
104     }
105   }
106 
107   @Override
108   protected MethodMapper getMethodMapper() {
109     return CassandraMethodMapper.getMethodMapper();
110   }
111 
112   /**
113    * Binds every value in a single call, rather than one call per bind variable. Overridden from
114    * {@link CpoStatementFactory#setBindValues} because driver 4.x's {@code BoundStatement} is
115    * immutable -- every individual {@code setXxx(index, value)} call returns a new instance instead
116    * of mutating in place -- so building the full value array once and calling {@link
117    * PreparedStatement#bind(Object...)} is both simpler and avoids that pitfall entirely.
118    *
119    * @param bindValues the bind values to apply to the underlying statement, in parameter order;
120    *     {@code null} is treated as no bind values
121    * @throws CpoException if a value could not be resolved for binding
122    */
123   @Override
124   public void setBindValues(Collection<BindAttribute> bindValues) throws CpoException {
125     Object[] values = new Object[bindValues == null ? 0 : bindValues.size()];
126 
127     if (bindValues != null) {
128       int i = 0;
129       for (BindAttribute bindAttr : bindValues) {
130         Object bindObject = bindAttr.bindObject();
131         CpoAttribute cpoAttribute = bindAttr.cpoAttribute();
132 
133         if (getMethodMapper().getDataMethodMapEntry(bindObject.getClass()) != null) {
134           // a raw datastore-typed literal (e.g. a dynamic where-clause value): bind as-is
135           getLocalLogger()
136               .debug(
137                   "{}={}",
138                   cpoAttribute == null ? bindAttr.name() : cpoAttribute.getDataName(),
139                   bindObject);
140           values[i] = bindObject;
141         } else {
142           // bindObject is the bean; extract and transform the attribute's value
143           CpoData cpoData = getCpoData(cpoAttribute, i);
144           Object param = cpoData.transformOut(cpoAttribute.invokeGetter(bindObject));
145           getLocalLogger().debug("{}={}", cpoAttribute.getDataName(), param);
146           values[i] = param;
147         }
148         i++;
149       }
150     }
151 
152     boundStatement = preparedStatement.bind(values);
153   }
154 
155   @Override
156   protected CpoData getCpoData(CpoAttribute cpoAttribute, int index) {
157     return new CassandraBoundStatementCpoData(this, cpoAttribute, index);
158   }
159 
160   @Override
161   protected Object getBindableStatement() {
162     return getBoundStatement();
163   }
164 
165   @Override
166   protected int getStartingIndex() {
167     return 0;
168   }
169 
170   /**
171    * Gets the BoundStatent associated with this factory
172    *
173    * @return The BoundStatement
174    */
175   public BoundStatement getBoundStatement() {
176     return boundStatement;
177   }
178 
179   /**
180    * Replaces the BoundStatement held by this factory. Driver 4.x's BoundStatement is immutable:
181    * every {@code setXxx(index, value)} call returns a new instance rather than mutating in place,
182    * so callers that individually adjust a single bound value (e.g. paging) must write the result
183    * back here.
184    *
185    * @param boundStatement The new BoundStatement instance
186    */
187   void setBoundStatement(BoundStatement boundStatement) {
188     this.boundStatement = boundStatement;
189   }
190 }