Server side, I construct a java.util.Hashmap, populate it with values (key and value are both String) and pass it back to the client via an async callback. It is empty when it gets back to the client side.
I can replicate this with a net new hashmap that is only used in one place server side. Java 6 and GWT 2.7
Server side service:
public class Service extends RemoteServiceServlet implements IService {
public Model buildModel() {
Model model = new Model();
model.addItemToMyMap("key", "value");
return model;
}
}
Model:
public class Model implements Serializable {
private Map<String, String> myMap = new HashMap<String, String>();
public void addItemToMyMap(String key, String value) {
if(key != null) {
myMap.put(key, value);
}
}
public Map<String, String> getMyMap() {
return myMap;
}
}
Async Interface:
public interface IServiceAsync {
public void buildModel(AsyncCallback<Model> callback); {
}
Client Side:
service.buildModel(new AsyncCallback<Model>() {
public void onSuccess(Model model) {
logger.warning(model.getMyMap().size());
}
public void onFailure(Throwable caught) {
logger.warning("ERROR!");
}
}
Problem solved. Eclipse save actions was making the map final and thus preventing serialization. When I removed the final modifier (and prevented Eclipse from adding it back), I was able to see the map's contents on the client side.
Related
I have a class which calls two singleton classes FirstClass and SecondClass as below. Is there a way to access the data computed in FirstClass in the SecondClass. In this case I don't want to make external service call in second class since the first class has already called it. Instead, just use the data (stored in first class function) in the second data function. What are the ways to do it.
public class CallingFunction() {
List<String> generateData() {
return Lists.newArrayList(new FirstClass(), new SecondClass())
}
#Singleton
public class FirstClass() extends interface {
public String function() {
//operations. This function calls multiple services and stores ouput to hashMap
Map<String, String> hashedData = Maps.newHashMap();
hashedData.put(dataFromAnotherService);
return hashedData.get("abc");
}
}
#Singleton
public class SecondClass() extends interface {
public String function() {
//Use hashedData here instead of invoking the service again.
//Other operations
return "data";
}
}
Yes you can achieve the functionality by something below logic:
#Singleton
public class FirstClass() extends interface {
private static FirstClass instance;
private Map<String, String> hashedData = new HashMap<>();
public String function() {
//operations. This function calls multiple services and stores ouput to hashMap
hashedData.put(dataFromAnotherService);
return hashedData.get("abc");
}
public Map<String, String> getHashedData() {
return this.hashedData;
}
public static FirstClass getInstance() {
if (instance == null) instance = new FirstClass();
return instance;
}
}
#Singleton
public class SecondClass() extends interface {
public String function() {
FirstClass instance = FirstClass.getInstance();
// instance.getHashedData() here instead of invoking the service again.
//Other operations
return "data";
}
}
I am developing application which having Parse Platform. To fetch data I am calling ParseCloud.callFunctionInBackground function.
I have registered the Parse and its sub class into the Application class like below :
public class App extends Application {
#Override
public void onCreate(){
super.onCreate();
Parse.setLogLevel(Parse.LOG_LEVEL_VERBOSE);
OkHttpClient.Builder builder = new OkHttpClient.Builder();
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
builder.networkInterceptors().add(httpLoggingInterceptor);
ParseObject.registerSubclass(ParseMessage.class);
Parse.initialize(new Parse.Configuration.Builder(this)
.applicationId("KEY")
.server("URL")
.build());
}
}
I have below model class which extends ParseObject :
#ParseClassName("ParseMessage")
public class ParseMessage extends ParseObject {
// Ensure that your subclass has a public default constructor
public ParseMessage() {
super();
}
public ParsePhoto getPhotos() {
return (ParsePhoto) getParseObject("photos");
}
public void setPhotos(ParsePhoto value) {
put("photos", value);
}
public String getCaption() {
return getString("caption");
}
public void setCaption(String value) {
put("caption", value);
}
}
When I calling this below method from my Fragment :
HashMap<String, Object> params = new HashMap<String, Object>();
ParseCloud.callFunctionInBackground("MY_METHOD", params, new FunctionCallback<ArrayList<ParseMessage>>() {
public void done(ArrayList<ParseMessage> mapObject, ParseException e) {
if (e == null) {
ParseMessage object = mapObject.get(i);
}
} else {
}
}
});
But I am getting below exception :
java.lang.ClassCastException: com.parse.ParseObject cannot be cast to
com.example.ParseMessage
I already searched lots of thins from Google and Stackoverflow, but I did not get any solutions of it. Can anyone help me into this as I already spend a lot of time on this. Below response which I am getting from Parse :
The info you have provided is not very concrete, but from the debugger screen, it looks like you are trying to convert ParsePhoto into ParseMessage. ParsePhoto is subclass of ParseObject, and I believe this is causing the issue.
I have below Enum from which I am calling appropriate execute method basis on what type of enum (eventType) is passed.
public enum EventType {
EventA {
#Override
public Map<String, Map<String, String>> execute(String eventMapHolder) {
final Map<String, String> holder = parseStringToMap(eventMapHolder);
if (holder.isEmpty() || Strings.isNullOrEmpty(holder.get("m_itemId"))) {
return ImmutableMap.of();
}
String itemId = holder.get("m_itemId");
Map<String, String> clientInfoHolder = getClientInfo(itemId);
holder.putAll(clientInfoHolder);
return ImmutableMap.<String, Map<String, String>>builder().put(EventA.name(), holder)
.build();
}
},
EventB {
#Override
public Map<String, Map<String, String>> execute(String eventMapHolder) {
final Map<String, String> holder = parseStringToMap(eventMapHolder);
if (holder.isEmpty() || Strings.isNullOrEmpty(holder.get("m_itemId"))) {
return ImmutableMap.of();
}
return ImmutableMap.<String, Map<String, String>>builder().put(EventB.name(), holder)
.build();
}
},
EventC {
#Override
public Map<String, Map<String, String>> execute(String eventMapHolder) {
final Map<String, String> holder = parseStringToMap(eventMapHolder);
if (holder.isEmpty() || Strings.isNullOrEmpty(holder.get("m_itemId"))) {
return ImmutableMap.of();
}
String itemId = holder.get("m_itemId");
Map<String, String> clientInfoHolder = getClientInfo(itemId);
holder.putAll(clientInfoHolder);
return ImmutableMap.<String, Map<String, String>>builder().put(EventC.name(), holder)
.build();
}
};
public abstract Map<String, Map<String, String>> execute(String eventMapHolder);
public Map<String, String> parseStringToMap(String eventMapHolder) {
// parse eventMapHolder String to Map
}
public Map<String, String> getClientInfo(final String clientId) {
// code to populate the map and return it
}
}
For example: If I get "EventA", then I am calling it's execute method. Similarly if I get "EventB" then I am callings it's execute method and so on.
String eventType = String.valueOf(payload.get("eventType"));
String eventMapHolder = String.valueOf(payload.get("eventMapHolder"));
Map<String, Map<String, String>> processedMap = EventType.valueOf(eventType).execute(eventMapHolder);
In general I will have more event types (around 10-12) in the same enum class and mostly they will do same operation as EventA, EventB and EventC.
Question:
Now as you can see, code in execute method of EventA and EventC are identically similar but the only difference is what I put as "key" (event name) in the returned immutable map. Is there any way to remove that duplicated code but still achieve the same functionality in the enum.
For example, something on this ground. By writing multiple enums side by side separated by comma (if the execute method functionality is same). I know this doesn't work because I have a abstract method which I need to implement it everywhere but is it still possible by making some changes or any other better way?
public enum EventType {
EventA,
EventC {
#Override
public Map<String, Map<String, String>> execute(String eventMapHolder) {
// same code which is there in execute method for EventA and EventC
}
},
EventB {
#Override
public Map<String, Map<String, String>> execute(String eventMapHolder) {
// same code which is there in execute method of EventB
}
};
// other methods which are there already
}
I know one way is to make a method with all the common things and call those method by passing appropriate Event type enum name. Is there any other way apart from that by using enum features or any other changes?
If there is any other better way or any other design pattern to do this then I am open for suggestions as welll which can help me remove duplicated code.
Idea is - basis on what type of event is passed, I want to call its execute method and avoid duplication if possible.
There are two simple mechanisms (that can of course be combined).
The first one consists in having the execute() in the base class, delegating to specific code defined in each subclass (i.e. the template method pattern):
enum Foo {
A {
#Override
protected void specificCode() {
//...
}
},
B {
#Override
public void specificCode() {
//...
}
};
public void execute() {
// ... common code
specificCode();
// ... common code
}
protected abstract void specificCode();
}
The second one consists in having the execute() overridden in each subclass but delegating to a common method defined in the base class:
enum Foo {
A {
#Override
public void execute() {
//...
commonCode();
// ...
}
},
B {
#Override
public void execute() {
//...
commonCode();
// ...
}
};
public abstract void execute();
protected void commonCode() {
// ...
}
}
Something like this?
package enumCodeReuse;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
public enum EventType2 {
EventA
, EventB
, EventC
;
public Map<String, Map<String, String>> execute(String eventMapHolder) {
final Map<String, String> holder = parseStringToMap(eventMapHolder);
if (holder.isEmpty() || Strings.isNullOrEmpty(holder.get("m_itemId"))) {
return ImmutableMap.of();
}
String itemId = holder.get("m_itemId");
Map<String, String> clientInfoHolder = getClientInfo(itemId);
holder.putAll(clientInfoHolder);
return ImmutableMap.<String, Map<String, String>>builder()
.put(this.name(), holder)
.build();
};
public Map<String, String> parseStringToMap(String eventMapHolder) {
// parse eventMapHolder String to Map
return null; // FIXME
}
public Map<String, String> getClientInfo(final String clientId) {
// code to populate the map and return it
return null; // FIXME
}
}
I am trying to test my Play Application controller, but am struggling to find away to have the test return both session data while utilising an instance of the controller.
public class HomeControllerTest extends WithApplication {
#Override
protected Application provideApplication() {
return new GuiceApplicationBuilder()
.configure("play.http.router", "router.Routes")
.build();
}
#Test
public void testGameChoiceHvH() {
Map form = new HashMap<String, String>();
form.put("gameType", "0");
HomeController homeController = new HomeController();
Result result = invokeWithContext(fakeRequest().bodyForm(form),
() -> homeController.chooseGame());
assertEquals(SEE_OTHER, result.status());
assertEquals("/play", result.header("Location").get());
assertFalse(result.session().isEmpty());
String gameId = result.session().get("game_id");
assertTrue(homeController.getGame(gameId).getCurrentPlayer() instanceof Human);
}
}
The final assertion here is simulating taking the gameId stored in the session and using it retrieve the game instance, created in the controller action and stored in a Map within the controller instance. The issue is that by using invokeWithContext, the result does not contain the Cookie object at all, so it cannot be retrieved.
The alternative method I have found to create a post request is the following:
public class HomeControllerTest extends WithApplication {
#Override
protected Application provideApplication() {
return new GuiceApplicationBuilder()
.configure("play.http.router", "router.Routes")
.build();
}
#Test
public void testGameChoiceHvH() {
Map form = new HashMap<String, String>();
form.put("gameType", "0");
HomeController homeController = new HomeController();
Result result = route(fakeRequest(routes.HomeController.chooseGame()).bodyForm(form));
assertEquals(SEE_OTHER, result.status());
assertEquals("/play", result.header("Location").get());
assertFalse(result.session().isEmpty());
String gameId = result.session().get("game_id");
assertTrue(homeController.getGame(gameId).getCurrentPlayer() instanceof Human);
}
}
However, this obviously means that the final assertion is looking at a new instance of the HomeController, not the one used by the route function, so as such, the Map is empty.
For clarity, here is the relevant controller code:
public class HomeController extends Controller {
private WebInterface ui = new WebInterface();
private Map<String, Board> boardMap = new HashMap<>();
private Map<String, Game> gameMap = new HashMap<>();
public Game getGame(String gameId) {
return gameMap.get(gameId);
}
public Result chooseGame() {
Map<String, String[]> request = request().body().asFormUrlEncoded();
Board board = new Board();
Game game = new Game(new PlayerFactory(ui).create(GameType.values()[Integer.valueOf(request.get("gameType")[0])]));
boardMap.put(Integer.toString(board.hashCode()), board);
gameMap.put(Integer.toString(game.hashCode()), game);
session("game_id", Integer.toString(game.hashCode()));
session("board_id", Integer.toString(board.hashCode()));
return redirect("/play");
}
Any help would be much appreciated.
So, after too long looking for a solution to this, I found the answer in the source code:
/**
* Calls a Callable which invokes a Controller or some other method with a Context
*/
public static <V> V invokeWithContext(RequestBuilder requestBuilder, Callable<V> callable) {
try {
Context.current.set(new Context(requestBuilder));
return callable.call();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
Context.current.remove();
}
}
Based on this, information, it is now obvious why invokeWithContext doesn't expose the context after it is run. So as a quick and dirty implementation of a solution:
#Test
public void testGameChoiceHvH() {
Map form = new HashMap<String, String>();
form.put("gameType", "0");
HomeController homeController = new HomeController();
Http.RequestBuilder request = fakeRequest(routes.HomeController.chooseGame()).bodyForm(form);
try {
Http.Context.current.set(new Http.Context(request));
homeController.chooseGame();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
assertFalse(Http.Context.current().session().isEmpty());
String gameId = Http.Context.current().session().get("game_id");
assertTrue(homeController.getGame(gameId).getCurrentPlayer() instanceof DelayedComputer);
Http.Context.current.remove();
}
}
This exposes both the session, and the homeController instance, so the assertions can both be tested.
I'm having a Map<String, Boolean> which I'd like to edit values from via BeanEditor.
I've been poking around and figured I need to create my own property conduit for it:
public class MapPropertyConduit implements PropertyConduit {
private Object key;
#SuppressWarnings("rawtypes")
private Class dataType;
#SuppressWarnings("rawtypes")
public MapPropertyConduit(Object key, Class dataType) {
this.key = key;
this.dataType = dataType;
}
#Override
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
return null;
}
#SuppressWarnings("rawtypes")
#Override
public Object get(Object instance) {
return ((Map) instance).get(key);
}
#SuppressWarnings("unchecked")
#Override
public void set(Object instance, Object value) {
((Map) instance).put(key, value);
}
#SuppressWarnings("rawtypes")
#Override
public Class getPropertyType() {
return dataType;
}
}
I initialized it like this:
this.reportingModel = beanModelSource.createEditModel(Map.class, messages);
MViewTransactionDisplayModel selectedModel = getMViewTransactionReportModel();
Map<String, Boolean> displayFields = selectedModel.getDisplayModel();
for (Entry<String, Boolean> entry : displayFields.entrySet()) {
this.reportingModel.add(entry.getKey(), new MapPropertyConduit(entry.getKey(), Boolean.class)).dataType(Boolean.class.getName());
}
In my tml I did this:
<t:beaneditor t:id="mapEditorId" t:object="myMap" t:model="reportingModel"/>
And the exception I get is:
Render queue error in BeginRender[MyPage:mapeditorid.propertyeditor]: Unable to locate a block to edit property 'property1' (with data type 'java.lang.Boolean') of object {property1=true, property2=true, property3=true, property4=true, property5=true, property6=true, property7=true, property8=true, property9=true, property10=true, property11=true, property12=true, property13=true}: There is no defined way to edit data of type 'java.lang.Boolean'. Make a contribution to the BeanBlockSource service for this type.
I am kind of puzzled since I was under the impression that I can edit Booleans with a simple checkbox.
It's either that, or I failed on providing/calling custom property conduit?
Is there a way to fix this so I can freely edit the values in a map?
When I changed
this.reportingModel.add(entry.getKey(), new MapPropertyConduit(entry.getKey(), Boolean.class)).dataType(Boolean.class.getName());
to
this.reportingModel.add(entry.getKey(), new MapPropertyConduit(entry.getKey(), Boolean.class)).dataType("boolean");
it suddenly worked.
Does anyone have a complete list of available data types?
There's an example of using a MapPropertyConduit here
You might also be interested in the map: binding prefix here