blob: 9b360c873aee781046273acd19747a54d39b095e [file] [log] [blame]
/*
* Copyright (C) 2016 Jorge Ruesga
*
* 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 or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ruesga.gerrit.plugins.fcm;
import java.io.File;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import org.h2.jdbcx.JdbcConnectionPool;
import org.h2.jdbcx.JdbcDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gerrit.extensions.annotations.PluginData;
import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.ruesga.gerrit.plugins.fcm.rest.CloudNotificationInfo;
@Singleton
public class DatabaseManager {
private static final Logger log =
LoggerFactory.getLogger(Configuration.class);
private static final String DATABASE_NAME = "cloud-notifications";
private final File dbFile;
private final String pluginName;
private final Gson gson;
private JdbcConnectionPool connectionPool;
@Inject
public DatabaseManager(
@PluginName String pluginName,
@PluginData java.nio.file.Path path,
Configuration cfg) {
this.pluginName = pluginName;
this.gson = new GsonBuilder().create();
if (cfg.databasePath != null && !cfg.databasePath.isEmpty()) {
this.dbFile = new File(cfg.databasePath);
} else {
this.dbFile = new File(path.toFile(), DATABASE_NAME);
}
boolean canWrite = (this.dbFile.exists() && this.dbFile.canWrite())
|| (this.dbFile.getParentFile() != null
&& this.dbFile.getParentFile().canWrite());
if (!canWrite) {
log.warn(String.format(
"[%s] Database is not writeable.", pluginName));
throw new IllegalArgumentException("Database is not writeable: "
+ this.dbFile.getAbsolutePath());
}
}
public void initialize() {
log.info(String.format("[%s] Initialize database [%s]...",
pluginName, this.dbFile.getAbsolutePath()));
JdbcDataSource ds = new JdbcDataSource();
ds.setURL("jdbc:h2:" + this.dbFile.getAbsolutePath());
this.connectionPool = JdbcConnectionPool.create(ds);
createDatabaseIfNeeded();
}
public void shutdown() {
this.connectionPool.dispose();
}
public CloudNotificationInfo getCloudNotification(
int accountId, String deviceId, String token) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = this.connectionPool.getConnection();
st = conn.prepareStatement("select * from notifications where " +
"user = ? and device = ? and token = ?");
st.setInt(1, accountId);
st.setString(2, deviceId);
st.setString(3, token);
rs = st.executeQuery();
if (rs.next()) {
return gson.fromJson(
rs.getString("data"), CloudNotificationInfo.class);
}
} catch (SQLException ex) {
log.warn(String.format(
"[%s] Failed to access notifications database",
this.pluginName), ex);
} finally {
safelyCloseResources(conn, st, rs);
}
return null;
}
public List<CloudNotificationInfo> getCloudNotifications(int accountId) {
List<CloudNotificationInfo> notifications = new ArrayList<>();
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = this.connectionPool.getConnection();
st = conn.prepareStatement("select * from notifications where " +
"user = ?");
st.setInt(1, accountId);
rs = st.executeQuery();
while (rs.next()) {
notifications.add(gson.fromJson(
rs.getString("data"), CloudNotificationInfo.class));
}
} catch (SQLException ex) {
log.warn(String.format(
"[%s] Failed to access notifications database",
this.pluginName), ex);
} finally {
safelyCloseResources(conn, st, rs);
}
return notifications;
}
public List<CloudNotificationInfo> getCloudNotifications(
int accountId, String device) {
List<CloudNotificationInfo> notifications = new ArrayList<>();
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = this.connectionPool.getConnection();
st = conn.prepareStatement("select * from notifications where " +
"user = ? and device = ?");
st.setInt(1, accountId);
st.setString(2, device);
rs = st.executeQuery();
while (rs.next()) {
notifications.add(gson.fromJson(
rs.getString("data"), CloudNotificationInfo.class));
}
} catch (SQLException ex) {
log.warn(String.format(
"[%s] Failed to access notifications database",
this.pluginName), ex);
} finally {
safelyCloseResources(conn, st, rs);
}
return notifications;
}
public void registerCloudNotification(
int accountId, CloudNotificationInfo notification) {
Connection conn = null;
PreparedStatement st = null;
try {
conn = this.connectionPool.getConnection();
st = conn.prepareStatement("merge into notifications (user, " +
"device, token, data) KEY(user, device, token) " +
"VALUES (?, ?, ?, ?)");
st.setInt(1, accountId);
st.setString(2, notification.device);
st.setString(3, notification.token);
st.setString(4, gson.toJson(notification));
st.execute();
} catch (SQLException ex) {
log.warn(String.format(
"[%s] Failed to update device: %s",
this.pluginName, notification.device), ex);
} finally {
safelyCloseResources(conn, st, null);
}
}
public void unregisterCloudNotification(
int accountId, String deviceId, String token) {
Connection conn = null;
PreparedStatement st = null;
try {
conn = this.connectionPool.getConnection();
st = conn.prepareStatement("delete from notifications where " +
"user = ? and device = ? and token = ?");
st.setInt(1, accountId);
st.setString(2, deviceId);
st.setString(3, token);
st.execute();
} catch (SQLException ex) {
log.warn(String.format(
"[%s] Failed to delete device: %s",
this.pluginName, deviceId), ex);
} finally {
safelyCloseResources(conn, st, null);
}
}
private void createDatabaseIfNeeded() {
Connection conn = null;
Statement st = null;
try {
conn = this.connectionPool.getConnection();
st = conn.createStatement();
st.execute(
"create table if not exists notifications (" +
"user int unsigned NOT NULL, " +
"device varchar(250) NOT NULL, " +
"token varchar(250) NOT NULL, " +
"data varchar(4000) NOT NULL," +
"primary key (user, device, token))");
} catch (SQLException ex) {
// The table exists. Ignore
} finally {
safelyCloseResources(conn, st, null);
}
}
private void safelyCloseResources(
Connection conn, Statement st, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (Exception ex) {
// Ignore
}
}
if (st != null) {
try {
st.close();
} catch (Exception ex) {
// Ignore
}
}
if (conn != null) {
try {
conn.close();
} catch (Exception ex) {
// Ignore
}
}
}
}