Skip to content
This repository has been archived by the owner on Feb 12, 2022. It is now read-only.

Commit

Permalink
Merge pull request #619 from bsura/cache_users
Browse files Browse the repository at this point in the history
Store user objects on centralized cache.
  • Loading branch information
bsura authored Jul 28, 2017
2 parents b2b0c72 + 6ff35d3 commit 1d69b19
Show file tree
Hide file tree
Showing 16 changed files with 311 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -809,7 +809,7 @@ public Alert deserialize(JsonParser jp, DeserializationContext ctxt) throws IOEx
SimpleModule module = new SimpleModule();
module.addDeserializer(Trigger.class, new Trigger.Deserializer());
module.addDeserializer(Notification.class, new Notification.Deserializer());
module.addDeserializer(PrincipalUser.class, new PrincipalUser.Deserializer());
module.addDeserializer(PrincipalUser.class, new Alert.PrincipalUserDeserializer());

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
Expand Down Expand Up @@ -892,5 +892,39 @@ private boolean _contains(BigInteger triggerID, List<Trigger> triggersWithIDsOnl

}

public static class PrincipalUserSerializer extends JsonSerializer<PrincipalUser> {

@Override
public void serialize(PrincipalUser value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeStringField("id", value.getId().toString());
jgen.writeStringField("username", value.getUserName());
jgen.writeStringField("email", value.getEmail());
jgen.writeEndObject();
}
}

public static class PrincipalUserDeserializer extends JsonDeserializer<PrincipalUser> {

@Override
public PrincipalUser deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {

PrincipalUser user = new PrincipalUser();
JsonNode rootNode = jp.getCodec().readTree(jp);

BigInteger id = new BigInteger(rootNode.get("id").asText());
user.id = id;

String username = rootNode.get("username").asText();
user.setUserName(username);

String email = rootNode.get("email").asText();
user.setEmail(email);

return user;
}

}

}
/* Copyright (c) 2016, Salesforce.com, Inc. All rights reserved. */
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,13 @@
import java.io.Serializable;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
Expand Down Expand Up @@ -100,11 +104,26 @@ public class PrincipalUser extends JPAEntity implements Serializable {
public static class Serializer extends JsonSerializer<PrincipalUser> {

@Override
public void serialize(PrincipalUser value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
public void serialize(PrincipalUser user, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeStringField("id", value.getId().toString());
jgen.writeStringField("username", value.getUserName());
jgen.writeStringField("email", value.getEmail());
jgen.writeStringField("id", user.getId().toString());
jgen.writeStringField("username", user.getUserName());
jgen.writeStringField("email", user.getEmail());
jgen.writeBooleanField("privileged", user.isPrivileged());
jgen.writeObjectField("preferences", user.getPreferences());

jgen.writeArrayFieldStart("ownedDashboardIds");
for(Dashboard dashboard : user.getOwnedDashboards()) {
jgen.writeNumber(dashboard.getId());
}
jgen.writeEndArray();

jgen.writeNumberField("createdDate", user.getCreatedDate().getTime());
jgen.writeNumberField("modifiedDate", user.getModifiedDate().getTime());
jgen.writeFieldName("createdBy");
jgen.writeNumber(user.getCreatedBy().getId());
jgen.writeFieldName("modifiedBy");
jgen.writeNumber(user.getModifiedBy().getId());
jgen.writeEndObject();
}
}
Expand All @@ -126,6 +145,36 @@ public PrincipalUser deserialize(JsonParser jp, DeserializationContext ctxt) thr
String email = rootNode.get("email").asText();
user.setEmail(email);

user.setPrivileged(rootNode.get("privileged").asBoolean());

Map<Preference, String> preferences = new HashMap<>();
JsonNode preferencesNode = rootNode.get("preferences");
if(preferencesNode.isObject()) {
Iterator<Entry<String, JsonNode>> fieldsIter = preferencesNode.fields();
while(fieldsIter.hasNext()) {
Entry<String, JsonNode> field = fieldsIter.next();
preferences.put(Preference.valueOf(field.getKey()), field.getValue().asText());
}
}
user.preferences = preferences;

List<Dashboard> ownedDashboards = new ArrayList<>();
JsonNode ownedDashboardIds = rootNode.get("ownedDashboardIds");
if(ownedDashboardIds.isArray()) {
for(JsonNode ownedDashboardId : ownedDashboardIds) {
Dashboard d = new Dashboard();
d.id = new BigInteger(ownedDashboardId.asText());
ownedDashboards.add(d);
}
}
user.setOwnedDashboards(ownedDashboards);

user.setCreatedBy(new PrincipalUser(new BigInteger(rootNode.get("createdBy").asText())));
user.createdDate = new Date(rootNode.get("createdDate").asLong());

user.setModifiedBy(new PrincipalUser(new BigInteger(rootNode.get("modifiedBy").asText())));
user.modifiedDate = new Date(rootNode.get("modifiedDate").asLong());

return user;
}

Expand All @@ -136,15 +185,19 @@ public PrincipalUser deserialize(JsonParser jp, DeserializationContext ctxt) thr
@Basic(optional = false)
@Column(nullable = false, unique = true)
private String userName;

@Basic(optional = false)
@Column(nullable = false, unique = true)
private String email;

@ElementCollection
@MapKeyColumn(name = "name")
@Column(name = "preference")
private Map<Preference, String> preferences = new HashMap<>();

@OneToMany(mappedBy = "owner")
private List<Dashboard> ownedDashboards = new ArrayList<>();

private boolean privileged = false;

//~ Constructors *********************************************************************************************************************************
Expand All @@ -155,7 +208,7 @@ public PrincipalUser deserialize(JsonParser jp, DeserializationContext ctxt) thr
* @param userName The user name for the
* @param email The email address for the user.
*/
public PrincipalUser(String userName, String email) {
private PrincipalUser(String userName, String email) {
this(null, userName, email);
}

Expand All @@ -176,6 +229,10 @@ public PrincipalUser(PrincipalUser creator, String userName, String email) {
protected PrincipalUser() {
super(null);
}

private PrincipalUser(BigInteger id) {
this.id = id;
}

//~ Methods **************************************************************************************************************************************

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ private void _initializeObjectMapper() {
module.addSerializer(Alert.class, new Alert.Serializer());
module.addSerializer(Trigger.class, new Trigger.Serializer());
module.addSerializer(Notification.class, new Notification.Serializer());
module.addSerializer(PrincipalUser.class, new PrincipalUser.Serializer());
module.addSerializer(PrincipalUser.class, new Alert.PrincipalUserSerializer());
module.addDeserializer(Alert.class, new Alert.Deserializer());

_mapper.registerModule(module);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package com.salesforce.dva.argus.service.users;

import static com.salesforce.dva.argus.system.SystemAssert.requireArgument;
import static java.math.BigInteger.ZERO;

import java.io.IOException;
import java.math.BigInteger;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.google.inject.Inject;
import com.salesforce.dva.argus.entity.PrincipalUser;
import com.salesforce.dva.argus.service.AuditService;
import com.salesforce.dva.argus.service.CacheService;
import com.salesforce.dva.argus.service.NamedBinding;
import com.salesforce.dva.argus.service.UserService;
import com.salesforce.dva.argus.service.jpa.DefaultJPAService;
import com.salesforce.dva.argus.system.SystemConfiguration;

public class CachedUserService extends DefaultJPAService implements UserService {

//TTL of 30 days
private static final int TTL_SECS = 30 * 24 * 60 * 60;

private final Logger _logger = LoggerFactory.getLogger(getClass());
private final CacheService _cacheService;
private final UserService _defaultUserService;
private ObjectMapper _mapper;

/**
* Creates a new CachedUserService object.
*
* @param auditService The audit service. Cannot be null.
* @param config Service properties
*/
@Inject
public CachedUserService(AuditService auditService, SystemConfiguration config, CacheService cacheService,
@NamedBinding UserService userService) {
super(auditService, config);
_cacheService = cacheService;
_defaultUserService = userService;

_mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(PrincipalUser.class, new PrincipalUser.Serializer());
module.addDeserializer(PrincipalUser.class, new PrincipalUser.Deserializer());
_mapper.registerModule(module);
}

@Override
public PrincipalUser findUserByUsername(String userName) {
requireNotDisposed();
requireArgument(userName != null && !userName.trim().isEmpty(), "User name cannot be null or empty.");

String cachedUser = _cacheService.get(userName);

if(cachedUser != null) {
try {
PrincipalUser user = _mapper.readValue(cachedUser, PrincipalUser.class);
return user;
} catch (IOException e) {
_logger.warn("Failed to deserialize user object retrieved from cache. Will get from persistent storage.");
}
}

_logger.debug("User not found in cache. Will read from persistent storage.");
PrincipalUser user = _defaultUserService.findUserByUsername(userName);
if(user != null) {
try {
_cacheService.put(user.getUserName(), _mapper.writeValueAsString(user), TTL_SECS);
_cacheService.put(user.getId().toString(), _mapper.writeValueAsString(user), TTL_SECS);
} catch (JsonProcessingException e) {
_logger.warn("Failed to serialize user object. User {} will not be cached.", user);
}
}

return user;
}

@Override
public PrincipalUser findUserByPrimaryKey(BigInteger id) {
requireNotDisposed();
requireArgument(id != null && id.compareTo(ZERO) > 0, "ID must be a positive non-zero value.");

String cachedUser = _cacheService.get(id.toString());

if(cachedUser != null) {
try {
PrincipalUser user = _mapper.readValue(cachedUser, PrincipalUser.class);
return user;
} catch (IOException e) {
_logger.warn("Failed to deserialize user object retrieved from cache. Will get from persistent storage.");
}
}

_logger.debug("User not found in cache. Will read from persistent storage.");
PrincipalUser user = _defaultUserService.findUserByPrimaryKey(id);
if(user != null) {
try {
_cacheService.put(user.getUserName(), _mapper.writeValueAsString(user), TTL_SECS);
_cacheService.put(user.getId().toString(), _mapper.writeValueAsString(user), TTL_SECS);
} catch (JsonProcessingException e) {
_logger.warn("Failed to serialize user object. User {} will not be cached.", user);
}
}

return user;
}

@Override
public void deleteUser(PrincipalUser user) {
requireNotDisposed();
requireArgument(user != null && user.getId() != null && user.getId().compareTo(ZERO) > 0, "User cannot be null and must have a valid ID.");

_cacheService.delete(user.getId().toString());

if(user.getUserName() == null || user.getUserName().isEmpty()) {
user = findUserByPrimaryKey(user.getId());
_cacheService.delete(user.getUserName().toString());
}
_defaultUserService.deleteUser(user);
}

@Override
public PrincipalUser updateUser(PrincipalUser user) {
requireNotDisposed();
requireArgument(user != null, "User cannot be null.");

if(user.getId() != null) {
_cacheService.delete(user.getId().toString());
}

if(user.getUserName() != null) {
_cacheService.delete(user.getUserName().toString());
}
return _defaultUserService.updateUser(user);
}

@Override
public PrincipalUser findAdminUser() {
requireNotDisposed();

return _defaultUserService.findAdminUser();
}

@Override
public PrincipalUser findDefaultUser() {
requireNotDisposed();

return _defaultUserService.findDefaultUser();
}

@Override
public long getUniqueUserCount() {
requireNotDisposed();

return _defaultUserService.getUniqueUserCount();
}

}
Loading

0 comments on commit 1d69b19

Please sign in to comment.