How to compare two URLs in java? - java

Here's a simple problem - given two urls, is there some built-in method, or an Apache library that decides whether they are (logically) equal?
For example, these two urls are equal:
http://stackoverflow.com
http://stackoverflow.com/

While URI.equals() (as well as the problematic URL.equals()) does not return true for these specific examples, I think it's the only case where equivalence can be assumed (because there is no empty path in the HTTP protocol).
The URIs http://stackoverflow.com/foo and http://stackoverflow.com/foo/ can not be assumed to be equivalent.
Maybe you can use URI.equals() wrapped in a utility method that handles this specific case explicitly.

URL::equals reference
URL urlOne = new URL("http://stackoverflow.com");
URL urlTwo = new URL("http://stackoverflow.com/");
if( urlOne.equals(urlTwo) )
{
// ....
}
Note from docs -
Two URL objects are equal if they have the same protocol, reference equivalent hosts, have the same port number on the host, and the same file and fragment of the file.
Two hosts are considered equivalent if both host names can be resolved into the same IP addresses; else if either host name can't be resolved, the host names must be equal without regard to case; or both host names equal to null.
Since hosts comparison requires name resolution, this operation is a blocking operation.
Note: The defined behavior for equals is known to be inconsistent with virtual hosting in HTTP.
So, instead you should prefer URI::equals reference as #Joachim suggested.

The following may work for you - it validates that 2 urls are equal, allows the parameters to be supplied in different orders, and allows a variety of options to be configured, that being:
Is the host case sensitive
Is the path case sensitive
Are query string parameters case sensitive
Are query string values case sensitive
Is the scheme case sensitive
You can test it like so:
class Main {
public static void main(String[] args)
{
UrlComparer urlComparer = new UrlComparer();
expectResult(false, "key a case different", urlComparer.urlsMatch("//test.com?A=a&B=b", "//test.com?a=a&b=b"));
expectResult(false, "key a case different", urlComparer.urlsMatch("https://WWW.TEST.COM?A=1&b=2", "https://www.test.com?b=2&a=1"));
expectResult(false, "key a value different", urlComparer.urlsMatch("/test?a=2&A=A", "/test?a=A&a=2"));
expectResult(false, "key a value different", urlComparer.urlsMatch("https://WWW.TEST.COM?A=a&b=2", "https://www.test.com?b=2&A=1"));
expectResult(false, "null", urlComparer.urlsMatch("/test", null));
expectResult(false, "null", urlComparer.urlsMatch(null, "/test"));
expectResult(false, "port different", urlComparer.urlsMatch("//test.com:22?A=a&B=b", "//test.com:443?A=a&B=b"));
expectResult(false, "port different", urlComparer.urlsMatch("https://WWW.TEST.COM:8443", "https://www.test.com"));
expectResult(false, "protocol different", urlComparer.urlsMatch("http://WWW.TEST.COM:2121", "https://www.test.com:2121"));
expectResult(false, "protocol different", urlComparer.urlsMatch("http://WWW.TEST.COM?A=a&b=2", "https://www.test.com?b=2&A=a"));
expectResult(true, "both null", urlComparer.urlsMatch(null, null));
expectResult(true, "host and scheme different case", urlComparer.urlsMatch("HTTPS://WWW.TEST.COM", "https://www.test.com"));
expectResult(true, "host different case", urlComparer.urlsMatch("https://WWW.TEST.COM:443", "https://www.test.com"));
expectResult(true, "identical urls", urlComparer.urlsMatch("//test.com:443?A=a&B=b", "//test.com:443?A=a&B=b"));
expectResult(true, "identical urls", urlComparer.urlsMatch("/test?a=A&a=2", "/test?a=A&a=2"));
expectResult(true, "identical urls", urlComparer.urlsMatch("https://www.test.com", "https://www.test.com"));
expectResult(true, "parameter order changed", urlComparer.urlsMatch("https://www.test.com?a=1&b=2&c=522%2fMe", "https://www.test.com?c=522%2fMe&b=2&a=1"));
expectResult(true, "parmeter order changed", urlComparer.urlsMatch("https://WWW.TEST.COM?a=1&b=2", "https://www.test.com?b=2&a=1"));
}
public static void expectResult(boolean expectedResult, String msg, boolean result)
{
if (expectedResult != result)
throw new RuntimeException(msg);
}
}
UrlComparer.java
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
public class UrlComparer
{
private boolean hostIsCaseSensitive = false;
private boolean pathIsCaseSensitive = true;
private boolean queryStringKeysAreCaseSensitive = true;
private boolean queryStringValuesAreCaseSensitive = false;
private boolean schemeIsCaseSensitive = false;
public boolean urlsMatch(String url1, String url2)
{
try
{
if (Objects.equals(url1, url2))
return true;
URI uri1 = new URI(url1);
URI uri2 = new URI(url2);
// Compare Query String Parameters
Map<String, String> mapParams1 = getQueryStringParams(uri1);
Map<String, String> mapParams2 = getQueryStringParams(uri2);
if (!mapsAreEqual(mapParams1, mapParams2, getQueryStringValuesAreCaseSensitive()))
return false;
// Compare scheme (http or https)
if (!stringsAreEqual(uri1.getScheme(), uri2.getScheme(), getSchemeIsCaseSensitive()))
return false;
// Compare host
if (!stringsAreEqual(uri1.getHost(), uri2.getHost(), getHostIsCaseSensitive()))
return false;
// Compare path
if (!stringsAreEqual(uri1.getPath(), uri2.getPath(), getPathIsCaseSensitive()))
return false;
// Compare ports
if (!portsAreEqual(uri1, uri2))
return false;
return true;
}
catch (Exception e)
{
return false;
}
}
protected Map<String, String> getQueryStringParams(URI uri)
{
Map<String, String> result = getListAsMap(URLEncodedUtils.parse(uri, "UTF-8"), getQueryStringKeysAreCaseSensitive());
return result;
}
protected boolean stringsAreEqual(String s1, String s2, boolean caseSensitive)
{
// Eliminate null cases
if (s1 == null || s2 == null)
{
if (s1 == s2)
return true;
return false;
}
if (caseSensitive)
{
return s1.equals(s2);
}
return s1.equalsIgnoreCase(s2);
}
protected boolean mapsAreEqual(Map<String, String> map1, Map<String, String> map2, boolean caseSensitiveValues)
{
for (Map.Entry<String, String> entry : map1.entrySet())
{
String key = entry.getKey();
String map1value = entry.getValue();
String map2value = map2.get(key);
if (!stringsAreEqual(map1value, map2value, caseSensitiveValues))
return false;
}
for (Map.Entry<String, String> entry : map2.entrySet())
{
String key = entry.getKey();
String map2value = entry.getValue();
String map1value = map2.get(key);
if (!stringsAreEqual(map1value, map2value, caseSensitiveValues))
return false;
}
return true;
}
protected boolean portsAreEqual(URI uri1, URI uri2)
{
int port1 = uri1.getPort();
int port2 = uri2.getPort();
if (port1 == port2)
return true;
if (port1 == -1)
{
String scheme1 = (uri1.getScheme() == null ? "http" : uri1.getScheme()).toLowerCase();
port1 = scheme1.equals("http") ? 80 : 443;
}
if (port2 == -1)
{
String scheme2 = (uri2.getScheme() == null ? "http" : uri2.getScheme()).toLowerCase();
port2 = scheme2.equals("http") ? 80 : 443;
}
boolean result = (port1 == port2);
return result;
}
protected Map<String, String> getListAsMap(List<NameValuePair> list, boolean caseSensitiveKeys)
{
Map<String, String> result;
if (caseSensitiveKeys)
{
result = new HashMap<String, String>();
}
else
{
result = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
}
for (NameValuePair param : list)
{
if (caseSensitiveKeys)
{
if (!result.containsKey(param.getName()))
result.put(param.getName(), param.getValue());
}
else
{
result.put(param.getName(), param.getValue());
}
}
return result;
}
public boolean getSchemeIsCaseSensitive()
{
return schemeIsCaseSensitive;
}
public void setSchemeIsCaseSensitive(boolean schemeIsCaseSensitive)
{
this.schemeIsCaseSensitive = schemeIsCaseSensitive;
}
public boolean getHostIsCaseSensitive()
{
return hostIsCaseSensitive;
}
public void setHostIsCaseSensitive(boolean hostIsCaseSensitive)
{
this.hostIsCaseSensitive = hostIsCaseSensitive;
}
public boolean getPathIsCaseSensitive()
{
return pathIsCaseSensitive;
}
public void setPathIsCaseSensitive(boolean pathIsCaseSensitive)
{
this.pathIsCaseSensitive = pathIsCaseSensitive;
}
public boolean getQueryStringKeysAreCaseSensitive()
{
return queryStringKeysAreCaseSensitive;
}
public void setQueryStringKeysAreCaseSensitive(boolean queryStringKeysAreCaseSensitive)
{
this.queryStringKeysAreCaseSensitive = queryStringKeysAreCaseSensitive;
}
public boolean getQueryStringValuesAreCaseSensitive()
{
return queryStringValuesAreCaseSensitive;
}
public void setQueryStringValuesAreCaseSensitive(boolean queryStringValuesAreCaseSensitive)
{
this.queryStringValuesAreCaseSensitive = queryStringValuesAreCaseSensitive;
}
}

sameFile
public boolean sameFile(URL other)Compares two URLs,
excluding the fragment component.
Returns true if this URL and the other argument are equal without taking the fragment component into consideration.
Parameters:
other - the URL to compare against.
Returns:
true if they reference the same remote object; false otherwise.
also please go through this link
http://download.oracle.com/javase/6/docs/api/java/net/URL.html#sameFile(java.net.URL)
As iam unable to add comment , browser throwing Javascript error. so iam adding my comment here. regret for inconvience.
//this what i suggeted
>URL url1 = new URL("http://stackoverflow.com/foo");
>URL url2 = new URL("http://stackoverflow.com/foo/");
>System.out.println(url1.sameFile(url2));
// this is suggested by Joachim Sauer
>URI uri = new URI("http://stackoverflow.com/foo/");
>System.out.println(uri.equals("http://stackoverflow.com/foo"));
// Both are giving same result
so Joachim Sauer check once.

Related

Check if JSONObject matches JSON boolean expression

To explain correctly the problem I must start with an example let's say I have a list of users like this
[
{ "name": "John", "surname": "Doe", "age": 22 },
{ "name": "Syrus", "surname": "Black", "age": 20 }
]
And I have also a JSONObject representing a condition that must be matched like this:
{
"OR":[
{ "name": { "eq": "John"} },
{ "AND":[
{ "name": { "eq": "Syrus"} },
{ "age": { "gt": 18 } }
] }
]
}
Which should be translated in:
name = "John" OR (name = "Syrus" AND age > 18)
Now I have to make the code that given the JSONObject condition and the list of the users checks for each users if the condition is matched.
At the moment this is what I have done:
import java.util.Set;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.BiConsumer;
import com.query.Queryable;
import org.json.JSONArray;
import org.json.JSONObject;
public class QueryableTreeMap<K,V extends JSONObject> extends TreeMap<K,V> implements Queryable<JSONObject,JSONObject> {
private static final long serialVersionUID = 2586026774025401270L;
private static boolean test(Set<Map.Entry<String, Object>> condition, JSONObject value){
boolean isValid = true;
Iterator<Map.Entry<String, Object>> iter = condition.iterator();
while(iter.hasNext()){
Map.Entry<String, Object> subcond = iter.next();
if(subcond.getKey().equals("OR")){
//isValid = isValid || test((Set<Map.Entry<String, Object>>) subcond.getValue(), value);
} else if(subcond.getKey().equals("AND")){
//isValid = isValid && test((Set<Map.Entry<String, Object>>) subcond.getValue(), value);
} else if(subcond.getKey().equals("NOT")){
} else {
}
}
return isValid;
}
#Override
public JSONObject query(JSONObject query) {
// the set containing the conditions
Set<Map.Entry<String, Object>> entries = query.toMap().entrySet();
// the JSONArray with containing the records that match the condition
JSONArray array = new JSONArray();
// for each JSONObject inside this structure
this.forEach(new BiConsumer<K,JSONObject>(){
#Override
public void accept(K key, JSONObject value) {
// testing if the current record matches the condition
if(test(entries, value)) array.put(value);
}
});
// returns a JSONObject containing a JSONArray that contains the records that match the condition
return new JSONObject(array);
}
}
I am currently stuck in the test method which should in-fact test if the given object matches the given condition.
I don't mind changing the format of the JSON condition as long as it is a JSONObject.
At the moment i have come up with a partial solution that builds an object called Condition that represents the boolean expression inside the JSONObject (not very efficient but still a possible solution) this is obviously not working at the moment, i need help on what i should do now
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Condition {
private Condition left, right;
private String boolExpr, key, value;
private boolean not;
private int operationType; // greater then, less then, equal to, greater or equal of, less or equal of
public Condition(Map<String, Object> map) {
Set<Map.Entry<String, Object>> set = map.entrySet();
Iterator<Map.Entry<String, Object>> iterator = set.iterator();
while(iterator.hasNext()){
Object entry = iterator.next();
System.out.println(entry);
System.out.println(entry.getClass());
if(entry instanceof Map.Entry) {
String key = (String) ((Map.Entry) entry).getKey();
switch(key){
case "AND":
case "OR":
this.boolExpr = key;
break;
case "eq":
case "gt":
case "lt":
case "gte":
case "lte":
this.operationType = getOperationTypeFromString(key);
break;
case "NOT":
this.not = true;
break;
default:
this.key = key;
break;
}
}
}
}
public int getOperationTypeFromString(String operation) {
switch(operation){
case "eq":
return 0;
case "gt":
return 1;
case "lt":
return 2;
case "gte":
return 3;
case "lte":
return 4;
default:
return 0;
}
}
}
I would prefer not to use the Condition class and just use the JSONObject instead.
I am using the org.json JSON-Java parser.
Edit: code updated to use org.json.
Below is a working implementation that handles your example.
The function that actually does the work is match, which recursively traverses the filter and applies each node to the supplied object. The function returns true if the given object satisfies the given filter.
Conceptually the match function is mapping each construct in your filter language (AND, OR, EQ, LT etc) into its Java equivalent. E.g. AND maps to Stream.allMatch, NOT maps to the Java ! operator, EQ maps to Object.equals, etc. I.e. match is defining the semantics for your filter language.
I hope the code is self-explanatory - let me know if anything is unclear.
import org.json.*;
import java.io.*;
import java.nio.file.*;
import java.util.List;
import java.util.stream.*;
import static java.util.stream.Collectors.toList;
public class Test {
public static void main(String[] args) throws IOException {
final List<JSONObject> jsObjs =
stream(readJsonArray("users.json"))
.map(JSONObject.class::cast)
.collect(toList());
final JSONObject jsFilter = readJsonObject("filter.json");
final List<JSONObject> matches = applyFilter(jsObjs, jsFilter);
System.out.println(matches);
}
private static List<JSONObject> applyFilter(List<JSONObject> jsObjs, JSONObject jsFilter) {
return jsObjs.stream()
.filter(jsObj -> match(jsObj, jsFilter))
.collect(toList());
}
private static boolean match(JSONObject jsObj, JSONObject jsFilter) {
final String name = getSingleKey(jsFilter);
final Object value = jsFilter.get(name);
switch (name) {
case "AND":
return stream((JSONArray)value)
.map(JSONObject.class::cast)
.allMatch(jse -> match(jsObj, jse));
case "OR":
return stream((JSONArray)value)
.map(JSONObject.class::cast)
.anyMatch(jse -> match(jsObj, jse));
case "NOT":
return !match(jsObj, (JSONObject)((JSONArray)value).get(0));
default:
final JSONObject jsOp = (JSONObject)value;
final String operator = getSingleKey(jsOp);
final Object operand = jsOp.get(operator);
switch (operator) {
case "eq": return jsObj.get(name).equals(operand);
case "lt": return (Integer)jsObj.get(name) < (Integer)operand;
case "gt": return (Integer)jsObj.get(name) > (Integer)operand;
default: throw new IllegalArgumentException("Unexpected operator: " + operator);
}
}
}
private static JSONObject readJsonObject(String fileName) throws IOException {
try (Reader reader = Files.newBufferedReader(Paths.get(fileName))) {
return new JSONObject(new JSONTokener(reader));
}
}
private static JSONArray readJsonArray(String fileName) throws IOException {
try (Reader reader = Files.newBufferedReader(Paths.get(fileName))) {
return new JSONArray(new JSONTokener(reader));
}
}
private static Stream<Object> stream(JSONArray jsa) {
return StreamSupport.stream(jsa.spliterator(), false);
}
private static String getSingleKey(JSONObject jso) {
if (jso.length() != 1) {
throw new IllegalArgumentException("Expected single entry");
} else {
return jso.keySet().iterator().next();
}
}
}

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.

Hibernate gives a strange ClassCast exception (using Transformers)

This code:
#Override
public List<FactCodeDto> getAllFactsWithoutParentsAsFactDto() {
String completeQuery = FactCodeQueries.SELECT_DTO_FROM_FACT_WITH_NO_PARENTS;
Query query = createHibernateQueryForUnmappedTypeFactDto(completeQuery);
List<FactCodeDto> factDtoList = query.list(); //line 133
return factDtoList;
}
calling this method:
private Query createHibernateQueryForUnmappedTypeFactDto(String sqlQuery) throws HibernateException {
return FactCodeQueries.addScalars(createSQLQuery(sqlQuery)).setResultTransformer(Transformers.aliasToBean(FactCodeDto.class));
}
gives me a ClassCastException -> part of the trace:
Caused by: java.lang.ClassCastException: org.bamboomy.cjr.dto.FactCodeDto cannot be cast to java.util.Map
at org.hibernate.property.access.internal.PropertyAccessMapImpl$SetterImpl.set(PropertyAccessMapImpl.java:102)
at org.hibernate.transform.AliasToBeanResultTransformer.transformTuple(AliasToBeanResultTransformer.java:78)
at org.hibernate.hql.internal.HolderInstantiator.instantiate(HolderInstantiator.java:75)
at org.hibernate.loader.custom.CustomLoader.getResultList(CustomLoader.java:435)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2423)
at org.hibernate.loader.Loader.list(Loader.java:2418)
at org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:336)
at org.hibernate.internal.SessionImpl.listCustomQuery(SessionImpl.java:1898)
at org.hibernate.internal.AbstractSessionImpl.list(AbstractSessionImpl.java:318)
at org.hibernate.internal.SQLQueryImpl.list(SQLQueryImpl.java:125)
at org.bamboomy.cjr.dao.factcode.FactCodeDAOImpl.getAllFactsWithoutParentsAsFactDto(FactCodeDAOImpl.java:133)
Which is pretty strange because, indeed, if you look up the source code of Hibernate it tries to do this:
#Override
#SuppressWarnings("unchecked")
public void set(Object target, Object value, SessionFactoryImplementor factory) {
( (Map) target ).put( propertyName, value ); //line 102
}
Which doesn't make any sense...
target is of type Class and this code tries to cast it to Map,
why does it try to do that???
any pointers are more than welcome...
I'm using Hibernate 5 (and am upgrading from 3)...
edit: I also use Spring (4.2.1.RELEASE; also upgrading) which calls these methods upon deploy, any debugging pointers are most welcome as well...
edit 2: (the whole FactCodeDto class, as requested)
package org.bamboomy.cjr.dto;
import org.bamboomy.cjr.model.FactCode;
import org.bamboomy.cjr.model.FactCodeType;
import org.bamboomy.cjr.utility.FullDateUtil;
import org.bamboomy.cjr.utility.Locales;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.util.Assert;
import java.util.*;
#Getter
#Setter
#ToString
public class FactCodeDto extends TreeNodeValue {
private String cdFact;
private String cdFactSuffix;
private Boolean isSupplementCode;
private Boolean isTitleCode;
private Boolean mustBeFollowed;
private Date activeFrom;
private Date activeTo;
private Boolean isCode;
private Long idFact;
private Long idParent;
private String type;
Map<Locale, String> description = new HashMap<Locale, String>(3);
public FactCodeDto() {
}
public FactCodeDto(String prefix, String suffix) {
super();
this.cdFact = prefix;
this.cdFactSuffix = suffix;
}
public FactCodeDto(String cdFact, String cdFactSuffix, Boolean isSupplementCode, Boolean mustBeFollowed) {
super();
this.cdFact = cdFact;
this.cdFactSuffix = cdFactSuffix;
this.isSupplementCode = isSupplementCode;
this.mustBeFollowed = mustBeFollowed;
}
public FactCodeDto(String cdFact, String cdFactSuffix, Boolean isSupplementCode, Boolean mustBeFollowed, Long idFact, Long idParent, Boolean isCode, Boolean isTitleCode, Date from, Date to, Map<Locale, String> descriptions,String type) {
super();
this.cdFact = cdFact;
this.cdFactSuffix = cdFactSuffix;
this.isSupplementCode = isSupplementCode;
this.mustBeFollowed = mustBeFollowed;
this.idFact = idFact;
this.idParent = idParent;
this.isCode = isCode;
this.isTitleCode = isTitleCode;
this.activeFrom = from;
this.activeTo = to;
if (descriptions != null) {
this.description = descriptions;
}
this.type = type;
}
public FactCodeDto(FactCode fc) {
this(fc.getPrefix(), fc.getSuffix(), fc.isSupplementCode(), fc.isHasMandatorySupplCodes(), fc.getId(), fc.getParent(), fc.isActualCode(), fc.isTitleCode(), fc.getActiveFrom(), fc.getActiveTo(), fc.getAllDesc(),fc.getType().getCode());
}
public String formatCode() {
return FactCode.formatCode(cdFact, cdFactSuffix);
}
public boolean isActive() {
Date now = new Date(System.currentTimeMillis());
return FullDateUtil.isBetweenDates(now, this.activeFrom, this.activeTo);
}
public void setDescFr(String s) {
description.put(Locales.FRENCH, s);
}
public void setDescNl(String s) {
description.put(Locales.DUTCH, s);
}
public void setDescDe(String s) {
description.put(Locales.GERMAN, s);
}
/**
* public String toString() {
* StringBuilder sb = new StringBuilder();
* sb.append(getIdFact() + ": ")
* .append(getIdParent() + ": ")
* .append(" " + cdFact + cdFactSuffix + ": " + (isSupplementCode ? "NO Principal " : " Principal "))
* .append((mustBeFollowed ? " Must Be Followed " : "NOT Must Be Followed "));
* return sb.toString();
* }
*/
public Map<Locale, String> getDescription() {
return description;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
String fullCode = formatCode();
result = prime * result + ((fullCode == null) ? 0 : fullCode.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
FactCodeDto other = (FactCodeDto) obj;
return formatCode().equals(other.formatCode());
}
#Override
public boolean isChildOf(TreeNodeValue value) {
Assert.notNull(value);
boolean isChild = false;
if (value instanceof FactCodeDto) {
if (this.getIdParent() != null) {
isChild = this.getIdParent().equals(((FactCodeDto) value).getIdFact());
}
}
return isChild;
}
#Override
public boolean isBrotherOf(TreeNodeValue value) {
Assert.notNull(value);
boolean isBrother = false;
if (value instanceof FactCodeDto) {
if (this.getIdParent() != null) {
isBrother = this.getIdParent().equals(((FactCodeDto) value).getIdParent());
}
}
return isBrother;
}
#Override
public boolean isParentOf(TreeNodeValue value) {
Assert.notNull(value);
boolean isParent = false;
if (value instanceof FactCodeDto) {
isParent = this.getIdFact().equals(((FactCodeDto) value).getIdParent());
}
return isParent;
}
#Override
public int compareTo(TreeNodeValue to) {
if (to instanceof FactCodeDto) {
return formatCode().compareTo(((FactCodeDto) to).formatCode());
} else return 1;
}
public String getCode() {
return formatCode();
}
}
I found that AliasToBean has changed in Hibernate 5. For me adding getter for my field fixed the problem.
This exception occurs when the setters and getters are not mapped correctly to the column names.
Make sure you have the correct getters and setters for the query(Correct names and correct datatypes).
Read more about it here:
http://javahonk.com/java-lang-classcastexception-com-wfs-otc-datamodels-imagineexpirymodel-cannot-cast-java-util-map/
I do some investigation on this question. The problem is that Hibernate converts aliases for column names to upper case — cdFact becomesCDFACT.
Read for a more deeply explanation and workaround here:
mapping Hibernate query results to custom class?
In the end it wasn't so hard to find a solution,
I just created my own (custom) ResultTransformer and specified that in the setResultTransformer method:
private Query createHibernateQueryForUnmappedTypeFactDto(String sqlQuery) throws HibernateException {
return FactCodeQueries.addScalars(createSQLQuery(sqlQuery)).setResultTransformer(new FactCodeDtoResultTransformer());
//return FactCodeQueries.addScalars(createSQLQuery(sqlQuery)).setResultTransformer(Transformers.aliasToBean(FactCodeDto.class));
}
the code of the custom result transformer:
package org.bamboomy.cjr.dao.factcode;
import org.bamboomy.cjr.dto.FactCodeDto;
import java.util.Date;
import java.util.List;
/**
* Created by a162299 on 3-11-2015.
*/
public class FactCodeDtoResultTransformer implements org.hibernate.transform.ResultTransformer {
#Override
public Object transformTuple(Object[] objects, String[] strings) {
FactCodeDto result = new FactCodeDto();
for (int i = 0; i < objects.length; i++) {
setField(result, strings[i], objects[i]);
}
return result;
}
private void setField(FactCodeDto result, String string, Object object) {
if (string.equalsIgnoreCase("cdFact")) {
result.setCdFact((String) object);
} else if (string.equalsIgnoreCase("cdFactSuffix")) {
result.setCdFactSuffix((String) object);
} else if (string.equalsIgnoreCase("isSupplementCode")) {
result.setIsSupplementCode((Boolean) object);
} else if (string.equalsIgnoreCase("isTitleCode")) {
result.setIsTitleCode((Boolean) object);
} else if (string.equalsIgnoreCase("mustBeFollowed")) {
result.setMustBeFollowed((Boolean) object);
} else if (string.equalsIgnoreCase("activeFrom")) {
result.setActiveFrom((Date) object);
} else if (string.equalsIgnoreCase("activeTo")) {
result.setActiveTo((Date) object);
} else if (string.equalsIgnoreCase("descFr")) {
result.setDescFr((String) object);
} else if (string.equalsIgnoreCase("descNl")) {
result.setDescNl((String) object);
} else if (string.equalsIgnoreCase("descDe")) {
result.setDescDe((String) object);
} else if (string.equalsIgnoreCase("type")) {
result.setType((String) object);
} else if (string.equalsIgnoreCase("idFact")) {
result.setIdFact((Long) object);
} else if (string.equalsIgnoreCase("idParent")) {
result.setIdParent((Long) object);
} else if (string.equalsIgnoreCase("isCode")) {
result.setIsCode((Boolean) object);
} else {
throw new RuntimeException("unknown field");
}
}
#Override
public List transformList(List list) {
return list;
}
}
in hibernate 3 you could set Aliasses to queries but you can't do that anymore in hibernate 5 (correct me if I'm wrong) hence the aliasToBean is something you only can use when actually using aliasses; which I didn't, hence the exception.
Im my case :
=> write sql query and try to map result to Class List
=> Use "Transformers.aliasToBean"
=> get Error "cannot be cast to java.util.Map"
Solution :
=> just put \" before and after query aliases
ex:
"select first_name as \"firstName\" from test"
The problem is that Hibernate converts aliases for column names to upper case or lower case
I solved it by defining my own custom transformer as given below -
import org.hibernate.transform.BasicTransformerAdapter;
public class FluentHibernateResultTransformer extends BasicTransformerAdapter {
private static final long serialVersionUID = 6825154815776629666L;
private final Class<?> resultClass;
private NestedSetter[] setters;
public FluentHibernateResultTransformer(Class<?> resultClass) {
this.resultClass = resultClass;
}
#Override
public Object transformTuple(Object[] tuple, String[] aliases) {
createCachedSetters(resultClass, aliases);
Object result = ClassUtils.newInstance(resultClass);
for (int i = 0; i < aliases.length; i++) {
setters[i].set(result, tuple[i]);
}
return result;
}
private void createCachedSetters(Class<?> resultClass, String[] aliases) {
if (setters == null) {
setters = createSetters(resultClass, aliases);
}
}
private static NestedSetter[] createSetters(Class<?> resultClass, String[] aliases) {
NestedSetter[] result = new NestedSetter[aliases.length];
for (int i = 0; i < aliases.length; i++) {
result[i] = NestedSetter.create(resultClass, aliases[i]);
}
return result;
}
}
And used this way inside the repository method -
#Override
public List<WalletVO> getWalletRelatedData(WalletRequest walletRequest,
Set<String> requiredVariablesSet) throws GenericBusinessException {
String query = getWalletQuery(requiredVariablesSet);
try {
if (query != null && !query.isEmpty()) {
SQLQuery sqlQuery = mEntityManager.unwrap(Session.class).createSQLQuery(query);
return sqlQuery.setResultTransformer(new FluentHibernateResultTransformer(WalletVO.class))
.list();
}
} catch (Exception ex) {
exceptionThrower.throwDatabaseException(null, false);
}
return Collections.emptyList();
}
It worked perfectly !!!
Try putting Column names and field names both in capital letters.
This exception occurs when the class that you specified in the AliasToBeanResultTransformer does not have getter for the corresponding columns. Although the exception details from the hibernate are misleading.

Testing controllers using Spring, JUNIT, MockMvc and Hamcrest

I am trying to test a controller of mine which returns me a List of Objects on the get method to populate a dropdown on my page.
I am trying to write a JUnit test using MockMvc and Hamcrest to test the same.
I want to compare the List of objects and test if it fails or not.
I have created a static List of objects in my Test.java and I am getting a List of objects from the model.attribute method.
To Test: if both the List of Objects are equal and don't contain any other objects.
My object is called Option which has 3 properties. Key, Value and Selected. I have to check if the all the keys exists in the List or not.
I am unable to create a matcher to do the same. I am trying to create a matcher to compare my List.
So far I have done the following:
#Before
public void setup() throws Exception {
// This would build a MockMvc with only the following controller
this.mockMvc = MockMvcBuilders.standaloneSetup(openAccountController)
.build();
}
#Test
public void testOpenAccount() {
try {
setAllLegislations();
this.mockMvc
.perform(get("/open_account.htm"))
// This method is used to print out the actual httprequest
// and httpresponse on the console.
.andDo(print())
// Checking if status is 200
.andExpect(status().isOk())
.andExpect(
model().attributeExists("appFormAccountPlans",
"appFormLiraLegislations",
"appFormLrspLegislations",
"appFormRlspLegislations"))
.andExpect(
model().attribute("appFormAccountPlans", hasSize(5)))
.andExpect(
model().attribute("appFormLiraLegislations",
hasSize(8)))
.andExpect(
model().attribute("appFormLrspLegislations",
hasSize(2)))
.andExpect(
model().attribute("appFormRlspLegislations",
hasSize(1)))
.andExpect(
model().attribute(
"appFormLiraLegislations",
hasKeyFeatureMatcher(getLiraLegislations(allLegislations))));
private Matcher<List<Option>> hasKeyFeatureMatcher(
final List<Option> expectedOptions) {
return new FeatureMatcher<List<Option>, List<Option>>(
equalTo(expectedOptions), "Options are", "was") {
#Override
protected List<Option> featureValueOf(List<Option> actualOptions) {
boolean flag = false;
if (actualOptions.size() == expectedOptions.size()) {
for (Option expectedOption : expectedOptions) {
for (Option actualOption : actualOptions) {
if (expectedOption.getKey().equals(
actualOption.getKey())) {
flag = true;
} else {
flag = false;
break;
}
}
}
}
if (flag)
return actualOptions;
else
return null;
}
};
}
private List<Option> getLiraLegislations(List<Option> legislation) {
List<Option> liraLegislations = new ArrayList<Option>();
Iterator<Option> iterator = legislation.iterator();
while (iterator.hasNext()) {
Option option = iterator.next();
if (LIRA_LEGISLATIONS.contains(option.getKey())) {
liraLegislations.add(option);
}
}
return liraLegislations;
}
private List<Option> allLegislations;
public List<Option> getAllLegislations() {
return allLegislations;
}
public void setAllLegislations() {
allLegislations = new ArrayList<Option>();
for (String key : ALL_LEGISLATIONS) {
Option option = new Option();
option.setKey(key);
allLegislations.add(option);
}
}
private static final Set<String> ALL_LEGISLATIONS = new HashSet<String>(
Arrays.asList(AccountLegislationEnum.AB.toString(),
AccountLegislationEnum.MB.toString(),
AccountLegislationEnum.NB.toString(),
AccountLegislationEnum.NL.toString(),
AccountLegislationEnum.NS.toString(),
AccountLegislationEnum.ON.toString(),
AccountLegislationEnum.QC.toString(),
AccountLegislationEnum.SK.toString(),
AccountLegislationEnum.BC.toString(),
AccountLegislationEnum.FE.toString(),
AccountLegislationEnum.NT.toString(),
AccountLegislationEnum.PE.toString(),
AccountLegislationEnum.YT.toString(),
AccountLegislationEnum.NU.toString(),
AccountLegislationEnum.UNKNOWN.toString()));
This is how I am getting my model attribute as:
Attribute = appFormLiraLegislations
value = [com.abc.arch.core.gui.eform.gui.Option#199d1739, com.abc.arch.core.gui.eform.gui.Option#185fac52, com.abc.arch.core.gui.eform.gui.Option#312a47fe, com.abc.arch.core.gui.eform.gui.Option#4edc8de9, com.abc.arch.core.gui.eform.gui.Option#71e8e471, com.abc.arch.core.gui.eform.gui.Option#70edf123, com.abc.arch.core.gui.eform.gui.Option#15726ac1, com.abc.arch.core.gui.eform.gui.Option#abeafe7]
Thanks in advance.
You can make your life definitely easier when you correctly implement Option object hashCode() and equals() methods using key attribute; then you can simply write:
model().attribute("appFormLiraLegislations",getLiraLegislations(allLegislations)))
and rely on list1.equals(list2) method to do the work for you.
Option hashCode and equals implementation:
public class Option {
private String key;
private String label;
...
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((key == null) ? 0 : key.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Option other = (Option) obj;
if (key == null) {
if (other.key != null)
return false;
} else if (!key.equals(other.key))
return false;
return true;
}
}
Methods above are generated by my IDE. I also don't know exactly what is structure of your Option class, so I add label property for example in addition to key property.
I created a custom Hamcrest matcher to compare the List of Option by checking the size and the keys.
private Matcher<List<Option>> hasOptionsFeatureMatcher(
final List<Option> expectedOptions) {
return new FeatureMatcher<List<Option>, List<Option>>(
equalTo(expectedOptions), "Options are", "Options were") {
#Override
protected List<Option> featureValueOf(List<Option> actualOptions) {
boolean flag = false;
if (expectedOptions.size() == actualOptions.size()) {
for (Option expected : expectedOptions) {
for (Option actual : actualOptions) {
if (expected.getKey().equals(actual.getKey())) {
flag = true;
break;
} else {
flag = false;
}
}
}
} else
flag = false;
if (flag)
return expectedOptions;
else
return null;
}
};
Implementation would be as follows:
private static final ImmutableBiMap<String, String> LIRA = new ImmutableBiMap.Builder<String, String>()
.put(AccountLegislationEnum.AB.toString(), "ALBERTA")
.put(AccountLegislationEnum.MB.toString(), "MANITTOBA")
.put(AccountLegislationEnum.NB.toString(), "NEW BRUNSWICK")
.put(AccountLegislationEnum.NL.toString(), "NEWFOUNDLAND")
.put(AccountLegislationEnum.NS.toString(), "NOVA SCOTIA")
.put(AccountLegislationEnum.ON.toString(), "ONTARIO")
.put(AccountLegislationEnum.QC.toString(), "QUEBEC")
.put(AccountLegislationEnum.SK.toString(), "SASKATCHEWAN")
.put(AccountLegislationEnum.UNKNOWN.toString(), "UNKNOWN").build();
private List<Option> prepareOptions(ImmutableBiMap<String, String> map) {
List<Option> legislations = new ArrayList<Option>();
for (Map.Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
Option option = new Option();
option.setKey(key);
option.setValue(value);
legislations.add(option);
}
return legislations;
}
.andExpect(model().attribute("appFormLiraLegislations",hasOptionsFeatureMatcher(prepareOptions(LIRA))))

Is it possible to evaluate a boolean expression for String comparions?

I will have a String like
('abc' != 'xyz' AND 'thy' = 'thy') OR ('ujy' = 'ujy')
The String will be able to have as many "AND" groups as it wants. There will not be any nested groups within the AND groups. All groups will ALWAYS be serparated by an OR.
I can just switch out the AND for && and OR for ||.
What I would like is to pass this String into some type of eval method and output TRUE or FALSE.
Is there anything out there that can do this?
You can use the built-in Javascript engine coming with the JDK1.6 to evaluate string containing math expressions.
You an give a lookup here: ScriptEngine
Here an example:
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class Myclass {
public static void main(String[] args) {
try {
ScriptEngineManager sem = new ScriptEngineManager();
ScriptEngine se = sem.getEngineByName("JavaScript");
String myExpression = "('abc' == 'xyz' && 'thy' == 'thy') || ('ujy' == 'ujy')";
System.out.println(se.eval(myExpression));
} catch (ScriptException e) {
System.out.println("Invalid Expression");
e.printStackTrace();
}
}
}
Just remember to replace the following:
'AND' with '&&',
'OR' with '||',
'=' must be '=='
Otherwise it will not accept your expression and will throws a javax.script.ScriptException
you can use script package to achieve this like
ScriptEngine engine = new ScriptEngineManager().getEngineByExtension("js");
String[] vars = {"var1 = 'xyz'", "var2 = 'xyz'"};
try {
for (String var : vars) {
engine.eval(var);
}
System.out.println(engine.eval("var1 == var2 "));
} catch (ScriptException e) {
e.printStackTrace();
}
I think you have to parse it and write custom classes for it like this
public interface StringEquals{
public boolean equals(String s1, String s2);
}
public class Equals implements StringEquals{
private String mS1;
private STring mS2;
public NotEquals(String s1, String s2){
mS1 = s1;
mS2 = s2;
}
public boolean equals(){
return mSq1.equals(mS2);
}
}
public class NotEquals implements StringEquals{
private String mS1;
private STring mS2;
public NotEquals(String s1, String s2){
mS1 = s1;
mS2 = s2;
}
public boolean equals(){
return !mS1.equals(mS2);
}
}
public class AndGroup{
private List<StringEquals> mStrings;
public AndGroup(List<StringEquals> list){
mStrings = list;
}
public boolean getResult(){
for(StringEquals e: mStrings){
if (!e.equals()){
return false;
}
}
return true;
}
and a class to parse it:
public boolean eval(String evalString){
List<AndGroup> groups = new LinkedList<AndGroup>();
String[] ands = evalString.split("OR");
for (String andExp : ands){
List<StringEquals> list = new LinkedList<StringEquals>();
String compares = andExp.split("AND");
for (String comp: compares){
if (comp.contains("!="){
String[] notEqual = comp.split("!=");
list.add(new NotEquals(notEqual[0], notEqual[1]));
} else {
String[] equal = comp.split("=");
list.add(new Equals(equal[0], equal[1]);
}
}
groups.add(new AndGroup(list));
}
for (AndGroup g: groups){
if (g.getResult()){
return true;
}
}
return false;
}
not tested, but it may point you into the right direction
I think you can try groovy (if it's an option for you) with it's groovy.util.Eval, but first you should process your string and replace AND\OR with && and ||.

Categories