All Downloads are FREE. Search and download functionalities are using the official Maven repository.

patterntesting.runtime.monitor.db.internal.StasiPreparedStatement Maven / Gradle / Ivy

Go to download

PatternTesting Runtime (patterntesting-rt) is the runtime component for the PatternTesting framework. It provides the annotations and base classes for the PatternTesting testing framework (e.g. patterntesting-check, patterntesting-concurrent or patterntesting-exception) but can be also used standalone for classpath monitoring or profiling. It uses AOP and AspectJ to perform this feat.

There is a newer version: 2.4.0
Show newest version
/*
 * $Id: StasiPreparedStatement.java,v 1.2 2014/04/19 19:14:30 oboehm Exp $
 *
 * Copyright (c) 2014 by Oliver Boehm
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express orimplied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * (c)reated 05.04.2014 by oliver ([email protected])
 */

package patterntesting.runtime.monitor.db.internal;

import java.io.*;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.*;
import java.sql.Date;
import java.util.*;

import org.apache.commons.lang.StringUtils;

import patterntesting.runtime.log.*;
import patterntesting.runtime.monitor.ProfileMonitor;
import patterntesting.runtime.monitor.db.SqlStatistic;

/**
 * A simple wrapper for {@link PreparedStatement} to be able to find resource
 * problems while reading and writing to the database. It allows us also to
 * measure times of SQL statements.
 * 

* Why the name "Stasi..."? The Stasi was the official state security service of * Eastern Germany which controls the people (like NSA in the U.S. or KGB in * Russia, see also Wikipedia). * The StasiPreparedStatement controls the embedded {@link PreparedStatement} - * therefore the name. *

* * @author oliver * @version $Revision: 1.2 $ * @since 1.4.1 (05.04.2014) */ public final class StasiPreparedStatement extends StasiStatement implements PreparedStatement { private static final PerfLogger log = new PerfLogger(StasiPreparedStatement.class); private final LogWatch logWatch = new LogWatch(); private final PreparedStatement preparedStatement; private final String sqlTemplate; private final Map parameters = new HashMap(); /** * Instantiates a new stasi prepared statement. * * @param statement the statement * @param args the arguments for the creation of the * {@link PreparedStatement} */ public StasiPreparedStatement(final PreparedStatement statement, final Object... args) { super(statement); this.preparedStatement = statement; this.sqlTemplate = (String) args[0]; } /** * Adds the batch. * * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#addBatch() */ public void addBatch() throws SQLException { this.preparedStatement.addBatch(); log.trace("Batch added."); } /** * Clear parameters. * * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#clearParameters() */ public void clearParameters() throws SQLException { this.preparedStatement.clearParameters(); this.parameters.clear(); log.trace("Parameters cleared."); } /** * Close. * * @throws SQLException the sQL exception * @see java.sql.Statement#close() */ public void close() throws SQLException { this.getStatement().close(); log.debug("Statement for \"{}\" was closed after {}.", this.sqlTemplate, logWatch); } /** * Execute. * * @return true, if successful * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#execute() */ public boolean execute() throws SQLException { ProfileMonitor mon = SqlStatistic.start(this.sqlTemplate); try { boolean ok = this.preparedStatement.execute(); logSQL(mon, ok); return ok; } catch (SQLException ex) { throw enrichedSQLException(mon, ex); } } /** * Execute query. * * @return the result set * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#executeQuery() */ public ResultSet executeQuery() throws SQLException { ProfileMonitor mon = SqlStatistic.start(this.sqlTemplate); try { ResultSet rs = new StasiResultSet(this.preparedStatement.executeQuery()); this.logSQL(mon, rs); return rs; } catch (SQLException ex) { throw enrichedSQLException(mon, ex); } } /** * Execute update. * * @return the int * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#executeUpdate() */ public int executeUpdate() throws SQLException { ProfileMonitor mon = SqlStatistic.start(this.sqlTemplate); try { int ret = this.preparedStatement.executeUpdate(); logSQL(mon, ret); return ret; } catch (SQLException ex) { throw enrichedSQLException(mon, ex); } } private SQLException enrichedSQLException(final ProfileMonitor mon, final SQLException original) { return enrichedSQLException(mon, this.resolveSQL(), original); } /** * Gets the meta data. * * @return the meta data * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#getMetaData() */ public ResultSetMetaData getMetaData() throws SQLException { return this.preparedStatement.getMetaData(); } /** * Gets the parameter meta data. * * @return the parameter meta data * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#getParameterMetaData() */ public ParameterMetaData getParameterMetaData() throws SQLException { return this.preparedStatement.getParameterMetaData(); } /** * Sets the array. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setArray(int, java.sql.Array) */ public void setArray(final int arg0, final Array arg1) throws SQLException { this.preparedStatement.setArray(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the ascii stream. * * @param arg0 the arg0 * @param arg1 the arg1 * @param arg2 the arg2 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setAsciiStream(int, java.io.InputStream, int) */ public void setAsciiStream(final int arg0, final InputStream arg1, final int arg2) throws SQLException { this.preparedStatement.setAsciiStream(arg0, arg1, arg2); this.parameters.put(arg0, arg1); } /** * Sets the ascii stream. * * @param arg0 the arg0 * @param arg1 the arg1 * @param arg2 the arg2 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setAsciiStream(int, java.io.InputStream, long) */ public void setAsciiStream(final int arg0, final InputStream arg1, final long arg2) throws SQLException { this.preparedStatement.setAsciiStream(arg0, arg1, arg2); this.parameters.put(arg0, arg1); } /** * Sets the ascii stream. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setAsciiStream(int, java.io.InputStream) */ public void setAsciiStream(final int arg0, final InputStream arg1) throws SQLException { this.preparedStatement.setAsciiStream(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the big decimal. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setBigDecimal(int, java.math.BigDecimal) */ public void setBigDecimal(final int arg0, final BigDecimal arg1) throws SQLException { this.preparedStatement.setBigDecimal(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the binary stream. * * @param arg0 the arg0 * @param arg1 the arg1 * @param arg2 the arg2 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setBinaryStream(int, java.io.InputStream, int) */ public void setBinaryStream(final int arg0, final InputStream arg1, final int arg2) throws SQLException { this.preparedStatement.setBinaryStream(arg0, arg1, arg2); this.parameters.put(arg0, arg1); } /** * Sets the binary stream. * * @param arg0 the arg0 * @param arg1 the arg1 * @param arg2 the arg2 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setBinaryStream(int, java.io.InputStream, long) */ public void setBinaryStream(final int arg0, final InputStream arg1, final long arg2) throws SQLException { this.preparedStatement.setBinaryStream(arg0, arg1, arg2); this.parameters.put(arg0, arg1); } /** * Sets the binary stream. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setBinaryStream(int, java.io.InputStream) */ public void setBinaryStream(final int arg0, final InputStream arg1) throws SQLException { this.preparedStatement.setBinaryStream(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the blob. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setBlob(int, java.sql.Blob) */ public void setBlob(final int arg0, final Blob arg1) throws SQLException { this.preparedStatement.setBlob(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the blob. * * @param arg0 the arg0 * @param arg1 the arg1 * @param arg2 the arg2 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setBlob(int, java.io.InputStream, long) */ public void setBlob(final int arg0, final InputStream arg1, final long arg2) throws SQLException { this.preparedStatement.setBlob(arg0, arg1, arg2); this.parameters.put(arg0, arg1); } /** * Sets the blob. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setBlob(int, java.io.InputStream) */ public void setBlob(final int arg0, final InputStream arg1) throws SQLException { this.preparedStatement.setBlob(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the boolean. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setBoolean(int, boolean) */ public void setBoolean(final int arg0, final boolean arg1) throws SQLException { this.preparedStatement.setBoolean(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the byte. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setByte(int, byte) */ public void setByte(final int arg0, final byte arg1) throws SQLException { this.preparedStatement.setByte(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the bytes. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setBytes(int, byte[]) */ public void setBytes(final int arg0, final byte[] arg1) throws SQLException { this.preparedStatement.setBytes(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the character stream. * * @param arg0 the arg0 * @param arg1 the arg1 * @param arg2 the arg2 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setCharacterStream(int, java.io.Reader, int) */ public void setCharacterStream(final int arg0, final Reader arg1, final int arg2) throws SQLException { this.preparedStatement.setCharacterStream(arg0, arg1, arg2); this.parameters.put(arg0, arg1); } /** * Sets the character stream. * * @param arg0 the arg0 * @param arg1 the arg1 * @param arg2 the arg2 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setCharacterStream(int, java.io.Reader, long) */ public void setCharacterStream(final int arg0, final Reader arg1, final long arg2) throws SQLException { this.preparedStatement.setCharacterStream(arg0, arg1, arg2); this.parameters.put(arg0, arg1); } /** * Sets the character stream. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setCharacterStream(int, java.io.Reader) */ public void setCharacterStream(final int arg0, final Reader arg1) throws SQLException { this.preparedStatement.setCharacterStream(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the clob. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setClob(int, java.sql.Clob) */ public void setClob(final int arg0, final Clob arg1) throws SQLException { this.preparedStatement.setClob(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the clob. * * @param arg0 the arg0 * @param arg1 the arg1 * @param arg2 the arg2 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setClob(int, java.io.Reader, long) */ public void setClob(final int arg0, final Reader arg1, final long arg2) throws SQLException { this.preparedStatement.setClob(arg0, arg1, arg2); this.parameters.put(arg0, arg1); } /** * Sets the clob. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setClob(int, java.io.Reader) */ public void setClob(final int arg0, final Reader arg1) throws SQLException { this.preparedStatement.setClob(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the date. * * @param arg0 the arg0 * @param arg1 the arg1 * @param arg2 the arg2 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setDate(int, java.sql.Date, java.util.Calendar) */ public void setDate(final int arg0, final Date arg1, final Calendar arg2) throws SQLException { this.preparedStatement.setDate(arg0, arg1, arg2); this.parameters.put(arg0, arg1); } /** * Sets the date. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setDate(int, java.sql.Date) */ public void setDate(final int arg0, final Date arg1) throws SQLException { this.preparedStatement.setDate(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the double. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setDouble(int, double) */ public void setDouble(final int arg0, final double arg1) throws SQLException { this.preparedStatement.setDouble(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the float. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setFloat(int, float) */ public void setFloat(final int arg0, final float arg1) throws SQLException { this.preparedStatement.setFloat(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the int. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setInt(int, int) */ public void setInt(final int arg0, final int arg1) throws SQLException { this.preparedStatement.setInt(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the long. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setLong(int, long) */ public void setLong(final int arg0, final long arg1) throws SQLException { this.preparedStatement.setLong(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the n character stream. * * @param arg0 the arg0 * @param arg1 the arg1 * @param arg2 the arg2 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setNCharacterStream(int, java.io.Reader, long) */ public void setNCharacterStream(final int arg0, final Reader arg1, final long arg2) throws SQLException { this.preparedStatement.setNCharacterStream(arg0, arg1, arg2); this.parameters.put(arg0, arg1); } /** * Sets the n character stream. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setNCharacterStream(int, java.io.Reader) */ public void setNCharacterStream(final int arg0, final Reader arg1) throws SQLException { this.preparedStatement.setNCharacterStream(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the n clob. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setNClob(int, java.sql.NClob) */ public void setNClob(final int arg0, final NClob arg1) throws SQLException { this.preparedStatement.setNClob(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the n clob. * * @param arg0 the arg0 * @param arg1 the arg1 * @param arg2 the arg2 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setNClob(int, java.io.Reader, long) */ public void setNClob(final int arg0, final Reader arg1, final long arg2) throws SQLException { this.preparedStatement.setNClob(arg0, arg1, arg2); this.parameters.put(arg0, arg1); } /** * Sets the n clob. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setNClob(int, java.io.Reader) */ public void setNClob(final int arg0, final Reader arg1) throws SQLException { this.preparedStatement.setNClob(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the n string. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setNString(int, java.lang.String) */ public void setNString(final int arg0, final String arg1) throws SQLException { this.preparedStatement.setNString(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the null. * * @param arg0 the arg0 * @param arg1 the arg1 * @param arg2 the arg2 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setNull(int, int, java.lang.String) */ public void setNull(final int arg0, final int arg1, final String arg2) throws SQLException { this.preparedStatement.setNull(arg0, arg1, arg2); this.parameters.put(arg0, "'null'"); } /** * Sets the null. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setNull(int, int) */ public void setNull(final int arg0, final int arg1) throws SQLException { this.preparedStatement.setNull(arg0, arg1); this.parameters.put(arg0, "'null'"); } /** * Sets the object. * * @param arg0 the arg0 * @param arg1 the arg1 * @param arg2 the arg2 * @param arg3 the arg3 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setObject(int, java.lang.Object, int, int) */ public void setObject(final int arg0, final Object arg1, final int arg2, final int arg3) throws SQLException { this.preparedStatement.setObject(arg0, arg1, arg2, arg3); this.parameters.put(arg0, arg1); } /** * Sets the object. * * @param arg0 the arg0 * @param arg1 the arg1 * @param arg2 the arg2 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setObject(int, java.lang.Object, int) */ public void setObject(final int arg0, final Object arg1, final int arg2) throws SQLException { this.preparedStatement.setObject(arg0, arg1, arg2); this.parameters.put(arg0, arg1); } /** * Sets the object. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setObject(int, java.lang.Object) */ public void setObject(final int arg0, final Object arg1) throws SQLException { this.preparedStatement.setObject(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the ref. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setRef(int, java.sql.Ref) */ public void setRef(final int arg0, final Ref arg1) throws SQLException { this.preparedStatement.setRef(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the row id. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setRowId(int, java.sql.RowId) */ public void setRowId(final int arg0, final RowId arg1) throws SQLException { this.preparedStatement.setRowId(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the sqlxml. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setSQLXML(int, java.sql.SQLXML) */ public void setSQLXML(final int arg0, final SQLXML arg1) throws SQLException { this.preparedStatement.setSQLXML(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the short. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setShort(int, short) */ public void setShort(final int arg0, final short arg1) throws SQLException { this.preparedStatement.setShort(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the string. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setString(int, java.lang.String) */ public void setString(final int arg0, final String arg1) throws SQLException { this.preparedStatement.setString(arg0, arg1); this.parameters.put(arg0, "'" + arg1 + "'"); } /** * Sets the time. * * @param arg0 the arg0 * @param arg1 the arg1 * @param arg2 the arg2 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setTime(int, java.sql.Time, java.util.Calendar) */ public void setTime(final int arg0, final Time arg1, final Calendar arg2) throws SQLException { this.preparedStatement.setTime(arg0, arg1, arg2); this.parameters.put(arg0, arg1); } /** * Sets the time. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setTime(int, java.sql.Time) */ public void setTime(final int arg0, final Time arg1) throws SQLException { this.preparedStatement.setTime(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the timestamp. * * @param arg0 the arg0 * @param arg1 the arg1 * @param arg2 the arg2 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setTimestamp(int, java.sql.Timestamp, java.util.Calendar) */ public void setTimestamp(final int arg0, final Timestamp arg1, final Calendar arg2) throws SQLException { this.preparedStatement.setTimestamp(arg0, arg1, arg2); this.parameters.put(arg0, arg1); } /** * Sets the timestamp. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setTimestamp(int, java.sql.Timestamp) */ public void setTimestamp(final int arg0, final Timestamp arg1) throws SQLException { this.preparedStatement.setTimestamp(arg0, arg1); this.parameters.put(arg0, arg1); } /** * Sets the url. * * @param arg0 the arg0 * @param arg1 the arg1 * @throws SQLException the sQL exception * @see java.sql.PreparedStatement#setURL(int, java.net.URL) */ public void setURL(final int arg0, final URL arg1) throws SQLException { this.preparedStatement.setURL(arg0, arg1); } /** * Sets the unicode stream. * * @param arg0 the arg0 * @param arg1 the arg1 * @param arg2 the arg2 * @throws SQLException the sQL exception */ @SuppressWarnings("deprecation") public void setUnicodeStream(final int arg0, final InputStream arg1, final int arg2) throws SQLException { this.preparedStatement.setUnicodeStream(arg0, arg1, arg2); } /** * We want to return the real SQL as string represenation. * * @return the string * @see java.lang.Object#toString() */ @Override public String toString() { return this.resolveSQL(); } private String resolveSQL() { String[] elements = StringUtils.split(this.sqlTemplate + " ", '?'); StringBuffer buf = new StringBuffer(elements[0]); for (int i = 1; i < elements.length; i++) { buf.append(this.getSqlParameter(i)); buf.append(elements[i]); } return buf.toString().trim(); } private Object getSqlParameter(final int n) { Object param = this.parameters.get(n); if (param == null) { return '?'; } return param; } private void logSQL(final ProfileMonitor mon, final Object ret) { if (log.isDebugEnabled()) { log.stop(mon, this.resolveSQL(), ret); } else { log.stop(mon, this.sqlTemplate, ret); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy