package org.atomhopper.postgres.adapter;
import org.apache.abdera.model.Entry;
import org.apache.commons.lang.StringUtils;
import org.atomhopper.adapter.FeedPublisher;
import org.atomhopper.adapter.NotImplemented;
import org.atomhopper.adapter.PublicationException;
import org.atomhopper.adapter.ResponseBuilder;
import org.atomhopper.adapter.request.adapter.DeleteEntryRequest;
import org.atomhopper.adapter.request.adapter.PostEntryRequest;
import org.atomhopper.adapter.request.adapter.PutEntryRequest;
import org.atomhopper.postgres.model.PersistedEntry;
import org.atomhopper.postgres.query.EntryRowMapper;
import org.atomhopper.postgres.query.PostgreSQLTextArray;
import org.atomhopper.response.AdapterResponse;
import org.atomhopper.response.EmptyBody;
import org.atomhopper.util.uri.template.EnumKeyedTemplateParameters;
import org.atomhopper.util.uri.template.URITemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import java.io.IOException;
import java.io.StringWriter;
import java.util.*;
import com.yammer.metrics.Metrics;
import com.yammer.metrics.core.Counter;
import static org.apache.abdera.i18n.text.UrlEncoding.decode;
public class PostgresFeedPublisher implements FeedPublisher {
private static final Logger LOG = LoggerFactory.getLogger(PostgresFeedPublisher.class);
private static final String UUID_URI_SCHEME = "urn:uuid:";
private static final String LINKREL_SELF = "self";
private JdbcTemplate jdbcTemplate;
private boolean allowOverrideId = false;
private boolean allowOverrideDate = false;
private Map<String, Counter> counterMap = Collections.synchronizedMap(new HashMap<String, Counter>());
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public void setAllowOverrideId(boolean allowOverrideId) {
this.allowOverrideId = allowOverrideId;
}
public void setAllowOverrideDate(boolean allowOverrideDate) {
this.allowOverrideDate = allowOverrideDate;
}
@Override
@NotImplemented
public void setParameters(Map<String, String> params) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public AdapterResponse<Entry> postEntry(PostEntryRequest postEntryRequest) {
final Entry abderaParsedEntry = postEntryRequest.getEntry();
final PersistedEntry persistedEntry = new PersistedEntry();
final String insertSQL = "INSERT INTO entries (entryid, creationdate, datelastupdated, entrybody, feed, categories) VALUES (?, ?, ?, ?, ?, ?)";
boolean entryIdSent = abderaParsedEntry.getId() != null;
// Generate an ID for this entry
if (allowOverrideId && entryIdSent && StringUtils.isNotBlank(abderaParsedEntry.getId().toString().trim())) {
String entryId = abderaParsedEntry.getId().toString();
// Check to see if entry with this id already exists
PersistedEntry exists = getEntry(entryId, postEntryRequest.getFeedName());
if (exists != null) {
String errMsg = String.format("Unable to persist entry. Reason: entryId (%s) not unique.", entryId);
return ResponseBuilder.conflict(errMsg);
}
persistedEntry.setEntryId(abderaParsedEntry.getId().toString());
} else {
persistedEntry.setEntryId(UUID_URI_SCHEME + UUID.randomUUID().toString());
abderaParsedEntry.setId(persistedEntry.getEntryId());
}
if (allowOverrideDate) {
Date updated = abderaParsedEntry.getUpdated();
if (updated != null) {
persistedEntry.setDateLastUpdated(updated);
persistedEntry.setCreationDate(updated);
}
}
// Set the categories
persistedEntry.setCategories(processCategories(abderaParsedEntry.getCategories()));
if (abderaParsedEntry.getSelfLink() == null) {
abderaParsedEntry.addLink(decode(postEntryRequest.urlFor(new EnumKeyedTemplateParameters<URITemplate>(URITemplate.FEED)))
+ "entries/" + persistedEntry.getEntryId()).setRel(LINKREL_SELF);
}
persistedEntry.setFeed(postEntryRequest.getFeedName());
persistedEntry.setEntryBody(entryToString(abderaParsedEntry));
abderaParsedEntry.setUpdated(persistedEntry.getDateLastUpdated());
abderaParsedEntry.setPublished(persistedEntry.getCreationDate());
jdbcTemplate.update(insertSQL, new Object[]{
persistedEntry.getEntryId(), persistedEntry.getCreationDate(), persistedEntry.getDateLastUpdated(),
persistedEntry.getEntryBody(), persistedEntry.getFeed(), new PostgreSQLTextArray(persistedEntry.getCategories())
});
incrementCounterForFeed(postEntryRequest.getFeedName());
return ResponseBuilder.created(abderaParsedEntry);
}
private String[] processCategories(List<org.apache.abdera.model.Category> abderaCategories) {
final List<String> categoriesList = new ArrayList<String>();
for (org.apache.abdera.model.Category abderaCat : abderaCategories) {
categoriesList.add(abderaCat.getTerm().toLowerCase());
}
final String[] categoryArray = new String[categoriesList.size()];
categoriesList.toArray(categoryArray);
return categoryArray;
}
private String entryToString(Entry entry) {
final StringWriter writer = new StringWriter();
try {
entry.writeTo(writer);
} catch (IOException ioe) {
LOG.error("Unable to write entry to string. Unable to persist entry. Reason: " + ioe.getMessage(), ioe);
throw new PublicationException(ioe.getMessage(), ioe);
}
return writer.toString();
}
@Override
@NotImplemented
public AdapterResponse<Entry> putEntry(PutEntryRequest putEntryRequest) {
throw new UnsupportedOperationException("Not supported.");
}
@Override
@NotImplemented
public AdapterResponse<EmptyBody> deleteEntry(DeleteEntryRequest deleteEntryRequest) {
throw new UnsupportedOperationException("Not supported.");
}
private PersistedEntry getEntry(final String entryId, final String feedName) {
final String entrySQL = "SELECT * FROM entries WHERE feed = ? AND entryid = ?";
List<PersistedEntry> entry = jdbcTemplate
.query(entrySQL, new Object[]{feedName, entryId}, new EntryRowMapper());
return entry.size() > 0 ? entry.get(0) : null;
}
private void incrementCounterForFeed(String feedName) {
if (!counterMap.containsKey(feedName)) {
synchronized (counterMap) {
if (!counterMap.containsKey(feedName)) {
Counter counter = Metrics.newCounter(PostgresFeedPublisher.class, "entries-created-for-" + feedName);
counterMap.put(feedName, counter);
}
}
}
counterMap.get(feedName).inc();
}
}