modifying a map while iterating over its entrySet - java

In the java docs of the map interface's entrySet() method I found this statement and I really do no understand it.
The set is backed by the map, so changes to the map are reflected in the set, and vice-versa. If the map is modified while an iteration over the set is in progress, the results of the iteration are undefined
what is meant by undefined here?
For more clarification, this is my situation.
I have a web application based on spring & hibernate.
Our team implemented custom caching class called CachedIntegrationClients.
We are using RabbitMQ as a messaging server.
instead of getting our clients each time we want to send a message to the server, we cache the clients using the previous caching class.
The problem is that the messages are sent to the messaging server twice.
Viewing the logs, we found that the method that get the cached clients return the client twice, although this (theoretically) impossible as we store the clients in a map, and the map does not allow duplicate keys.
After some smoke viewing of the code I found that the method that iterates over the cached clients gets a set of the clients from the cached clients map.
So I suspected that while iterating over this set, another request is made by another client and this client may be uncached, so it modifies the map.
Any way this is the CachedIntegrationClients class
public class CachedIntegrationClientServiceImpl {
private IntegrationDao integrationDao;
private IntegrationService integrationService;
Map<String, IntegrationClient> cachedIntegrationClients = null;
#Override
public void setBaseDAO(BaseDao baseDao) {
super.setBaseDAO(integrationDao);
}
#Override
public void refreshCache() {
cachedIntegrationClients = null;
}
synchronized private void putOneIntegrationClientOnCache(IntegrationClient integrationClient){
fillCachedIntegrationClients(); // only fill cache if it is null , it will never refill cache
if (! cachedIntegrationClients.containsValue(integrationClient)) {
cachedIntegrationClients.put(integrationClient.getClientSlug(),integrationClient);
}
}
/**
* only fill cache if it is null , it will never refill cache
*/
private void fillCachedIntegrationClients() {
if (cachedIntegrationClients != null) {
return ;
}
log.debug("filling cache of cachedClients");
cachedIntegrationClients = new HashMap<String, IntegrationClient>(); // initialize cache Map
List<IntegrationClient> allCachedIntegrationClients= integrationDao.getAllIntegrationClients();
if (allCachedIntegrationClients != null) {
for (IntegrationClient integrationClient : allCachedIntegrationClients) {
integrationService
.injectCssFileForIntegrationClient(integrationClient);
fetchClientServiceRelations(integrationClient
.getIntegrationClientServiceList());
}
for (IntegrationClient integrationClient : allCachedIntegrationClients) {
putOneIntegrationClientOnCache(integrationClient);
}
}
}
/**
* fetch all client service
* #param integrationClientServiceList
*/
private void fetchClientServiceRelations(
List<IntegrationClientService> integrationClientServiceList) {
for (IntegrationClientService integrationClientService : integrationClientServiceList) {
fetchClientServiceRelations(integrationClientService);
}
}
private void fetchClientServiceRelations(IntegrationClientService clientService) {
for (Exchange exchange : clientService.getExchangeList()) {
exchange.getId();
}
for (Company company : clientService.getCompanyList()) {
company.getId();
}
}
/**
* Get a client given its slug.
*
* If the client was not found, an exception will be thrown.
*
* #throws ClientNotFoundIntegrationException
* #return IntegrationClient
*/
#Override
public IntegrationClient getIntegrationClient(String clientSlug) throws ClientNotFoundIntegrationException {
if (cachedIntegrationClients == null) {
fillCachedIntegrationClients();
}
if (!cachedIntegrationClients.containsKey(clientSlug)) {
IntegrationClient integrationClient = integrationDao.getIntegrationClient(clientSlug);
if (integrationClient != null) {
this.fetchClientServiceRelations(integrationClient.getIntegrationClientServiceList());
integrationService.injectCssFileForIntegrationClient(integrationClient);
cachedIntegrationClients.put(clientSlug, integrationClient);
}
}
IntegrationClient client = cachedIntegrationClients.get(clientSlug);
if (client == null) {
throw ClientNotFoundIntegrationException.forClientSlug(clientSlug);
}
return client;
}
public void setIntegrationDao(IntegrationDao integrationDao) {
this.integrationDao = integrationDao;
}
public IntegrationDao getIntegrationDao() {
return integrationDao;
}
public Map<String, IntegrationClient> getCachedIntegrationClients() {
if (cachedIntegrationClients == null) {
fillCachedIntegrationClients();
}
return cachedIntegrationClients;
}
public IntegrationService getIntegrationService() {
return integrationService;
}
public void setIntegrationService(IntegrationService integrationService) {
this.integrationService = integrationService;
}
}
and here is the method that iterates over the set
public List<IntegrationClientService> getIntegrationClientServicesForService(IntegrationServiceModel service) {
List<IntegrationClientService> integrationClientServices = new ArrayList<IntegrationClientService>();
for (Entry<String, IntegrationClient> entry : cachedIntegrationClientService.getCachedIntegrationClients().entrySet()) {
IntegrationClientService integrationClientService = getIntegrationClientService(entry.getValue(), service);
if (integrationClientService != null) {
integrationClientServices.add(integrationClientService);
}
}
return integrationClientServices;
}
Also here is the method that calls the previous one
List<IntegrationClientService> clients = integrationService.getIntegrationClientServicesForService(service);
System.out.println(clients.size());
if (clients.size() > 0) {
log.info("Inbound service message [" + messageType.getKey() + "] to be sent to " + clients.size()
+ " registered clients: [" + StringUtils.arrayToDelimitedString(clients.toArray(), ", ") + "]");
for (IntegrationClientService integrationClientService : clients) {
Message<T> message = integrationMessageBuilder.build(messageType, payload, integrationClientService);
try {
channel.send(message);
} catch (RuntimeException e) {
messagingIntegrationService.handleException(e, messageType, integrationClientService, payload);
}
}
} else {
log.info("Inbound service message [" + messageType.getKey() + "] but no registered clients, not taking any further action.");
}
and here is the logs that appears on the server
BaseIntegrationGateway.createAndSendToSubscribers(65) | Inbound service message [news.create] to be sent to 3 registered clients: [Id=126, Service=IntegrationService.MESSAGE_NEWS, Client=MDC, Id=125, Service=IntegrationService.MESSAGE_NEWS, Client=CNBC, Id=125, Service=IntegrationService.MESSAGE_NEWS, Client=CNBC]

Undefined means there is no requirement for any specific behavior. The implementation is free to start WWIII, re-hang all your toilet rolls by the overhand method, sully your grandmother, etc.
The only permitted modification with a specified behaviour is via the iterator.

Have you looked at java.concurrent.ConcurrentHashMap?
EDIT: I looked over your code again this stikes me as odd:
In fillCachedIntegrationClients() you have the following loop:
for (IntegrationClient integrationClient : allCachedIntegrationClients) {
putOneIntegrationClientOnCache(integrationClient);
}
But the putOneIntegrationClientOnCache method itself directly calls fillCachedIntegrationClients();
synchronized private void putOneIntegrationClientOnCache(IntegrationClient integrationClient){
fillCachedIntegrationClients(); // only fill cache if it is null , it will never refill cache
...
}
Something there must go wrong. You are calling fillCachedIntegrationClients() twice. Actually if I am not mistaken this should actually be a never-ending loop since one method calls the other and vice versa. The != null condition is never met during the initialization. Ofcourse you are modifying and iterating in a undefined way so maybe that saves you from an infinite loop.

Related

how to use persistent anchors with sceneform?

I have saved a persistent anchor (for 365 days) on the cloud. Now, I want to retrieve it. I can do it just fine using the code Google provided in one of its sample projects. However, I want to use Sceneform since I want to do some manipulations afterwards (drawing 3D shapes), that are much easier to do in Sceneform. However, I can't seem to resolve the persistent cloud anchors. All the examples I find online, don't deal with persistent cloud anchors and they only deal with the normal 24 hour cloud anchors.
#RequiresApi(api = VERSION_CODES.N)
protected void onUpdateFrame(FrameTime frameTime) {
Frame frame = arFragment.getArSceneView().getArFrame();
// If there is no frame, just return.
if (frame == null) {
return;
}
if (session == null) {
Log.d(TAG, "setup a session once");
session = arFragment.getArSceneView().getSession();
cloudAnchorManager = new CloudAnchorManager(session);
}
if (resolveListener == null && session != null) {
Log.d(TAG, "setup a resolveListener once");
resolveListener = new MemexViewingActivity.ResolveListener();
// Encourage the user to look at a previously mapped area.
if (cloudAnchorId != null && !gotGoodAnchor && cloudAnchorManager != null) {
Log.d(TAG, "put resolveListener on cloud manager once");
userMessageText.setText(R.string.resolving_processing);
cloudAnchorManager.resolveCloudAnchor(cloudAnchorId, resolveListener);
}
}
if (cloudAnchorManager != null && session != null) {
try {
Frame dummy = session.update();
cloudAnchorManager.onUpdate();
} catch (CameraNotAvailableException e) {
e.printStackTrace();
}
}
}
Is there anything wrong in the above update function that I have written? The CloudAnchorManager class is the same one Google uses in its Persistent Cloud Anchor example. Here, I will put its code too:
package com.memex.eu.helpers;
import android.util.Log;
import com.google.ar.core.Anchor;
import com.google.ar.core.Anchor.CloudAnchorState;
import com.google.ar.core.Session;
import com.google.common.base.Preconditions;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* A helper class to handle all the Cloud Anchors logic, and add a callback-like mechanism on top of
* the existing ARCore API.
*/
public class CloudAnchorManager {
/** Listener for the results of a host operation. */
public interface CloudAnchorListener {
/** This method is invoked when the results of a Cloud Anchor operation are available. */
void onComplete(Anchor anchor);
}
private final Session session;
private final Map<Anchor, CloudAnchorListener> pendingAnchors = new HashMap<>();
public CloudAnchorManager(Session session) {
this.session = Preconditions.checkNotNull(session);
}
/** Hosts an anchor. The {#code listener} will be invoked when the results are available. */
public synchronized void hostCloudAnchor(Anchor anchor, CloudAnchorListener listener) {
Preconditions.checkNotNull(listener, "The listener cannot be null.");
// This is configurable up to 365 days.
Anchor newAnchor = session.hostCloudAnchorWithTtl(anchor, /* ttlDays= */ 365);
pendingAnchors.put(newAnchor, listener);
}
/** Resolves an anchor. The {#code listener} will be invoked when the results are available. */
public synchronized void resolveCloudAnchor(String anchorId, CloudAnchorListener listener) {
Preconditions.checkNotNull(listener, "The listener cannot be null.");
Anchor newAnchor = session.resolveCloudAnchor(anchorId);
pendingAnchors.put(newAnchor, listener);
}
/** Should be called after a {#link Session#update()} call. */
public synchronized void onUpdate() {
Preconditions.checkNotNull(session, "The session cannot be null.");
for (Iterator<Map.Entry<Anchor, CloudAnchorListener>> it = pendingAnchors.entrySet().iterator();
it.hasNext(); ) {
Map.Entry<Anchor, CloudAnchorListener> entry = it.next();
Anchor anchor = entry.getKey();
if (isReturnableState(anchor.getCloudAnchorState())) {
CloudAnchorListener listener = entry.getValue();
listener.onComplete(anchor);
it.remove();
}
}
}
/** Clears any currently registered listeners, so they won't be called again. */
synchronized void clearListeners() {
pendingAnchors.clear();
}
private static boolean isReturnableState(CloudAnchorState cloudState) {
switch (cloudState) {
case NONE:
case TASK_IN_PROGRESS:
return false;
default:
return true;
}
}
}
Also, here is another class I am using (this is also from the Google example project):
/* Listens for a resolved anchor. */
private final class ResolveListener implements CloudAnchorManager.CloudAnchorListener {
#Override
public void onComplete(Anchor resolvedAnchor) {
runOnUiThread(
() -> {
Anchor.CloudAnchorState state = resolvedAnchor.getCloudAnchorState();
if (state.isError()) {
Log.e(TAG, "Error resolving a cloud anchor, state " + state);
userMessageText.setText(getString(R.string.resolving_error, state));
return;
}
Log.e(TAG, "cloud anchor successfully resolved, state " + state);
anchor = resolvedAnchor;
userMessageText.setText(getString(R.string.resolving_success));
gotGoodAnchor = true;
});
}
}
when I run my app, I point the phone's camera at the physical space where I previously put an object but the anchor is never resolved. I think the problem might be in the update function but I can't seem to figure out what.
I guess I wasn't looking at the object properly. Now, it's working. This code is correct.

Method keeps running and sending the same information even for different request grpc

I am trying to implement GRPC and when i do so I get the correct response from the server and if I stop the server and run it again and use the other request that I implemented it works however if I try and make a second request straight after making one in from the first request I get the same response. It's like it is looping.
These are the two methods I am using from the client:
public void setSpaces(int id) {
channel =ManagedChannelBuilder.forAddress("localhost", 3000)
// Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
// needing certificates.
.usePlaintext()
.build();
blockingStub = carParkServiceGrpc.newBlockingStub(channel);
asyncStub = carParkServiceGrpc.newStub(channel);
logger.info("Will try to get CarPark " + id + " ...");
CarParkToUpdateRequest request = CarParkToUpdateRequest.newBuilder().setDeviceId(id).build();
carParkResponse response;
try {
response = blockingStub.setSpaces(request);
}catch(StatusRuntimeException e) {
logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
return;
}finally {
channel.shutdown();
}
logger.info("Carpark: " + response.getCarPark());
spacesArea.append(response.getCarPark().toString());
}
public void setFull(int id) {
channel =ManagedChannelBuilder.forAddress("localhost", 3000)
// Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
// needing certificates.
.usePlaintext()
.build();
blockingStub = carParkServiceGrpc.newBlockingStub(channel);
asyncStub = carParkServiceGrpc.newStub(channel);
logger.info("Will try to get CarPark " + id + " ...");
CarParkToUpdateRequest request = CarParkToUpdateRequest.newBuilder().setDeviceId(id).build();
carParkResponse response;
try {
response = blockingStub.setFull(request);
}catch(StatusRuntimeException e) {
logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
return;
}finally {
channel.shutdown();
}
logger.info("Carpark: " + response.getCarPark());
fullArea.append(response.getCarPark().toString());
}
These two methods are supposed to send a request to the server to change the status of the 'car park' so if I send a request with setFull I get a response saying the carpark is full etc.
These are the methods from the server:
public void setSpaces(CarParkToUpdateRequest request, StreamObserver<carParkResponse> rStreamObserver) {
ArrayList<CarParkOperations.proto.cp.CarPark> carList = Car.getInstance();
for(int i=0; i<carList.size(); i++) {
if(carList.get(i).getCarParkId() == request.getDeviceId()) {
CarParkOperations.proto.cp.CarPark heater_rec = (CarParkOperations.proto.cp.CarPark) carList.get(i);
Car.carparkCar.clear();
Car.carparkCar.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(heater_rec.getCarParkId()).setLocation(heater_rec.getLocation()).setStatus("Spaces").build());
}
}
for(CarParkOperations.proto.cp.CarPark heater : Car.carparkCar) {
carParkResponse response = carParkResponse.newBuilder().setCarPark(heater).build();
rStreamObserver.onNext(response);
rStreamObserver.onCompleted();
return;
}
}
public void setFull(CarParkToUpdateRequest request, StreamObserver<carParkResponse> rStreamObserver) {
ArrayList<CarParkOperations.proto.cp.CarPark> carList = Car.getInstance();
for(int i=0; i<carList.size(); i++) {
if(carList.get(i).getCarParkId() == request.getDeviceId()) {
CarParkOperations.proto.cp.CarPark heater_rec = (CarParkOperations.proto.cp.CarPark) carList.get(i);
Car.carparkCar.clear();
Car.carparkCar.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(heater_rec.getCarParkId()).setLocation(heater_rec.getLocation()).setStatus("Full").build());
}
}
for(CarParkOperations.proto.cp.CarPark heater : Car.carparkCar) {
carParkResponse response = carParkResponse.newBuilder().setCarPark(heater).build();
rStreamObserver.onNext(response);
rStreamObserver.onCompleted();
return;
}
}
I think it's most likely something to do with the server methods but cant seem to figure it out.
This is where I am storing the data:
package CarParkOperations.proto.cp;
import java.util.ArrayList;
import com.google.rpc.Status;
public class Car extends ArrayList<CarPark>{
public static Car carparkCar;
public static Car getInstance() {
if(carparkCar == null) {
carparkCar = new Car();
}
return carparkCar;
}
public Car() {
this.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(1).setStatus("Full").setLocation("Behind Building 1").build());
this.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(2).setStatus("Full").setLocation("Behind Building 1").build());
this.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(3).setStatus("Full").setLocation("Behind Building 4").build());
this.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(4).setStatus("Full").setLocation("Behind Building 3").build());
this.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(5).setStatus("Full").setLocation("Behind Building 2").build());
this.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(6).setStatus("Full").setLocation("Behind Building 1").build());
this.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(7).setStatus("Full").setLocation("Behind Building 1").build());
this.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(10).setStatus("Full").setLocation("Behind Building 6").build());
this.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(11).setStatus("Full").setLocation("Behind Building 1").build());
this.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(12).setStatus("Spaces").setLocation("Behind Building 1").build());
this.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(13).setStatus("Spaces").setLocation("Behind Building 1").build());
this.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(14).setStatus("Spaces").setLocation("Behind Building 1").build());
}
}
Any suggestions would be much appreciated.
You might need synchronize Car.getInstance() method, because without proper synchronization, if it is called by different threads it may surprisingly return different instances!
public static synchronized Car getInstance() {
if(carparkCar == null) {
carparkCar = new Car();
}
return carparkCar;
}
Also your Car class is not thread-safe because it extends ArrayList which is not thread-safe. You should let your Car class extend something like ConcurrentLinkedQueue instead, or let your Car class compose a field of list = Collections.synchronizedList(new ArrayList()) instead of extending ArrayList.

How to know Gigaspace is connected in Java application at startup

I am using spring Application and my gigaspace is connecting at startup. I am not getting any exception, if gigaspace is down.
#Override
public void onContextRefreshed(ContextRefreshedEvent event) {
String gigaSpaceURL = null;
LOGGER.info("({}) initializing gigaspaces client", getName());
try {
initGSProxy();
Iterator<Map.Entry<ConfiguredSpace, Space>> entries = spaces.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry<ConfiguredSpace, Space> entry = entries.next();
LOGGER.info("({}) initialing space- key=" +
entry.getKey() + ", value = " + entry.getValue(),
getName());
// TODO : Need to verify Boolean Value Input
gigaspace.createSpace(entry.getKey().name(),
entry.getValue().getURL(), false);
gigaSpaceURL = entry.getValue().getURL();
}
} catch (Exception e) {
return;
}
GenericUtil.updateLogLevel("INFO",
"com.renovite.ripps.ap.gs.Spaces");
LOGGER.info("\n************************************\nConnected with Gigaspace successfully:URL:" + gigaSpaceURL
+ "\n************************************\n");
GenericUtil.updateLogLevel("ERROR",
"com.renovite.ripps.ap.gs.Spaces");
}
Take reference of Gigaspace by using getGigaSpace() method which takes spacekey as an argument.If it throw exception at run time, it means application is not able to connect with specified Gigaspace url.
Or more elegant way, In your Gigaspace proxy class (which actually implements IGigaspace) override the getGigaSpace() method such that it will return null if connection is not possible.
/** The spaces. */
private transient Map spaces = new HashMap<>();
#Override
public GigaSpace getGigaSpace(String spaceKey) {
if(spaces.get(spaceKey) != null){
return spaces.get(spaceKey).getGigaSpace();
}
return null;
}
spaces is a Map of all urls that are registered with Gigapsace.If no one is registered, we are returning null in the above method.

Is there a simple/complicated way to create requestData() method using Sockets

As the title says, I need a way(no matter if its complicated or not) to create a getData() method that would send request packet to the server > receive the message I already have the system setup but I have a problem with it that I only get the result in PluginMessageReceiveEvent Here's my code with explanations:
public String requestData(String path) {
SocketUtils.sendData("REQUEST|" + p.getPlayer().getUniqueId() + "|" + path, "playerconfig", "BUNGEE");
return /*Need to get data from the plugin message to here*/;
}
#EventHandler
public void onPluginMessageReceive(PluginMessageReceiveEvent e) {
if (e.getChannel().equalsIgnoreCase("playerconfig")) {
String[] args = e.getMessage().split("\\|");
String uuid = args[0];
String path = args[1];//Maybe a HashMap<Path, Data> but that would make the requestData() result return null because you don't get the data instantly.
String data = args[2].replace("_", " ");
if (uuid.equals(p.getPlayer().getUniqueId() + "")) {
return data; //I need to get this result on request data method.
}
}
}
A simple solution is to wait on a lock in the requestData and notify that lock in onPluginMessageReceive. Something like this:
synchronized(this) {
wait();
}
And in your receive method:
synchronized(this) {
notifyAll();
}
Make the data a member field of the class.
Look out for exception handling and syntax errors.

Load all friends using google play services

http://discuss.cocos2d-x.org/t/google-play-game-services/7190/10 here there is a code that is for loading friends. This code is old and I have updated it to new version:
public static void loadFriends(JSONObject parameters) {
if (mFriends.size() > 0) {
mFriends.clear();
}
((AppActivity)mContext).runOnUiThread(new Runnable() {
public void run() {
Games.Players.loadInvitablePlayers(((AppActivity)mContext).getApiClient(), FRIENDS_PER_PAGE, false).setResultCallback(new ResultCallback<LoadPlayersResult>(){
#Override
public void onResult(LoadPlayersResult result) {
Log.w("GOOGLE SERVICES ****** ", "onResult");
PlayerBuffer playerBuffer = result.getPlayers();
for (Player player : playerBuffer) {
mFriends.add(player);
Log.i("GOOGLE SERVICES ---- ", String.format("Found player with id [%s] and display name [%s]", player.getPlayerId(), player.getDisplayName()));
}
if (playerBuffer.getCount() == FRIENDS_PER_PAGE) {
Log.w("GOOGLE SERVICES +++=== ", "loadMoreInvitablePlayers");
Games.Players.loadMoreInvitablePlayers(((AppActivity)mContext).getApiClient(), FRIENDS_PER_PAGE);
} else {
// call out and return all the friends
Log.w("GOOGLE SERVICES======== ", "call out and return all the friends");
for (Player friend : mFriends) {
Log.i("GOOGLE SERVICES", String.format("Found player with id [%s] and display name [%s]", friend.getPlayerId(), friend.getDisplayName()));
}
}
} // onResult
}); // loadInvitablePlayers
} // run
}); // runOnUiThread
}
But this works strangely. It loads from the first call 20 players, whereas the page size is 10 and considers that all the players are loaded. But I actually have more friends. How to load all my friends?
According to documentation, that behavior is normal:
"The number of entries to request for this initial page. Note that if cached data already exists, the returned buffer may contain more than this size, but it is guaranteed to contain at least this many if the collection contains enough records. This must be a value between 1 and 25."

Categories