package org.ff4j.springjdbc.store;
/*
* #%L
* ff4j-store-springjdbc
* %%
* Copyright (C) 2013 - 2017 FF4J
* %%
* 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.
* #L%
*/
import static org.ff4j.audit.EventConstants.ACTION_CHECK_OK;
import static org.ff4j.store.JdbcStoreConstants.COL_EVENT_HOSTNAME;
import static org.ff4j.store.JdbcStoreConstants.COL_EVENT_NAME;
import static org.ff4j.store.JdbcStoreConstants.COL_EVENT_SOURCE;
import static org.ff4j.store.JdbcStoreConstants.COL_EVENT_USER;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.sql.DataSource;
import org.ff4j.audit.Event;
import org.ff4j.audit.EventQueryDefinition;
import org.ff4j.audit.EventSeries;
import org.ff4j.audit.MutableHitCount;
import org.ff4j.audit.chart.TimeSeriesChart;
import org.ff4j.audit.repository.AbstractEventRepository;
import org.ff4j.audit.repository.EventRepository;
import org.ff4j.springjdbc.store.dto.HitCountDto;
import org.ff4j.springjdbc.store.rowmapper.EventRowMapper;
import org.ff4j.springjdbc.store.rowmapper.HitCountRowMapper;
import org.ff4j.store.JdbcQueryBuilder;
import org.ff4j.utils.JdbcUtils;
import org.ff4j.utils.MappingUtil;
import org.ff4j.utils.Util;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* Implementation of {@link EventRepository} to leverage on Spring Security.
*
* @author Cedrick LUNVEN (@clunven)
*/
public class EventRepositorySpringJdbc extends AbstractEventRepository {
/** SQL DataSource. */
private DataSource dataSource;
/** Access to storage. */
private JdbcTemplate jdbcTemplate;
/** Query builder. */
private JdbcQueryBuilder queryBuilder;
/** Mapping fro mresult to bean. */
private EventRowMapper EVENT_ROWMAPPER = new EventRowMapper();
/**
* Default constructor.
*/
public EventRepositorySpringJdbc() {
}
/**
* Default constructor.
*/
public EventRepositorySpringJdbc(DataSource ds) {
this.dataSource = ds;
}
/** {@inheritDoc} */
@Override
public void createSchema() {
JdbcQueryBuilder qb = getQueryBuilder();
if (!JdbcUtils.isTableExist(dataSource, qb.getTableNameAudit())) {
getJdbcTemplate().update(qb.sqlCreateTableAudit());
}
}
/** {@inheritDoc} */
@Override
public boolean saveEvent(Event evt) {
Util.assertEvent(evt);
return getJdbcTemplate().update(getQueryBuilder().sqlSaveAudit(),
evt.getUuid(), new java.sql.Timestamp(evt.getTimestamp()), evt.getType(),
evt.getName(), evt.getAction(), evt.getHostName(), evt.getSource(),
evt.getDuration(), evt.getUser(), evt.getValue(),
MappingUtil.fromMap(evt.getCustomKeys())) > 0;
}
/** {@inheritDoc} */
@Override
public Event getEventByUUID(String uuid, Long timestamp) {
Util.assertHasLength(uuid);
List < Event > evts = getJdbcTemplate().query(
getQueryBuilder().getEventByUuidQuery(), EVENT_ROWMAPPER, uuid);
return (!evts.isEmpty()) ? evts.get(0) : null;
}
/** {@inheritDoc} */
@Override
public Map<String, MutableHitCount> getFeatureUsageHitCount(EventQueryDefinition query) {
return computeHitCount(query, COL_EVENT_NAME);
}
/** {@inheritDoc} */
@Override
public Map<String, MutableHitCount> getHostHitCount(EventQueryDefinition query) {
return computeHitCount(query, COL_EVENT_HOSTNAME);
}
/** {@inheritDoc} */
@Override
public Map<String, MutableHitCount> getUserHitCount(EventQueryDefinition query) {
return computeHitCount(query, COL_EVENT_USER);
}
/** {@inheritDoc} */
@Override
public Map<String, MutableHitCount> getSourceHitCount(EventQueryDefinition query) {
return computeHitCount(query, COL_EVENT_SOURCE);
}
/**
* Compute over a column.
*
* @param query
* current query definition
* @param colName
* column name
* @return
* hit count for this value.
*/
private Map<String, MutableHitCount> computeHitCount(EventQueryDefinition query, String colName) {
List < HitCountDto> rawResult = getJdbcTemplate().query(
getQueryBuilder().getHitCount(colName),
new HitCountRowMapper(colName),
new Timestamp(query.getFrom()),
new Timestamp(query.getTo()));
Map<String, MutableHitCount> mapofHitCount = new HashMap<String, MutableHitCount>();
for (HitCountDto dto : rawResult) {
mapofHitCount.put(dto.getColumnName(), dto.getHitcount());
}
return mapofHitCount;
}
/** {@inheritDoc} */
@Override
public TimeSeriesChart getFeatureUsageHistory(EventQueryDefinition query, TimeUnit units) {
// Create the interval depending on units
TimeSeriesChart tsc = new TimeSeriesChart(query.getFrom(), query.getTo(), units);
// Search All events
Iterator<Event> iterEvent = searchFeatureUsageEvents(query).iterator();
// Dispatch events into time slots
while (iterEvent.hasNext()) {
tsc.addEvent(iterEvent.next());
}
return tsc;
}
/** {@inheritDoc} */
@Override
public EventSeries searchFeatureUsageEvents(EventQueryDefinition qDef) {
return searchEvents(
getQueryBuilder().getSelectFeatureUsageQuery(qDef),
qDef.getFrom(), qDef.getTo());
}
/** {@inheritDoc} */
@Override
public EventSeries getAuditTrail(EventQueryDefinition qDef) {
return searchEvents(
getQueryBuilder().getSelectAuditTrailQuery(qDef),
qDef.getFrom(), qDef.getTo());
}
/** {@inheritDoc} */
private EventSeries searchEvents(String sqlQuery, long from, long to) {
EventSeries es = new EventSeries();
es.addAll(getJdbcTemplate().query(sqlQuery, EVENT_ROWMAPPER,
new Timestamp(from), new Timestamp(to)));
return es;
}
/** {@inheritDoc} */
@Override
public void purgeAuditTrail(EventQueryDefinition query) {
Util.assertNotNull(query);
getJdbcTemplate().update(getQueryBuilder().getPurgeAuditTrailQuery(query),
new java.sql.Timestamp(query.getFrom()),
new java.sql.Timestamp(query.getTo()));
}
/** {@inheritDoc} */
@Override
public void purgeFeatureUsage(EventQueryDefinition query) {
Util.assertNotNull(query);
// Enforce removing events for feature usage
query.getActionFilters().add(ACTION_CHECK_OK);
getJdbcTemplate().update(
getQueryBuilder().getPurgeFeatureUsageQuery(query),
new java.sql.Timestamp(query.getFrom()),
new java.sql.Timestamp(query.getTo()));
}
/**
* @param dataSource
* the dataSource to set
*/
@Required
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
/**
* Getter accessor for attribute 'jdbcTemplate'.
*
* @return current value of 'jdbcTemplate'
*/
public JdbcTemplate getJdbcTemplate() {
if (jdbcTemplate == null) {
if (dataSource == null) {
throw new IllegalStateException("ff4j-jdbc: DatabaseStore has not been properly initialized, datasource is null");
}
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
return jdbcTemplate;
}
/**
* @return the queryBuilder
*/
public JdbcQueryBuilder getQueryBuilder() {
if (queryBuilder == null) {
queryBuilder = new JdbcQueryBuilder();
}
return queryBuilder;
}
/**
* @param queryBuilder the queryBuilder to set
*/
public void setQueryBuilder(JdbcQueryBuilder queryBuilder) {
this.queryBuilder = queryBuilder;
}
}