Can I create an abstraction to hide my "checking the cache" logic? - java

I have getResponse() methods which build a response from data fetched from the database (or pre-saved in a SavedData object). I would like to find a way to abstract out the "check savedData" logic from my getResponse() methods. Ideally, I want to figure out a way where my getResponse() methods don't even know SavedData exists, it is just hidden behind some interface. Is there a good abstraction here I can use to clean up this code?
The following is just pseudo-code. For each field which is returned in the getResponse() JSON object, they first check if that field has been saved in some SavedData and use it if it has, otherwise, they need to query the DB for the field.
interface ResponseGetter {
public Response getResponse(String userID, SavedData savedData);
}
class A implements ResponseGetter {
public Response getResponse(String userID, SavedData savedData) {
List<String> foo;
int bar;
String bizz;
foo = savedData.get(userID, "foo");
if (foo == null) {
foo = loadFooFromDB(userID);
}
bar = savedData.get(userID, "bar");
if (bar == null) {
bar = loadBarFromDB(userID);
}
bizz = savedData.get(userID, "bizz");
if (bizz == null) {
bizz = loadBizzFromDB(userID);
}
JSONObject json = new JSONObject();
json.put("foo", foo);
json.put("bar", bar);
json.put("bizz", biz);
return new Response(json);
}
private List<String> loadFooFromDB(String userID) {
List<String> returnList = new ArrayList<String>();
DB db = this.getDB();
String query = "SELECT foo FROM SomeTable WHERE user_id=" + userID;
Results results = db.executeQuery(query);
for (Result result : results) {
returnList.add(result.toString());
}
return returnList;
}
}
class B implements ResponseGetter {
public Response getResponse(String userID, SavedData savedData) {
List<String> baz;
int qux;
String corge;
baz = savedData.get(userID, "baz");
if (baz == null) {
baz = loadBazFromDB(userID);
}
qux = savedData.get(userID, "qux");
if (qux == null) {
qux = loadQuxFromDB(userID);
}
corge = savedData.get(userID, "corge");
if (corge == null) {
corge = loadCorgeFromDB(userID);
}
JSONObject json = new JSONObject();
json.put("baz", baz);
json.put("qux", qux);
json.put("corge", corge);
return new Response(json);
}
}

I would use generics and the Strategy Design Pattern.
I didn't really know what types and classes you could give up, since you provided a pseudo-code.
If I forgot to put something importante, please comment.
But I would go with something like this:
interface ResponseGetter<R> {
R getResponse(String userId, String dataToCheck, SavedData<R> savedData, Function<String, R> lambda);
}
class DB<R> {
public R executeQuery(String query) {
return null;
}
}
interface SavedData<R> {
public R get(String userId, String name);
}
class SomeClass<R> implements ResponseGetter<R> {
#Override
public R getResponse(String userId, String dataToCheck, SavedData<R> savedData, Function<String, R> lambda) {
R checkedData = savedData.get(userId, dataToCheck);
return checkedData == null ? loadDataFromDB(userId, lambda) : checkedData;
}
public R loadDataFromDB(String userId, Function<String, R> lambda) {
return lambda.apply(userId);
}
}
class SavedDataImpl<R> implements SavedData<R> {
#Override
public R get(String userId, String name) {
return null;
}
}
class Main {
public static void main(String[] args) {
// foo
SomeClass<List<String>> someClass = new SomeClass<>();
SavedDataImpl<List<String> savedDataImpl = new SavedDataImpl<>();
DB<List<String>> db = new DB<>();
List<String> foo = someClass.getResponse("1", "foo", savedDataImpl, (String userId) -> {
List<String> result = db.executeQuery("SELECT foo FROM SomeTable WHERE user_id=" + userId);
return result;
});
// bar
SomeClass<Integer> someClassTwo = new SomeClass<>();
SavedDataImpl<Integer savedDataImplTwo = new SavedDataImpl<>();
DB<Integer> dbTwo = new DB<>();
Integer bar = someClassTwo.getResponse("1", "bar", savedDataImplTwo, (String userId) -> {
Integer result = dbTwo.executeQuery("SELECT bar FROM SomeTable WHERE user_id=" + userId);
return result;
});
// bizz
SomeClass<String> someClassThree = new SomeClass<>();
SavedDataImpl<String savedDataImplThree = new SavedDataImpl<>();
DB<String> dbThree = new DB<>();
String bizz = someClassThree.getResponse("1", "bizz", savedDataImplThree, (String userId) -> {
String result = dbThree.executeQuery("SELECT bizz FROM SomeTable WHERE user_id=" + userId);
return result;
});
Map json = new HashMap();
json.put("foo", foo);
json.put("bar", bar);
json.put("bizz", bizz);
}
}
That way you are abstracting the behavior of the how to get the data and wich data to get.
I just formatted the response in a Map for simplicity.

Related

I have a Querydsl question. (Feat. BooleanBuilder)

#Test
void testDepartment() throws Exception {
String nameParam1 = "ComputerScience";
String nameParam2 = "Department of Mathematics";
String nameParam3 = "Department of Music";
String nameParam4 = "Department of Language and Literature";
String locParam = "null";
List<Department> result = seacrhDepartment(nameParam1, nameParam2, nameParam3, nameParam4, locParam);
assertThat(result.size()).isEqualTo(0);
}
private List<Department> seacrhDepartment(String nameParam1, String nameParam2, String nameParam3, String nameParam4, String locParam) {
BooleanBuilder builder = new BooleanBuilder();
if (nameParam1 != null) {
builder.and(department.name.eq(nameParam1));
}
if (nameParam2 != null) {
builder.and(department.name.eq(nameParam2));
}
if (nameParam3 != null) {
builder.and(department.name.eq(nameParam3));
}
if (nameParam4 != null) {
builder.and(department.name.eq(nameParam4));
}
if (locParam != null) {
builder.and(department.name.eq(locParam));
}
return queryFactory
.selectFrom(department)
.where(builder)
.fetch();
}
#BeforeEach
public void before() {
queryFactory = new JPAQueryFactory(em);
Department department1 = new Department("ComputerScience");
Department department2 = new Department("Department of Mathematics");
Department department3 = new Department("Department of Music");
Department department4 = new Department("Department of Language and Literature");
em.persist(department1);
em.persist(department2);
em.persist(department3);
em.persist(department4);
}
I pre-entered the data.. But Why does isEqual(0) have to be passed here..?
Since all conditions were set to builder.and, wouldn't it pass when set to isEqualTo(5)?
And what test should I use to search for the word com and get a computerscience department?
There is no Department that satisfied all filters. You probably want to use builder.or instead of builder.and.

Java service return the Result message along with Object class

Hi i have to return the ResultMessage along with my object class in my below code.
public List<EmployeeInformation> findAll(String IndexName) throws Exception {
String ResultMessage = new String();
if (Strings.isNullOrEmpty(IndexName)) {
ResultMessage= "Index name is null or empty";
} else if (isTextContainUpperCase(IndexName)) {
ResultMessage= "Index name should be in lowercase";
} else if (!checkEsConnection(client)) {
ResultMessage= "Elasticsearch deployment is not reachable";
}
SearchRequest searchRequest = new SearchRequest(IndexName);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchAllQuery());
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
return getSearchResult(searchResponse);
}
private List<EmployeeInformation> getSearchResult(SearchResponse response) {
SearchHit[] searchHit = response.getHits().getHits();
List<EmployeeInformation> EmployeeDocuments = new ArrayList<>();
for (SearchHit hit : searchHit) {
EmployeeDocuments
.add(objectMapper
.convertValue(hit
.getSourceAsMap(), EmployeeInformation.class));
}
return EmployeeDocuments;
}
What is the best way to return the ResultMessage along with my employeeinformation object. Could some one Modify the above code accordingly and post your answer or suggest me how to achieve this? thanks in advance
You can return ImmutablePair<ResultMessage, List<EmployeeInformation>>.
This objects then be accessed by getLeft() and getRight() methods. However, you will need org.apache.commons lib for this. It is worth adding this lib as it has many more such utils and wrapper classes. https://mvnrepository.com/artifact/org.apache.commons/commons-lang3
This code is my suggested sample code for your requirement.
public class Test {
public static void main(String args[]) {
Test test = new Test();
String dept = "test";
Object[] result = test.searchUser(dept);
// Here must and should be follow the initailize order.
String something = null;
EmployeeInformation employee = null;
if (result.length>= 0 && result[0] instanceof String) {
something = (String) result[0];
}
if (result.length>= 1 && result[1] instanceof EmployeeInformation) {
employee = (EmployeeInformation) result[1];
}
// do something
}
public Object[] searchUser(String dept) {
Object[] result = new Object[2];
result[0] = dept;
result[1] = getUser(dept);
return result;
}
private List<EmployeeInformation> getUser(String dept) {
List<EmployeeInformation> userList = null;
// some logic for get the EmployeeInformationInformation List based on
// department.
return userList;
}
}

Why does this REST-service not return while the other does?

SITUATION
In the code below you can see 2 REST services which both should return a MessageVO. The first service (serviceThatDoesWork) returns a MessageVO as excpected, but the second service (serviceThatDoesNotWork) refuses to, it doesn't even give any output at all.
However returning a Response (java.ws.rs.core.Response) with serviceThatDoesNotWork does give an output. Even when I skip the 'doStuff'-methods and create a dummy-MessageVO that is exactly the same for each service, the 2nd one doesn't return anything.
QUESTION
Why does the 2nd service fail to return a MessageVO? It doens't return anything when I try returning a MessageVO, and nothing out of the ordinary appears in the logging.
The two services need to return exactly the same kind of thing but still one of them doesn't want to return anything, what am I not seeing here?
Could it be because of the path (and/or the amount of parameters)?
CODE
MyServices.java:
#Path("/myService")
...
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("/myPath/{param1}/{param2}/{param3}")
public MessageVO serviceThatDoesWork(#PathParam("param1") Integer param1_id, #PathParam("param2") Integer param2_id, #PathParam("param2") Integer param2_id)
{
List<SomethingVO> lstO = MyRestServiceBusiness.doStuff(param1_id, param2_id, param3_id);
//return SUCCESS or FAIL message
MessageVO msg = new MessageVO();
if(lstO.size() > 0)
{
List<String> s = new ArrayList<String>();
for(SomethingVO k : lstO)
{
s.add(k.getId().toString());
}
msg.setItem_ids(s);
msg.setMsg("SUCCESS");
}
else
{
msg.setMsg("FAIL");
}
return msg;
}
...
#GET
#Path("/myPath/{param1}/{param2}/{param3}/{param4}/.../{param15}{a:(/a/[^/]+?)?}{b:(/b/[^/]+?)?}")
public Response serviceThatDoesNotWork(#PathParam("param1")Integer param1_id, ..., #PathParam("param15") Integer param15_id,
#PathParam("a") String a_id, #PathParam("b") String b_id)
{
//PUT 'OPTIONAL' PARAMS IN A LIST
List<Integer> lstI = new ArrayList<Integer>();
String aId = a_id != null ? a_id.split("/")[2] : null;
String bId = b_id != null ? b_id.split("/")[2] : null;
if(aId != null)
{
lstI.add(Integer.parseInt(aId ));
}
if(bId != null)
{
lstI.add(Integer.parseInt(bId ));
}
//DO STUFF
String afsId = "";
if(lstI.size() > 0)
{
afsId = MyRestServiceBusiness.doStuff(param1, ..., lstI);
}
//return SUCCESS or FAIL message
MessageVO msg = new MessageVO();
if(afsId != null && !afsId.isEmpty())
{
List<String> s = new ArrayList<String>();
s.add(afsId);
msg.setItem_ids(s);
msg.setMsg("SUCCESS");
}
else
{
List<String> s = new ArrayList<String>();
for(Integer i : lstI)
{
s.add(i.toString());
}
msg.setItem_ids(s);
msg.setMsg("FAIL");
}
//WENT THROUGH ALL ABOVE CODE AS EXPECTED, MESSAGEVO HAS BEEN FILLED PROPERLY
return msg;
}
CODE MessageVO.java:
#XmlRootElement
public class MessageVO
{
private String msg;
private List<String> item_ids;
//GETTERS
#XmlElement(name = "Message")
public String getMsg() {
return msg;
}
#XmlElement(name = "Item ID's")
public List<String> getItem_ids() {
return item_ids;
}
//SETTERS
public void setMsg(String msg) {
this.msg = msg;
}
public void setItem_ids(List<String> item_ids) {
this.item_ids = item_ids;
}
If I need to provide extra information please ask, this is my first attempt at (REST-) services.
As Vaseph mentioned in a comment I just forgot the #Produces annotation in the 2nd service.

How to Mock repository Items in ATG

I am trying to create a Mock class for droplet. I am able to mock the repository calls and req.getParameter but need help on how to mock the repository item list from the repository. Below is the sample code.
for (final RepositoryItem item : skuList) {
final String skuId = (String) item.getPropertyValue("id");
final String skuType = (String) item.getPropertyValue("skuType");
if (this.isLoggingDebug()) {
this.logDebug("skuType [ " + skuType + " ]");
}
final String skuActive = (String) item.getPropertyValue("isActive");
if EJSD.equalsIgnoreCase(skuType) && (skuActive.equals("1"))) {
eSkuList.add(item);
skuCode = (String) item.getPropertyValue(ESTConstants.SKU_MISC1);
} else (PJPROMIS.equalsIgnoreCase(skuType) && skuId.contains("PP") && (skuActive.equals("1"))) {
personalSkuList.add(item);
String tmp = "";
if (skuId.lastIndexOf("-") > -1) {
tmp = skuId.substring(skuId.lastIndexOf("-") + 1);
tmp = tmp.toUpperCase();
if (this.getDefaultDisplayNameMap() != null) {
String val = this.getDefaultDisplayNameMap().get(tmp);
if (StringUtils.isNotEmpty(val)) {
displayNameMap.put(skuId, val);
} else {
val = (String) item.getPropertyValue("displayName");
displayNameMap.put(skuId, val);
}
} else {
final String val = (String) item.getPropertyValue("displayName");
displayNameMap.put(skuId, val);
}
}
}
}
There are a multitude of ways to 'mock' the list. I've been doing it this was as I feel it is more readable.
#Mock private RepositoryItem skuMockA;
#Mock private RepositoryItem skuMockB;
List<RepositoryItem> skuList = new ArrayList<RepositoryItem>();
#BeforeMethod(groups = { "unit" })
public void setup() throws Exception {
testObj = new YourDropletName();
MockitoAnnotations.initMocks(this);
skuList = new ArrayList<RepositoryItem>();
skuList.add(skuMockA);
skuList.add(skuMockB);
Mockito.when(skuMockA.getPropertyValue("id")).thenReturn("skuA");
Mockito.when(skuMockA.getPropertyValue("skuType")).thenReturn(ActiveSkuDroplet.EJSD);
Mockito.when(skuMockA.getPropertyValue(ESTConstants.SKU_MISC1)).thenReturn("skuCodeA");
Mockito.when(skuMockA.getPropertyValue("displayName")).thenReturn("skuADisplayName");
Mockito.when(skuMockB.getPropertyValue("id")).thenReturn("skuB-PP");
Mockito.when(skuMockB.getPropertyValue("skuType")).thenReturn(ActiveSkuDroplet.PJPROMIS);
Mockito.when(skuMockB.getPropertyValue(ESTConstants.SKU_MISC1)).thenReturn("skuCodeB");
Mockito.when(skuMockB.getPropertyValue("displayName")).thenReturn("skuBDisplayName");
}
So when you then call this within a test it will be something like this:
Mockito.when(someMethodThatReturnsAList).thenReturn(skuList);
So the key really is that you are not mocking the List but instead the contents of the List.
Creating a mock using mockito is a good option.
But I am here explaining a different way of mocking the repository item.
Create a common implementation for RepositoryItem, say MockRepositoryItemImpl like this in your test package.
Public MockRepositoryItemImpl implements RepositoryItem {
private Map<String, Object> properties;
MockRepositoryItemImpl(){
properties = new HashMap<>();
}
#override
public Object getPropertyValue(String propertyName){
return properties.get(propertyName);
}
#override
public void setPropertyValue(String propertyName, Object propertyValue){
properties.put(propertyName, propertyValue);
}
}
Use this implementation to create the mock object in your test case.
RepositoryItem mockSKU = new MockRepositoryItemImpl();
mockSKU.setPropertyValue("id", "sku0001");
mockSKU.setPropertyValue("displayName", "Mock SKU");
mockSKU.setPropertyValue("skuType", "Type1");
mockSKU.setPropertyValue("isActive", "1");

How to intercept and change sql query dynamically in mybatis

I use mybatis to perform sql queries in my project. I need to intercept sql query before executing to apply some changed dynamically. I've read about #Interseptors like this:
#Intercepts({#Signature(type= Executor.class, method = "query", args = {...})})
public class ExamplePlugin implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
return invocation.proceed();
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public void setProperties(Properties properties) {
}
}
And it really intercepts executions, but there is no way to change sql query since appropriate field is not writable. Should I build new instance of whole object manually to just replace sql query? Where is the right place to intercept query execution to change it dynamically? Thank.
I hope it will help you:
#Intercepts( { #Signature(type = Executor.class, method = "query", args = {
MappedStatement.class, Object.class, RowBounds.class,
ResultHandler.class
})
})
public class SelectCountSqlInterceptor2 implements Interceptor
{
public static String COUNT = "_count";
private static int MAPPED_STATEMENT_INDEX = 0;
private static int PARAMETER_INDEX = 1;
#Override
public Object intercept(Invocation invocation) throws Throwable
{
processCountSql(invocation.getArgs());
return invocation.proceed();
}
#SuppressWarnings("rawtypes")
private void processCountSql(final Object[] queryArgs)
{
if (queryArgs[PARAMETER_INDEX] instanceof Map)
{
Map parameter = (Map) queryArgs[PARAMETER_INDEX];
if (parameter.containsKey(COUNT))
{
MappedStatement ms = (MappedStatement) queryArgs[MAPPED_STATEMENT_INDEX];
BoundSql boundSql = ms.getBoundSql(parameter);
String sql = ms.getBoundSql(parameter).getSql().trim();
BoundSql newBoundSql = new BoundSql(ms.getConfiguration(),
getCountSQL(sql), boundSql.getParameterMappings(),
boundSql.getParameterObject());
MappedStatement newMs = copyFromMappedStatement(ms,
new OffsetLimitInterceptor.BoundSqlSqlSource(newBoundSql));
queryArgs[MAPPED_STATEMENT_INDEX] = newMs;
}
}
}
// see: MapperBuilderAssistant
#SuppressWarnings({ "unchecked", "rawtypes" })
private MappedStatement copyFromMappedStatement(MappedStatement ms,
SqlSource newSqlSource)
{
Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms
.getId(), newSqlSource, ms.getSqlCommandType());
builder.resource(ms.getResource());
builder.fetchSize(ms.getFetchSize());
builder.statementType(ms.getStatementType());
builder.keyGenerator(ms.getKeyGenerator());
// setStatementTimeout()
builder.timeout(ms.getTimeout());
// setParameterMap()
builder.parameterMap(ms.getParameterMap());
// setStatementResultMap()
List<ResultMap> resultMaps = new ArrayList<ResultMap>();
String id = "-inline";
if (ms.getResultMaps() != null)
{
id = ms.getResultMaps().get(0).getId() + "-inline";
}
ResultMap resultMap = new ResultMap.Builder(null, id, Long.class,
new ArrayList()).build();
resultMaps.add(resultMap);
builder.resultMaps(resultMaps);
builder.resultSetType(ms.getResultSetType());
// setStatementCache()
builder.cache(ms.getCache());
builder.flushCacheRequired(ms.isFlushCacheRequired());
builder.useCache(ms.isUseCache());
return builder.build();
}
private String getCountSQL(String sql)
{
String lowerCaseSQL = sql.toLowerCase().replace("\n", " ").replace("\t", " ");
int index = lowerCaseSQL.indexOf(" order ");
if (index != -1)
{
sql = sql.substring(0, index);
}
return "SELECT COUNT(*) from ( select 1 as col_c " + sql.substring(lowerCaseSQL.indexOf(" from ")) + " ) cnt";
}
#Override
public Object plugin(Object target)
{
return Plugin.wrap(target, this);
}
#Override
public void setProperties(Properties properties)
{
}
}
You may consider using a string template library (eg Velocity, Handlebars, Mustache) to help you
As of to date, there is even MyBatis-Velocity (http://mybatis.github.io/velocity-scripting/) to help you to do scripting for the sql.
Depending on the changes you want to make, you may want to use the dynamic sql feature of mybatis 3

Categories