The following method gets a "Route" (class name and class method):
public Route getRoute(final String method, final String request) {
if (hasRoutes) {
for (Map.Entry<Pattern, HashMap<String, String>> entry : routes) {
Matcher match = entry.getKey().matcher(request);
if (match.find()) {
HashMap<String, String> methods = entry.getValue();
// ISSUE: Returns FALSE after 1st call of Router.getRoute()
if (methods.containsKey(method)) {
return new Route(match.group("interface"), "TRUE (" + method + " - " + match.group("interface") + "): " + methods.get(method));
} else {
return new Route(match.group("interface"), "FALSE (" + method + " - " + match.group("interface") + "): " + methods.values().toString() + ", SIZE: " + entry.getValue().size());
}
//return entry.getValue().containsKey(method) ? new Route(match.group("interface"), entry.getValue().get(method)) : null;
}
}
}
return null;
}
"routes" is defined as:
private Set<Entry<Pattern, HashMap<String, String>>> routes;
It is a cached representation of a JSON configuration file that defines supported routes, e.g.:
{
"^/?(?<interface>threads)/?$": {
"GET": "list",
"POST": "create"
},
"^/?(?<interface>threads)/(?<id>\\d+)/?$": {
"GET": "get",
"POST": "reply",
"PUT": "edit",
"PATCH": "edit",
"DELETE": "delete"
}
}
EDIT, here's how "routes" is filled from the contents of the JSON file:
try {
JsonParser parser = JSONFactory.createJsonParser(in);
JsonNode root = JSONMapper.readTree(parser);
Iterator base = root.getFieldNames();
Iterator node;
String match, method;
HashMap<Pattern, HashMap<String, String>> routesMap = new HashMap();
while (base.hasNext()) {
match = base.next().toString();
if (match != null) {
node = root.get(match).getFieldNames();
HashMap<String, String> methods = new HashMap();
while (node.hasNext()) {
method = node.next().toString();
if (method != null) {
methods.put(method, root.get(match).get(method).getTextValue());
}
}
if (!methods.isEmpty()) {
routesMap.put(Pattern.compile(match), methods);
}
}
}
if (!routesMap.isEmpty()) {
hasRoutes = true;
routes = routesMap.entrySet();
}
// Help garbage collection
parser = null;
root = null;
base = null;
node = null;
match = null;
method = null;
routesMap = null;
} catch (Exception ex) {
}
EDIT 2, properties in question & init() method:
public final static JsonFactory JSONFactory = new JsonFactory();
public final static ObjectMapper JSONMapper = new ObjectMapper();
public static Router router;
private final Class self = getClass();
private final ClassLoader loader = self.getClassLoader();
public void init(ServletConfig config) throws ServletException {
super.init(config);
router = new Router(self.getResourceAsStream("/v1_0/Routes.json"), JSONFactory, JSONMapper);
}
For some reason when accessing the servlet after the first time the HashMap is empty of values. A x.size() returns zero.
This is a rewrite of a PHP application from the ground up so I apologise in advance if the issue is something mundane.
Full source:
- Router source
- Route source
Your getOptions() method removes every entry from this map as it iterates. So, after calling getOptions() once, the map is empty.
By the way, assigning null to variables does not "help the garbage collector." The garbage collector knows that when the scope of a variable is exited, that variable no longer references the object. You are actually slowing things down by assigning values (null) that can never be read (as well as cluttering your code with counterproductive noise). A good static analysis tool like FindBugs will warn you that this is bad code.
Iterating through HashSet empties HashMap entry
That doesn't happen. Simply iterating a HashSet has no side-effects on the set's contents.
There is something else going on here that is causing your problem.
Related
[ISSUE] repo always returns null when I call repo methods, while stepping through, throws null pointer exception. then front end receives
500: Http failure response for http://localhost:4200/api/aiprollout/updatecsv: 500 Internal Server Error
[HAVE TRIED] Adjusting AutoWired and components and service annotations.
[QUESTIONS]
1- Does every repo method need its own service and controller method?
2- Is it okay to create a new service that uses an existing controller?
3- If this new service uses SuperCsv and I create custom CsvCellProcessors, can these cell processors also call the repo? Should these cell processors perform logic? or should it be done else where? What class annotations should these cellProcessors classes have? #Component?
Any advice is greatly appreciated, feel a little lost at this point not even sure what to do.
[CODE]
Controller:
#RestController
#EnableConfigurationProperties({SpoofingConfigurationProperties.class})
#RequestMapping(value = "")
public class AipRolloutController {
private final Logger logger = some logger
private final AipRolloutService AipRolloutService;
private final CsvParserService csvParserService;
#Autowired
public AipRolloutController(AipRolloutService aipRolloutService, CsvParserService csvParserService) {
this.AipRolloutService = aipRolloutService;
this.csvParserService = csvParserService;
}
#PostMapping(value = "/updatecsv", produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public ResponseEntity<?> processCsv(#RequestParam("csvFile") MultipartFile csvFile) throws IOException {
if (csvFile.isEmpty()) return new ResponseEntity(
responceJson("please select a file!"),
HttpStatus.NO_CONTENT
);
csvParserService.parseCsvFile(csvFile);
return new ResponseEntity(
responceJson("Successfully uploaded - " + csvFile.getOriginalFilename()),
new HttpHeaders(),
HttpStatus.CREATED
);
}
Service:
#Service
public class AipRolloutService {
private static final Logger logger = some logger
#Autowired
private AIPRolloutRepository AIPRolloutRepository;
New Csv parser Service
#Service
public class CsvParserService {
#Autowired private AipRolloutService aipRolloutService;
public CsvParserService(AipRolloutService aipRolloutService) {
this.aipRolloutService = aipRolloutService;
}
public void parseCsvFile(MultipartFile csvFile) throws IOException {
CsvMapReader csvMapReader = new CsvMapReader(new InputStreamReader(csvFile.getInputStream()), CsvPreference.STANDARD_PREFERENCE);
parseCsv(csvMapReader);
csvMapReader.close();
}
private void parseCsv(CsvMapReader csvMapReader) throws IOException {
String[] header = csvMapReader.getHeader(true);
List<String> headers = Arrays.asList(header);
verifySourceColumn(headers);
verifyPovColumn(headers);
final CellProcessor[] processors = getProcessors(headers);
Map<String, Object> csvImportMap = null;
while ((csvImportMap = csvMapReader.read(header, processors)) != null) {
CsvImportDTO csvImportDto = new CsvImportDTO(csvImportMap);
if ( activationTypeP(csvImportDto) ){
int mssValue = Integer.parseInt(csvImportDto.getMssValue());
aipRolloutService.updateAipRollout(csvImportDto.getSource(),
csvImportDto.getPov(),
csvImportDto.getActivationType(),
mssValue);
}
}
}
private CellProcessor[] getProcessors(List<String> headers) {
CellProcessor[] processors = new CellProcessor[headers.size()];
int index = 0;
for (String header : headers) {
if (header.contains(SOURCE_ID)) {
processors[index++] = new CsvSourceIdCellParser();
} else if (header.contains(POV)) {
processors[index++] = new CsvPovCellParser();
} else if (header.contains(ACTIVATION_TYPE)) {
processors[index++] = new CsvActivationTypeCellParser();
} else if (header.contains(ACTIVATION_DATE)) {
processors[index++] = new Optional();
} else if (header.contains(DEACTIVATION_DATE)) {
processors[index++] = new Optional();
} else if (header.contains(MSS_VALUE)) {
processors[index++] = new CsvMssValueCellParser();
} else {
processors[index++] = null; // throw exception? wrong header info instead of allowing null?
}
}
return processors;
}
Custom Cell Processor that calls repo and returns null
public class CsvSourceIdCellParser extends CellProcessorAdaptor {
#Autowired AIPRolloutRepository aipRolloutRepository;
public CsvSourceIdCellParser(){ super(); }
// this constructor allows other processors to be chained
public CsvSourceIdCellParser(CellProcessor next){ super(next); }
#Override
public Object execute(Object value, CsvContext csvContext) {
// throws an Exception if the input is null
validateInputNotNull(value, csvContext);
// get rid of description only need first 3 #'s
value = value.toString().substring(0,3);
// check if WH exists
if( aipRolloutRepository.dcExistsInDatabase(value.toString()) )
return value;
else
throw new RuntimeException("Check Warehouse Value, Value Not Found "
+ "Row number: " + csvContext.getRowNumber()
+ " Column number: " + csvContext.getColumnNumber());
}
}
Repository
#Repository
public class AIPRolloutRepository {
private static final Logger logger = LoggerFactory.getLogger(AIPRolloutRepository.class);
#Autowired
JdbcTemplate jdbcTemplate;
public AIPRolloutRepository() {
}
public boolean dcExistsInDatabase(String dc){
// Query for a count saves time and memory, query for distinct saves time and memory on execution
boolean hasRecord =
jdbcTemplate
.query( "select count (distinct '" + dc +"')" +
"from xxcus.XX_AIP_ROLLOUT" +
"where DC = '" + dc + "';",
new Object[] { dc },
(ResultSet rs) -> {
if (rs.next()) {
return true;
}
return false;
}
);
return hasRecord;
}
So I have a Map<String, ArrayList> parentToChild and want to create basically a "Family Tree" or nested hierarchy. Below is an example of the map but there could be more children at each level e.g. (Claire could have Matt and Bruce as children):
David -> [Claire]
Claire -> [Matt]
Matt -> [Sean, Terry]
I know the root of the tree should be David for the above example and it will only have one root.
Example output
{
"David": {
"Claire": {
"Matt": {
"Sean": {},
"Terry": {}
}
}
}
}
I've tried few things but genuinely stumped.
EDIT: Code tried so far
public Set<Tree> transform(Map<String, ArrayList<String>> input) {
Set<String> roots = new HashSet<String>(input.keySet());
Map<String, Tree> map = new HashMap<String, Tree>();
for (Map.Entry<String, ArrayList<String>> entry : input.entrySet()) {
String key = entry.getKey();
List<String> childKeys = entry.getValue();
Tree tree = map.get(key);
if (tree == null) {
tree = new Tree(key);
map.put(key, tree);
}
for (String childKey : childKeys) {
roots.remove(childKey);
Tree child = map.get(childKey);
if (child == null) {
child = new Tree(childKey);
map.put(childKey, child);
}
tree.addChild(child);
}
}
Set<Tree> res = new HashSet<Tree>(roots.size());
for (String key : roots) {
res.add(map.get(key));
}
return res;
}
Tree class:
public class Tree {
private String key;
private Tree child;
public Tree(String key){
this.key = key;
}
public void addChild(Tree child){
this.child = child;
}
}
The issue is when I use this code the output (What is in the set after debugging/printing) I get is
David:
Claire:
Matt:
Terry:
You could use a Map<String,Object>:
private static final Gson GSON = new GsonBuilder()
.setPrettyPrinting()
.create();
public static void main(String[] args) {
Map<String, List<String>> input = new HashMap<>();
input.put("David", Arrays.asList("Claire"));
input.put("Claire", Arrays.asList("Matt"));
input.put("Matt", Arrays.asList("Sean", "Terry"));
Map<String,Object> result = new HashMap<>();
convert(input, "David", result);
GSON.toJson(result, System.out);
}
private static void convert(Map<String, List<String>> input, String root,
Map<String,Object> result) {
if (!result.containsKey(root)) {
Map<String,Object> rootObj = new HashMap<>();
result.put(root, rootObj);
List<String> children = input.get(root);
if (children != null) {
for (String child: children) {
convert(input, child, rootObj);
}
}
}
}
Output:
{
"David": {
"Claire": {
"Matt": {
"Terry": {},
"Sean": {}
}
}
}
}
In the Java world you have access to Saxon 9.8 or later HE where XPath 3.1 or XQuery 3.1 or XSLT 3.0 all have support for representing your initial map as an XdmMap and processing them, for instance with XQuery:
declare namespace map = "http://www.w3.org/2005/xpath-functions/map";
declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";
declare option output:method 'json';
declare option output:indent 'yes';
declare variable $map as map(xs:string, array(xs:string)) external := map {
'David' : [ 'Claire' ],
'Claire' : [ 'Matt' ],
'Matt' : [ 'Sean', 'Terry' ]
};
declare variable $root as xs:string external := 'David';
declare function local:create-tree($map as map(xs:string, array(xs:string)), $children as xs:string*) as map(*) {
map:merge($children ! map { . : local:create-tree($map, $map(.)) })
};
local:create-tree($map, $root)
https://xqueryfiddle.liberty-development.net/3Nzd8bV
A simple Java example to run this with Saxon 10 HE (its API documentation is at http://saxonica.com/html/documentation/using-xquery/api-query/s9api-query.html), passing a Java Map to the XQuery (inserted inline as a string but could of course be loaded from a file instead) is:
import java.util.HashMap;
import java.util.Map;
import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.QName;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.XQueryCompiler;
import net.sf.saxon.s9api.XQueryEvaluator;
import net.sf.saxon.s9api.XQueryExecutable;
import net.sf.saxon.s9api.XdmMap;
public class SaxonJavaMapToNestedJSONObject {
public static void main(String[] args) throws SaxonApiException {
Map<String, String[]> map = new HashMap<>();
map.put("David", new String[] { "Claire" });
map.put("Claire", new String[] { "Matt" });
map.put("Matt", new String[] { "Sean", "Terry" });
Processor processor = new Processor(true);
XQueryCompiler compiler = processor.newXQueryCompiler();
XQueryExecutable executable = compiler.compile("declare namespace map = \"http://www.w3.org/2005/xpath-functions/map\";\n" +
"\n" +
"declare namespace output = \"http://www.w3.org/2010/xslt-xquery-serialization\";\n" +
"\n" +
"declare option output:method 'json';\n" +
"declare option output:indent 'yes';\n" +
"\n" +
"declare variable $map as map(xs:string, array(xs:string)) external;\n" +
"\n" +
"declare variable $root as xs:string external := 'David';\n" +
"\n" +
"declare function local:create-tree($map as map(xs:string, array(xs:string)), $children as xs:string*) as map(*) {\n" +
" map:merge($children ! map { . : local:create-tree($map, $map(.)) })\n" +
"};\n" +
"\n" +
"local:create-tree($map, $root)");
XQueryEvaluator evaluator = executable.load();
evaluator.setExternalVariable(new QName("map"), XdmMap.makeMap(map));
evaluator.run(processor.newSerializer(System.out));
}
}
Of course you could set the root variable as well from Java: evaluator.setExternalVariable(new QName("root"), new XdmAtomicValue("David"));
I Have a multithreaded environment in android app. I use a singleton class to store data. This singleton class contains a arraylist that is accessed using a synchronized method.
The app uses this arraylist to render images in app.
Initial problem : Concurrent modification error use to come so I made the get arraylist function syncronized.
Current Problem:Concurrent modification error not coming but in between empty arraylist returned (maybe when there is concurrent access).
Objective : I want to detect when Concurrent modification so that Instead of empty arraylist being return I can return last state of the arraylist.
public synchronized List<FrameData> getCurrentDataToShow() {
List<FrameData> lisCurrDataToShow = new ArrayList<FrameData>();
//for (FrameData fd : listFrameData) {//concurrent modification exception
//todo iterator test
Iterator<FrameData> iterator = listFrameData.iterator();
while (iterator.hasNext()) {
FrameData fd = iterator.next();
long currentTimeInMillis = java.lang.System.currentTimeMillis();
if ((currentTimeInMillis > fd.getStartDate().getTime() && currentTimeInMillis < fd.getEndDate().getTime()) || (fd.isAllDay() && DateUtils.isToday(fd.getStartDate().getTime()))) {
if (new File(ImageFrameActivity.ROOT_FOLDER_FILES + fd.getFileName()).exists()) {
lisCurrDataToShow.add(fd);
}
}
}
if (lisCurrDataToShow.size() == 0) {
lisCurrDataToShow.add(new FrameData(defaultFileName, null, null, null, String.valueOf(120), false));
}
return lisCurrDataToShow;
}
Referred to Detecting concurrent modifications?
Please help!
EDIT1:
This problem occurs rarely not everytime.
If a threads is accessing getCurrentDataToShow() and another thread tries to access this function what will the function return?? I'm new to multithreading , please guide
Edit 2
in oncreate following methods of singleton are called periodically
DataModelManager.getInstance().getCurrentDataToShow();
DataModelManager.getInstance().parseData(responseString);
Complete singleton class
public class DataModelManager {
private static DataModelManager dataModelManager;
private ImageFrameActivity imageFrameAct;
private String defaultFileName;
public List<FrameData> listFrameData = new ArrayList<FrameData>();
// public CopyOnWriteArrayList<FrameData> listFrameData= new CopyOnWriteArrayList<FrameData>();
private String screensaverName;
private boolean isToDownloadDeafultFiles;
private String tickerMsg = null;
private boolean showTicker = false;
private boolean showHotspot = false;
private String hotspotFileName=null;
public String getDefaultFileName() {
return defaultFileName;
}
public boolean isToDownloadDeafultFiles() {
return isToDownloadDeafultFiles;
}
public void setToDownloadDeafultFiles(boolean isToDownloadDeafultFiles) {
this.isToDownloadDeafultFiles = isToDownloadDeafultFiles;
}
private String fileNames;
private DataModelManager() {
}
public static DataModelManager getInstance() {
if (dataModelManager == null) {
synchronized (DataModelManager.class) {
if (dataModelManager == null) {
dataModelManager = new DataModelManager();
}
}
}
return dataModelManager;
}
private synchronized void addImageData(FrameData frameData) {
//Log.d("Frame Data","Start date "+frameData.getStartDate()+ " " +"end date "+frameData.getEndDate());
listFrameData.add(frameData);
}
public synchronized void parseData(String jsonStr) throws JSONException {
listFrameData.clear();
if (jsonStr == null) {
return;
}
List<String> listFileNames = new ArrayList<String>();
JSONArray jsonArr = new JSONArray(jsonStr);
int length = jsonArr.length();
for (int i = 0; i < length; i++) {
JSONObject jsonObj = jsonArr.getJSONObject(i);
dataModelManager.addImageData(new FrameData(jsonObj.optString("filename", ""), jsonObj.optString("start", ""), jsonObj.optString("end", ""), jsonObj.optString("filetype", ""), jsonObj.optString("playTime", ""), jsonObj.optBoolean("allDay", false)));
listFileNames.add(jsonObj.optString("filename", ""));
}
fileNames = listFileNames.toString();
}
public void setDefaultFileData(String jsonStr) throws JSONException {
JSONObject jsonObj = new JSONObject(jsonStr);
defaultFileName = jsonObj.optString("default_image", "");
screensaverName = jsonObj.optString("default_screensaver ", "");
}
#Override
public String toString() {
return fileNames.replace("[", "").replace("]", "") + "," + defaultFileName + "," + screensaverName;
}
public FrameData getFrameData(int index) {
return listFrameData.get(index);
}
public synchronized List<FrameData> getCurrentDataToShow() {
List<FrameData> lisCurrDataToShow = new ArrayList<FrameData>();
// for (FrameData fd : listFrameData) {//concurrent modification exception
//todo iterator test
Iterator<FrameData> iterator = listFrameData.iterator();
while (iterator.hasNext()) {
FrameData fd = iterator.next();
long currentTimeInMillis = java.lang.System.currentTimeMillis();
if ((currentTimeInMillis > fd.getStartDate().getTime() && currentTimeInMillis < fd.getEndDate().getTime()) || (fd.isAllDay() && DateUtils.isToday(fd.getStartDate().getTime()))) {
if (new File(ImageFrameActivity.ROOT_FOLDER_FILES + fd.getFileName()).exists()) {
lisCurrDataToShow.add(fd);
}
}
}
if (lisCurrDataToShow.size() == 0) {
lisCurrDataToShow.add(new FrameData(defaultFileName, null, null, null, String.valueOf(120), false));
}
return lisCurrDataToShow;
}
public String getCurrentFileNames() {
String currFileNames = "";
List<FrameData> currFrameData = getCurrentDataToShow();
for (FrameData data : currFrameData) {
currFileNames += "," + data.getFileName();
}
return currFileNames;
}
public ImageFrameActivity getImageFrameAct() {
return imageFrameAct;
}
public void setImageFrameAct(ImageFrameActivity imageFrameAct) {
this.imageFrameAct = imageFrameAct;
}
}
This is the only part of your question that is currently answerable:
If a threads is accessing getCurrentDataToShow() and another thread tries to access this function what will the function return?
It depends on whether you are calling getCurrentDataToShow() on the same target object; i.e. what this is.
If this is the same for both calls, then the first call will complete before the second call starts.
If this is different, you will be locking on different objects, and the two calls could overlap. Two threads need to lock the same object to achieve mutual exclusion.
In either case, this method is not changing the listFrameData collection. Hence it doesn't matter whether the calls overlap! However, apparently something else is changing the contents of the collection. If that code is not synchronizing at all, or if it is synchronizing on a different lock, then that could be a source of problems.
Now you say that you are not seeing ConcurrentModificationException's at the moment. That suggests (but does not prove) that there isn't a synchronization problem at all. And that suggests (but does not prove) that your current problem is a logic error.
But (as I commented above) there are reasons to doubt that the code you have shown us is an true reflection of your real code. You need to supply an MVCE if you want a more definite diagnosis.
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");
I am using java to call a url that returns a JSON object:
url = new URL("my URl");
urlInputStream = url.openConnection().getInputStream();
How can I convert the response into string form and parse it?
I would suggest you have to use a Reader to convert your InputStream in.
BufferedReader streamReader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
StringBuilder responseStrBuilder = new StringBuilder();
String inputStr;
while ((inputStr = streamReader.readLine()) != null)
responseStrBuilder.append(inputStr);
new JSONObject(responseStrBuilder.toString());
I tried in.toString() but it returns:
getClass().getName() + '#' + Integer.toHexString(hashCode())
(like documentation says it derives to toString from Object)
All the current answers assume that it is okay to pull the entire JSON into memory where the advantage of an InputStream is that you can read the input little by little. If you would like to avoid reading the entire Json file at once then I would suggest using the Jackson library (which is my personal favorite but I'm sure others like Gson have similar functions).
With Jackson you can use a JsonParser to read one section at a time. Below is an example of code I wrote that wraps the reading of an Array of JsonObjects in an Iterator. If you just want to see an example of Jackson, look at the initJsonParser, initFirstElement, and initNextObject methods.
public class JsonObjectIterator implements Iterator<Map<String, Object>>, Closeable {
private static final Logger LOG = LoggerFactory.getLogger(JsonObjectIterator.class);
private final InputStream inputStream;
private JsonParser jsonParser;
private boolean isInitialized;
private Map<String, Object> nextObject;
public JsonObjectIterator(final InputStream inputStream) {
this.inputStream = inputStream;
this.isInitialized = false;
this.nextObject = null;
}
private void init() {
this.initJsonParser();
this.initFirstElement();
this.isInitialized = true;
}
private void initJsonParser() {
final ObjectMapper objectMapper = new ObjectMapper();
final JsonFactory jsonFactory = objectMapper.getFactory();
try {
this.jsonParser = jsonFactory.createParser(inputStream);
} catch (final IOException e) {
LOG.error("There was a problem setting up the JsonParser: " + e.getMessage(), e);
throw new RuntimeException("There was a problem setting up the JsonParser: " + e.getMessage(), e);
}
}
private void initFirstElement() {
try {
// Check that the first element is the start of an array
final JsonToken arrayStartToken = this.jsonParser.nextToken();
if (arrayStartToken != JsonToken.START_ARRAY) {
throw new IllegalStateException("The first element of the Json structure was expected to be a start array token, but it was: " + arrayStartToken);
}
// Initialize the first object
this.initNextObject();
} catch (final Exception e) {
LOG.error("There was a problem initializing the first element of the Json Structure: " + e.getMessage(), e);
throw new RuntimeException("There was a problem initializing the first element of the Json Structure: " + e.getMessage(), e);
}
}
private void initNextObject() {
try {
final JsonToken nextToken = this.jsonParser.nextToken();
// Check for the end of the array which will mean we're done
if (nextToken == JsonToken.END_ARRAY) {
this.nextObject = null;
return;
}
// Make sure the next token is the start of an object
if (nextToken != JsonToken.START_OBJECT) {
throw new IllegalStateException("The next token of Json structure was expected to be a start object token, but it was: " + nextToken);
}
// Get the next product and make sure it's not null
this.nextObject = this.jsonParser.readValueAs(new TypeReference<Map<String, Object>>() { });
if (this.nextObject == null) {
throw new IllegalStateException("The next parsed object of the Json structure was null");
}
} catch (final Exception e) {
LOG.error("There was a problem initializing the next Object: " + e.getMessage(), e);
throw new RuntimeException("There was a problem initializing the next Object: " + e.getMessage(), e);
}
}
#Override
public boolean hasNext() {
if (!this.isInitialized) {
this.init();
}
return this.nextObject != null;
}
#Override
public Map<String, Object> next() {
// This method will return the current object and initialize the next object so hasNext will always have knowledge of the current state
// Makes sure we're initialized first
if (!this.isInitialized) {
this.init();
}
// Store the current next object for return
final Map<String, Object> currentNextObject = this.nextObject;
// Initialize the next object
this.initNextObject();
return currentNextObject;
}
#Override
public void close() throws IOException {
IOUtils.closeQuietly(this.jsonParser);
IOUtils.closeQuietly(this.inputStream);
}
}
If you don't care about memory usage, then it would certainly be easier to read the entire file and parse it as one big Json as mentioned in other answers.
For those that pointed out the fact that you can't use the toString method of InputStream like this see https://stackoverflow.com/a/5445161/1304830 :
My correct answer would be then :
import org.json.JSONObject;
public static String convertStreamToString(java.io.InputStream is) {
java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
...
JSONObject json = new JSONObject(convertStreamToString(url.openStream());
If you like to use Jackson Databind (which Spring uses by default for its HttpMessageConverters), then you may use the ObjectMapper.readTree(InputStream) API. For example,
ObjectMapper mapper = new ObjectMapper();
JsonNode json = mapper.readTree(myInputStream);
use jackson to convert json input stream to the map or object http://jackson.codehaus.org/
there are also some other usefull libraries for json, you can google: json java
Use a library.
GSON
Jackson
or one of many other JSON libraries that are out there.
Kotlin version with Gson
to read the response JSON:
val response = BufferedReader(
InputStreamReader(conn.inputStream, "UTF-8")
).use { it.readText() }
to parse response we can use Gson:
val model = Gson().fromJson(response, YourModelClass::class.java)
This example reads all objects from a stream of objects,
it is assumed that you need CustomObjects instead of a Map:
ObjectMapper mapper = new ObjectMapper();
JsonParser parser = mapper.getFactory().createParser( source );
if(parser.nextToken() != JsonToken.START_ARRAY) {
throw new IllegalStateException("Expected an array");
}
while(parser.nextToken() == JsonToken.START_OBJECT) {
// read everything from this START_OBJECT to the matching END_OBJECT
// and return it as a tree model ObjectNode
ObjectNode node = mapper.readTree(parser);
CustomObject custom = mapper.convertValue( node, CustomObject.class );
// do whatever you need to do with this object
System.out.println( "" + custom );
}
parser.close();
This answer was composed by using : Use Jackson To Stream Parse an Array of Json Objects and Convert JsonNode into Object
I suggest use javax.json.Json factory as less verbose possible solution:
JsonObject json = Json.createReader(yourInputStream).readObject();
Enjoy!
if you have JSON file you can set it on assets folder then call it using this code
InputStream in = mResources.getAssets().open("fragrances.json");
// where mResources object from Resources class
{
InputStream is = HTTPClient.get(url);
InputStreamReader reader = new InputStreamReader(is);
JSONTokener tokenizer = new JSONTokener(reader);
JSONObject jsonObject = new JSONObject(tokenizer);
}