View Javadoc
1   /*
2    * The contents of this file are subject to the terms of the Common Development and
3    * Distribution License (the License). You may not use this file except in compliance with the
4    * License.
5    *
6    * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7    * specific language governing permission and limitations under the License.
8    *
9    * When distributing Covered Software, include this CDDL Header Notice in each file and include
10   * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11   * Header, with the fields enclosed by brackets [] replaced by your own identifying
12   * information: "Portions copyright [year] [name of copyright owner]".
13   *
14   * Copyright 2015 ForgeRock AS.
15   */
16  package org.forgerock.audit.handlers.jdbc;
17  
18  import static org.forgerock.util.Utils.joinAsString;
19  
20  import java.util.Collection;
21  import java.util.Collections;
22  import java.util.LinkedList;
23  import java.util.List;
24  import java.util.Map;
25  
26  import org.forgerock.audit.AuditException;
27  import org.forgerock.audit.events.AuditEventHelper;
28  import org.forgerock.audit.handlers.jdbc.Parameter.Type;
29  import org.forgerock.json.JsonPointer;
30  import org.forgerock.json.JsonValue;
31  import org.forgerock.json.resource.QueryRequest;
32  import org.forgerock.json.resource.ResourceException;
33  import org.forgerock.util.Utils;
34  import org.slf4j.Logger;
35  import org.slf4j.LoggerFactory;
36  
37  /**
38   * Contains builds generic read and create events supported by multiple databases.
39   */
40  abstract class BaseDatabaseStatementProvider implements DatabaseStatementProvider {
41  
42      private static final Logger logger = LoggerFactory.getLogger(BaseDatabaseStatementProvider.class);
43  
44      /**
45       * {@inheritDoc}
46       */
47      @Override
48      public JdbcAuditEvent buildReadEvent(final TableMapping mapping, final String id,
49              final JsonValue eventTopicMetaData) throws AuditException {
50          final String idTableColumn = mapping.getFieldToColumn().get("_id");
51  
52          // build the read sql statement
53          String selectStatement = String.format("SELECT * FROM %s WHERE %s = ?", mapping.getTable(), idTableColumn);
54  
55          logger.info("Built select statement: {}", selectStatement);
56          final JdbcAuditEvent jdbcAuditEvent =
57                  new JdbcAuditEvent(
58                          selectStatement,
59                          Collections.singletonList(
60                                  new Parameter(
61                                          getParameterType(eventTopicMetaData, new JsonPointer("_id")),
62                                          id)));
63          return jdbcAuditEvent;
64      }
65  
66      /**
67       * {@inheritDoc}
68       */
69      @Override
70      public JdbcAuditEvent buildCreateEvent(final JsonValue content, final TableMapping tableMapping,
71              final JsonValue eventTopicMetaData) throws AuditException {
72          final Map<String, String> fieldToColumn = tableMapping.getFieldToColumn();
73  
74          String columns = joinAsString(", ", fieldToColumn.values());
75          String replacementTokens = joinAsString(", ", createReplacementTokens(fieldToColumn.keySet()));
76          String insertStatement = String.format("INSERT INTO %s ( %s ) VALUES ( %s )",
77                  tableMapping.getTable(), columns, replacementTokens);
78          logger.info("Built insert sql: {}", insertStatement);
79  
80          final SqlStatementParser sqlStatementParser = new SqlStatementParser(insertStatement);
81          final List<Parameter> params = new LinkedList<>();
82          for (String field : sqlStatementParser.getNamedParameters()) {
83              final JsonPointer fieldPointer = new JsonPointer(field);
84              final Parameter parameter =
85                      new Parameter(
86                              getParameterType(eventTopicMetaData, fieldPointer),
87                              content.get(fieldPointer) == null ? null : content.get(fieldPointer).getObject());
88              params.add(parameter);
89          }
90          return new JdbcAuditEvent(sqlStatementParser.getSqlStatement(), params);
91      }
92  
93      /**
94       * {@inheritDoc}
95       */
96      @Override
97      public abstract JdbcAuditEvent buildQueryEvent(final TableMapping mapping, final QueryRequest queryRequest,
98              final JsonValue eventTopicMetaData) throws AuditException;
99  
100     /**
101      * Creates a named parameter given a {@link JsonPointer}. A named parameter has the following format: ${SOME_VALUE}.
102      * @param pointer The {@link JsonPointer} to wrap.
103      * @return A {@link JsonPointer} wrapped as a named parameter.
104      */
105     protected String createNamedParameter(final JsonPointer pointer) {
106         return "${" + pointer.toString() + "}";
107     }
108 
109     /**
110      * Transforms the input values into named parameters.
111      * @param values The values to transform.
112      * @return A collection of the transformed values.
113      */
114     protected Collection<String> createReplacementTokens(Collection<String> values) {
115         Collection<String> transformedValues = new LinkedList<>();
116         for (final String value : values) {
117             transformedValues.add(createNamedParameter(new JsonPointer(value)));
118         }
119         return transformedValues;
120     }
121 
122     /**
123      * Gets the Type of the sql parameter.
124      * @param eventTopicMetaData The event topic metadata.
125      * @param field The field to get the type of.
126      * @return The parameter type.
127      * @throws AuditException If unable to get the parameter type.
128      */
129     protected Type getParameterType(final JsonValue eventTopicMetaData, final JsonPointer field)
130             throws AuditException {
131         try {
132             return Utils.asEnum(AuditEventHelper.getPropertyType(eventTopicMetaData, field), Type.class);
133         } catch (ResourceException e) {
134             final String error = String.format("Unable to get type for filed %s", field);
135             logger.error(error, field);
136             throw new AuditException(error, e);
137         }
138     }
139 }