I have a situation where I have a mobile system that when it's on the road in will connect to symmetricDS via the default sync URL specified in the engines file. But when that device is back at home base, it ends up on the same internal network as the master symmetricDS server. So it cannot resolve the outside hostname from inside the network.
Anyway.... I want to setup an symmetricDS extension so that it can try the default URL and then fallback to a secondary IP of 192.168.0.5 if it doesn't work. This is the code snippet that I think I need to start with. I have never done any java, so I'm a little lost reading this.
import java.net.URI;
import org.jumpmind.symmetric.ISymmetricEngine;
import org.jumpmind.symmetric.transport.ISyncUrlExtension;
import org.jumpmind.symmetric.ext.ISymmetricEngineAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SyncUrlRewrite implements ISymmetricEngineAware, ISyncUrlExtension {
protected Logger log = LoggerFactory.getLogger(getClass());
ISymmetricEngine engine;
#Override
public String resolveUrl(URI url) {
return url.toString();
}
#Override
public void setSymmetricEngine(ISymmetricEngine engine) {
this.engine = engine;
}
}
You might try looking at and implementing the ISyncUrlExtension interface with the resolveUrl() method. To get it to work, configure the sync_url for a node with the protocol of ext://beanName. beanName is the name you give your extension point in the extension xml file.
Then, you can implement logic to have the resolveUrl() method parse, for example, a list of URLs for your mobile client to try in the sync_url string.
public String resolveUrl(URI uri) {
if (uri.toString().startsWith("ext")) {
Map<String, String> params = getParameters(uri);
//Do some logic with your parsed parameters and return a url
} else {
else return uri.toString();
}
}
The HttpBandwidthSelector class can give you an example implementation.
Related
Can I use the Bing Maps API with Java for geolocation? I have the API key but I can't find anything on the net.
I've found a method with an Excel Macro that works but isn't enough, I need a java console script to do it.
Cheers, Damiano.
There doesn't appear to be any official way to make use of the Maps API in Java.
However, there is an unofficial Java wrapper for the API located here. This hasn't been updated in a while, so there's no guarantee it will still work, but it should be a good starting point for implementing geocoding requests.
There is also a method for implementing reverse-geocoding requests in the same wrapper at client.reverseGeocode().
import net.virtualearth.dev.webservices.v1.common.GeocodeResult;
import net.virtualearth.dev.webservices.v1.geocode.GeocodeRequest;
import net.virtualearth.dev.webservices.v1.geocode.GeocodeResponse;
import com.google.code.bing.webservices.client.BingMapsWebServicesClientFactory;
import com.google.code.bing.webservices.client.geocode.BingMapsGeocodeServiceClient;
import com.google.code.bing.webservices.client.geocode.BingMapsGeocodeServiceClient.GeocodeRequestBuilder;
public class BingMapsGeocodeServiceSample {
public static void main(String[] args) throws Exception {
BingMapsWebServicesClientFactory factory = BingMapsWebServicesClientFactory.newInstance();
BingMapsGeocodeServiceClient client = factory.createGeocodeServiceClient();
GeocodeResponse response = client.geocode(createGeocodeRequest(client));
printResponse(response);
}
private static void printResponse(GeocodeResponse response) {
for (GeocodeResult result : response.getResults().getGeocodeResult()) {
System.out.println(result.getDisplayName());
}
}
private static GeocodeRequest createGeocodeRequest(BingMapsGeocodeServiceClient client) {
GeocodeRequestBuilder builder = client.newGeocodeRequestBuilder();
builder.withCredentials("xxxxxx", null);
builder.withQuery("1 Microsoft Way, Redmond, WA");
// builder.withOptionsFilter(Confidence.HIGH);
return builder.getResult();
}
}
In my java code I call another 3rd party java class.
I want to catch that latter System.exit() exit code
So I use security-manager as suggested in this post
The problem is that I cannot read files now, I get permissions error
as seen in that post.
How can I catch the exit code and still read files?
Published class MyClass {
class MySecurityManager extends SecurityManager {
#Override
public void checkExit(int status) {
throw new SecurityException();
}
}
public void foo() {
MySecurityManager secManager = new MySecurityManager();
System.setSecurityManager(secManager);
try {
ConfigValidator.main(new String[]{"-dirs", SdkServiceConfig.s.PROPERTIES_FILE_PATH});
new FileInputStream(new File("/Users/eladb/WorkspaceQa/sdk-service/src/main/resources/convert_conditions.sh"));
} catch (SecurityException e) {
//Do something if the external code used System.exit()
String a = "1";
}
catch (Exception e) {
logger.error("failed converting properties file to proto", e);
}
}
}
You have two separate problems: Your trusted code cannot read the file, while the untrusted third-party library can still call System#exit unhindered. The former can be easily circumvented by granting further privileges to the trusted code; the latter is a tad trickier to address.
A bit of background
Privilege assignment
Code (the ProtectionDomains encapsulated by a thread's AccessControlContext) generally gets assigned Permissions in two ways: Statically, by the ClassLoader, upon class definition, and/or dynamically, by the Policy in effect. Other, less frequently encountered alternatives, exist as well: DomainCombiners, for instance, can modify AccessControlContexts' domains (and therefore the effective permissions of their respective code that is subject to authorization) on the fly, and custom domain implementations may use their own logic for permission implication, possibly disregarding or altering the semantics of the policy. By default the permission set of a domain is the union of its static and dynamic permissions. As for how exactly classes are mapped to domains, it is, for the most part, up to the loader's implementation. By default, all classes, JAR'ed or otherwise, residing beneath the same class path entry, are grouped under the same domain. More restrictive class loaders may choose to e.g. allocate a domain per class, which could be used to prevent communication even between classes within the same package.
Privilege evaluation
Under the default SecurityManager, for a privileged operation (an invocation of any method having a SecurityManager#checkXXX within its body) to succeed, every domain (of every class of every method) of the effective AccessControlContext must have been assigned, as explained above, the permission being checked. Recall however that the context need not necessarily represent "the truth" (the actual call stack)—system code gets optimized away early on, while AccessController#doPrivileged calls, along with the DomainCombiner potentially coupled to the AccessControlContext can modify the context's domains, and the authorization algorithm in its entirety, consequently.
Problem and workarounds
The issue with System#exit is that the corresponding permission (RuntimePermission("exitVM.*")) is one amongst few that are statically assigned by the default application class loader (sun.misc.Launcher$AppClassLoader) to all domains associated with classes loaded from the class path.
A number of alternatives come to mind:
Installing a custom SecurityManager which denies the particular right based on, e.g., the class attempting to terminate the JVM process.
Loading the third-party library from a "remote" location (a directory outside of the class path), so that it gets treated as "untrusted" code by its class loader.
Authoring and installing a different application class loader, which does not assign the extraneous permission.
Plugging a custom domain combiner into the access control context, which replaces, at the time of an authorization decision, all third-party domains with equivalent ones that do not have the offending permission.
I should, for the sake of completeness, note that at the Policy level, unfortunately, nothing can be done to negate statically assigned permissions.
The first option is overall the most convenient one, but I will not explore it further because:
The default SecurityManager is quite flexible, thanks to the handful of components (AccessController et al.) it interacts with. The background section in the beginning aimed to serve as a reminder of that flexibility, which "quick-n'-dirty" method overrides tend to cripple.
Careless modifications of the default implementation might cause (system) code to misbehave in curious ways.
Frankly, because it's boring―it's the one-size-fits-all solution perpetually advocated, while the fact that the default manager was standardized in 1.2 for a reason has long been forgotten.
The second alternative is easy to implement but impractical, complicating either development or the build process. Assuming you are not planning to invoke the library solely reflectively, or aided by interfaces present on the class path, it would have to be present initially, during development, and relocated before execution.
The third is, at least in the context of a standalone Java SE application, fairly straightforward and should not pose too much of a burden on performance. It is the approach I will favour herein.
The last option is the most novel and least convenient. It is hard to securely implement, has the greatest potential for performance degradation, and burdens client code with ensuring presence of the combiner prior to every delegation to untrusted code.
Proposed solution
The custom ClassLoader
The following is to be used as the replacement of the default application loader, or alternatively as the context class loader, or the loader used to load at least the untrusted classes. There is nothing novel to this implementation—all it does is prevent delegation to the default application class loader when the class in question is assumed to not be a system one. URLClassLoader#findClass, in turn, does not assign RuntimePermission("exitVM.*") to the domains of the classes it defines.
package com.example.trusted;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.regex.Pattern;
public class ClasspathClassLoader extends URLClassLoader {
private static final Pattern SYSTEM_CLASS_PREFIX = Pattern.compile("((java(x)?|sun|oracle)\\.).*");
public ClasspathClassLoader(ClassLoader parent) {
super(new URL[0], parent);
String[] classpath = System.getProperty("java.class.path").split(File.pathSeparator);
for (String classpathEntry : classpath) {
try {
if (!classpathEntry.endsWith(".jar") && !classpathEntry.endsWith("/")) {
// URLClassLoader assumes paths without a trailing '/' to be JARs by default
classpathEntry += "/";
}
addURL(new URL("file:" + classpathEntry));
}
catch (MalformedURLException mue) {
System.err.println(MessageFormat.format("Erroneous class path entry [{0}] skipped.", classpathEntry));
}
}
}
#Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class<?> ret;
synchronized (getClassLoadingLock(name)) {
ret = findLoadedClass(name);
if (ret != null) {
return ret;
}
if (SYSTEM_CLASS_PREFIX.matcher(name).matches()) {
return super.loadClass(name, resolve);
}
ret = findClass(name);
if (resolve) {
resolveClass(ret);
}
}
return ret;
}
}
If you also wish to fine-tune the domains assigned to loaded classes, you will additionally have to override findClass. The following variant of the loader is a very crude attempt at doing so. constructClassDomain therein merely creates one domain per class path entry (which is more or less the default), but can be modified to do something different.
package com.example.trusted;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.ByteBuffer;
import java.security.AccessController;
import java.security.CodeSource;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.security.cert.Certificate;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
public final class ClasspathClassLoader extends URLClassLoader {
private static final Pattern SYSTEM_CLASS_PREFIX = Pattern.compile("((java(x)?|sun|oracle)\\.).*");
private static final List<WeakReference<ProtectionDomain>> DOMAIN_CACHE = new ArrayList<>();
// constructor, loadClass same as above
#Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
URL classOrigin = getClassResource(name);
if (classOrigin == null) {
return super.findClass(name);
}
URL classCodeSourceOrigin = getClassCodeSourceResource(classOrigin);
if (classCodeSourceOrigin == null) {
return super.findClass(name);
}
return defineClass(name, readClassData(classOrigin), constructClassDomain(classCodeSourceOrigin));
}
private URL getClassResource(String name) {
return AccessController.doPrivileged((PrivilegedAction<URL>) () -> getResource(name.replace(".", "/") + ".class"));
}
private URL getClassCodeSourceResource(URL classResource) {
for (URL classpathEntry : getURLs()) {
if (classResource.getPath().startsWith(classpathEntry.getPath())) {
return classpathEntry;
}
}
return null;
}
private ByteBuffer readClassData(URL classResource) {
try {
BufferedInputStream in = new BufferedInputStream(classResource.openStream());
ByteArrayOutputStream out = new ByteArrayOutputStream();
int i;
while ((i = in.read()) != -1) {
out.write(i);
}
return ByteBuffer.wrap(out.toByteArray());
}
catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
private ProtectionDomain constructClassDomain(URL classCodeSourceResource) {
ProtectionDomain ret = getCachedDomain(classCodeSourceResource);
if (ret == null) {
CodeSource cs = new CodeSource(classCodeSourceResource, (Certificate[]) null);
DOMAIN_CACHE.add(new WeakReference<>(ret = new ProtectionDomain(cs, getPermissions(cs), this, null)));
}
return ret;
}
private ProtectionDomain getCachedDomain(URL classCodeSourceResource) {
for (WeakReference<ProtectionDomain> domainRef : DOMAIN_CACHE) {
ProtectionDomain domain = domainRef.get();
if (domain == null) {
DOMAIN_CACHE.remove(domainRef);
}
else if (domain.getCodeSource().implies(new CodeSource(classCodeSourceResource, (Certificate[]) null))) {
return domain;
}
}
return null;
}
}
The "unsafe" code
package com.example.untrusted;
public class Test {
public static void testExitVm() {
System.out.println("May I...?!");
System.exit(-1);
}
}
The entry point
package com.example.trusted;
import java.security.AccessControlException;
import java.security.Permission;
import com.example.untrusted.Test;
public class Main {
private static final Permission EXIT_VM_PERM = new RuntimePermission("exitVM.*");
public static void main(String... args) {
System.setSecurityManager(new SecurityManager());
try {
Test.testExitVm();
}
catch (AccessControlException ace) {
Permission deniedPerm = ace.getPermission();
if (EXIT_VM_PERM.implies(deniedPerm)) {
ace.printStackTrace();
handleUnauthorizedVmExitAttempt(Integer.parseInt(deniedPerm.getName().replace("exitVM.", "")));
}
}
}
private static void handleUnauthorizedVmExitAttempt(int exitCode) {
System.out.println("here let me do it for you");
System.exit(exitCode);
}
}
Testing
Packaging
Place the loader and the main class in one JAR (let's call it trusted.jar) and the demo untrusted class in another (untrusted.jar).
Assigning privileges
The default Policy (sun.security.provider.PolicyFile) is backed by the file at <JRE>/lib/security/java.policy, as well as any of the files referenced by the policy.url.n properties in <JRE>/lib/security/java.security. Modify the former (the latter should hopefully be empty by default) as follows:
// Standard extensions get all permissions by default
grant codeBase "file:${{java.ext.dirs}}/*" {
permission java.security.AllPermission;
};
// no default permissions
grant {};
// trusted code
grant codeBase "file:///path/to/trusted.jar" {
permission java.security.AllPermission;
};
// third-party code
grant codeBase "file:///path/to/untrusted.jar" {
permission java.lang.RuntimePermission "exitVM.-1", "";
};
Note that it is virtually impossible to get components extending the security infrastructure (custom class loaders, policy providers, etc.) to work properly without granting them AllPermission.
Running
Run:
java -classpath "/path/to/trusted.jar:/path/to/untrusted.jar" -Djava.system.class.loader=com.example.trusted.ClasspathClassLoader com.example.trusted.Main
The privileged operation should succeed.
Next comment out the RuntimePermission under untrusted.jar, within the policy file, and re-run. The privileged operation should fail.
As a closing note, when debugging AccessControlExceptions, running with -Djava.security.debug=access=domain,access=failure,policy can help track down offending domains and policy configuration issues.
I'm wondering how to do a permanent redirect 301 in Play framework 2.0.x for subdomains.
ex: www.example.com/* redirected to example.com/*. Anyone tried this before ?
The Global object will allow you to intercept the request. For obvious reasons you should do it with GET requests (ie. for SEO purposes), but others, like POST, PUT etc. should be created properly from the beginning in your views.
On the other hand, if it's just some app for serving common HTML pages for life production consider using some HTTP server on front of it - then you can do the trick with some rewriting rule.
import play.GlobalSettings;
import play.mvc.Action;
import play.mvc.Http;
import play.mvc.Result;
import java.lang.reflect.Method;
public class Global extends GlobalSettings {
#Override
public Action onRequest(final Http.Request request, Method method) {
if ("GET".equals(request.method()) && "www.example.com".equals(request.host())) {
return new Action.Simple() {
public Result call(Http.Context ctx) throws Throwable {
return movedPermanently("http://example.com" + request.path());
}
};
}
return super.onRequest(request, method);
}
}
In conf/routes file
GET / controllers.Application.index(path = "")
GET /*path controllers.Application.index(path)
In apps/controllers/Application.scala
object Application extends Controller {
def index(path: String) = Action {
Redirect("http://example.com/" + path, status = MOVED_PERMANENTLY)
}
}
I have an application that needs to make a SOAP client request to a system on the Internet, so it needs to go though our HTTP proxy.
One can do this by setting system-wide values such as system properties:
// Cowboy-style. Blow away anything any other part of the application has set.
System.getProperties().put("proxySet", "true");
System.getProperties().put("https.proxyHost", HTTPS_PROXY_HOST);
System.getProperties().put("https.proxyPort", HTTPS_PROXY_PORT);
Or by setting the default ProxySelector (also a system-wide setting):
// More Cowboy-style! Every thing Google has found says to do it this way!?!?!
ProxySelector.setDefault(new MyProxySelector(HTTPS_PROXY_HOST, HTTPS_PROXY_PORT));
Neither of these is a wise choice if there is the possibility of other subsystems wanting to access web servers via different HTTP proxies or without any proxy. Using the ProxySelector would let me configure which connections use the proxy, but I would have to figure that out for every single thing in the huge application.
A reasonable API would have a method that took a java.net.Proxy object just like the java.net.Socket(java.net.Proxy proxy) constructor does. That way the necessary settings are local to the part of the system that needs to set them. Is there some way to do this with a JAX-WS?
I do not want to set a system-wide proxy configuration.
I recommend using a custom ProxySelector. I had the same problem and it works great and is super flexible. It's simple too.
Here's my CustomProxySelector:
import org.hibernate.validator.util.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.*;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
/**
* So the way a ProxySelector works is that for all Connections made,
* it delegates to a proxySelector(There is a default we're going to
* override with this class) to know if it needs to use a proxy
* for the connection.
* <p>This class was specifically created with the intent to proxy connections
* going to the allegiance soap service.</p>
*
* #author Nate
*/
class CustomProxySelector extends ProxySelector {
private final ProxySelector def;
private Proxy proxy;
private static final Logger logger = Logger.getLogger(CustomProxySelector.class.getName());
private List<Proxy> proxyList = new ArrayList<Proxy>();
/*
* We want to hang onto the default and delegate
* everything to it unless it's one of the url's
* we need proxied.
*/
CustomProxySelector(String proxyHost, String proxyPort) {
this.def = ProxySelector.getDefault();
proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, (null == proxyPort) ? 80 : Integer.valueOf(proxyPort)));
proxyList.add(proxy);
ProxySelector.setDefault(this);
}
#Override
public List<Proxy> select(URI uri) {
logger.info("Trying to reach URL : " + uri);
if (uri == null) {
throw new IllegalArgumentException("URI can't be null.");
}
if (uri.getHost().contains("allegiancetech")) {
logger.info("We're trying to reach allegiance so we're going to use the extProxy.");
return proxyList;
}
return def.select(uri);
}
/*
* Method called by the handlers when it failed to connect
* to one of the proxies returned by select().
*/
#Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
logger.severe("Failed to connect to a proxy when connecting to " + uri.getHost());
if (uri == null || sa == null || ioe == null) {
throw new IllegalArgumentException("Arguments can't be null.");
}
def.connectFailed(uri, sa, ioe);
}
}
If you are using JAX-WS you might be able to set the socket factory used by the underlying HttpURLConnection. I see vague signs that this is possible for SSL (see HTTPS SSLSocketFactory) but I'm not certain if you can do that for regular HTTP connections (or quite frankly how that even works: the JAXWSProperties class they reference appears to be a non-standard JDK class).
If you can set the socket factory then you can configure a custom socket factory that uses the specific proxy you want.
I've run through the Google Web Toolkit StockWatcher Tutorial using Eclipse and the Google Plugin, and I'm attempting to make some basic changes to it so I can better understand the RPC framework.
I've modified the "getStocks" method on the StockServiceImpl server-side class so that it returns an array of Stock objects instead of String objects. The application compiles perfectly, but the Google Web Toolkit is returning the following error:
"No source code is available for type com.google.gwt.sample.stockwatcher.server.Stock; did you forget to inherit a required module?"
It seems that the client-side classes can't find an implementation of the Stock object, even though the class has been imported. For reference, here is a screenshot of my package hierarchy:
I suspect that I'm missing something in web.xml, but I have no idea what it is. Can anyone point me in the right direction?
EDIT: Forgot to mention that the Stock class is persistable, so it needs to stay on the server-side.
After much trial and error, I managed to find a way to do this. It might not be the best way, but it works. Hopefully this post can save someone else a lot of time and effort.
These instructions assume that you have completed both the basic StockWatcher tutorial and the Google App Engine StockWatcher modifications.
Create a Client-Side Implementation of the Stock Class
There are a couple of things to keep in mind about GWT:
Server-side classes can import client-side classes, but not vice-versa (usually).
The client-side can't import any Google App Engine libraries (i.e. com.google.appengine.api.users.User)
Due to both items above, the client can never implement the Stock class that we created in com.google.gwt.sample.stockwatcher.server. Instead, we'll create a new client-side Stock class called StockClient.
StockClient.java:
package com.google.gwt.sample.stockwatcher.client;
import java.io.Serializable;
import java.util.Date;
public class StockClient implements Serializable {
private Long id;
private String symbol;
private Date createDate;
public StockClient() {
this.createDate = new Date();
}
public StockClient(String symbol) {
this.symbol = symbol;
this.createDate = new Date();
}
public StockClient(Long id, String symbol, Date createDate) {
this();
this.id = id;
this.symbol = symbol;
this.createDate = createDate;
}
public Long getId() {
return this.id;
}
public String getSymbol() {
return this.symbol;
}
public Date getCreateDate() {
return this.createDate;
}
public void setId(Long id) {
this.id = id;
}
public void setSymbol(String symbol) {
this.symbol = symbol;
}
}
Modify Client Classes to Use StockClient[] instead of String[]
Now we make some simple modifications to the client classes so that they know that the RPC call returns StockClient[] instead of String[].
StockService.java:
package com.google.gwt.sample.stockwatcher.client;
import com.google.gwt.sample.stockwatcher.client.NotLoggedInException;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
#RemoteServiceRelativePath("stock")
public interface StockService extends RemoteService {
public Long addStock(String symbol) throws NotLoggedInException;
public void removeStock(String symbol) throws NotLoggedInException;
public StockClient[] getStocks() throws NotLoggedInException;
}
StockServiceAsync.java:
package com.google.gwt.sample.stockwatcher.client;
import com.google.gwt.sample.stockwatcher.client.StockClient;
import com.google.gwt.user.client.rpc.AsyncCallback;
public interface StockServiceAsync {
public void addStock(String symbol, AsyncCallback<Long> async);
public void removeStock(String symbol, AsyncCallback<Void> async);
public void getStocks(AsyncCallback<StockClient[]> async);
}
StockWatcher.java:
Add one import:
import com.google.gwt.sample.stockwatcher.client.StockClient;
All other code stays the same, except addStock, loadStocks, and displayStocks:
private void loadStocks() {
stockService = GWT.create(StockService.class);
stockService.getStocks(new AsyncCallback<String[]>() {
public void onFailure(Throwable error) {
handleError(error);
}
public void onSuccess(String[] symbols) {
displayStocks(symbols);
}
});
}
private void displayStocks(String[] symbols) {
for (String symbol : symbols) {
displayStock(symbol);
}
}
private void addStock() {
final String symbol = newSymbolTextBox.getText().toUpperCase().trim();
newSymbolTextBox.setFocus(true);
// Stock code must be between 1 and 10 chars that are numbers, letters,
// or dots.
if (!symbol.matches("^[0-9a-zA-Z\\.]{1,10}$")) {
Window.alert("'" + symbol + "' is not a valid symbol.");
newSymbolTextBox.selectAll();
return;
}
newSymbolTextBox.setText("");
// Don't add the stock if it's already in the table.
if (stocks.contains(symbol))
return;
addStock(new StockClient(symbol));
}
private void addStock(final StockClient stock) {
stockService.addStock(stock.getSymbol(), new AsyncCallback<Long>() {
public void onFailure(Throwable error) {
handleError(error);
}
public void onSuccess(Long id) {
stock.setId(id);
displayStock(stock.getSymbol());
}
});
}
Modify the StockServiceImpl Class to Return StockClient[]
Finally, we modify the getStocks method of the StockServiceImpl class so that it translates the server-side Stock classes into client-side StockClient classes before returning the array.
StockServiceImpl.java
import com.google.gwt.sample.stockwatcher.client.StockClient;
We need to change the addStock method slightly so that the generated ID is returned:
public Long addStock(String symbol) throws NotLoggedInException {
Stock stock = new Stock(getUser(), symbol);
checkLoggedIn();
PersistenceManager pm = getPersistenceManager();
try {
pm.makePersistent(stock);
} finally {
pm.close();
}
return stock.getId();
}
All other methods stay the same, except getStocks:
public StockClient[] getStocks() throws NotLoggedInException {
checkLoggedIn();
PersistenceManager pm = getPersistenceManager();
List<StockClient> stockclients = new ArrayList<StockClient>();
try {
Query q = pm.newQuery(Stock.class, "user == u");
q.declareParameters("com.google.appengine.api.users.User u");
q.setOrdering("createDate");
List<Stock> stocks = (List<Stock>) q.execute(getUser());
for (Stock stock : stocks)
{
stockclients.add(new StockClient(stock.getId(), stock.getSymbol(), stock.getCreateDate()));
}
} finally {
pm.close();
}
return (StockClient[]) stockclients.toArray(new StockClient[0]);
}
Summary
The code above works perfectly for me when deployed to Google App Engine, but triggers an error in Google Web Toolkit Hosted Mode:
SEVERE: [1244408678890000] javax.servlet.ServletContext log: Exception while dispatching incoming RPC call
com.google.gwt.user.server.rpc.UnexpectedException: Service method 'public abstract com.google.gwt.sample.stockwatcher.client.StockClient[] com.google.gwt.sample.stockwatcher.client.StockService.getStocks() throws com.google.gwt.sample.stockwatcher.client.NotLoggedInException' threw an unexpected exception: java.lang.NullPointerException: Name is null
Let me know if you encounter the same problem or not. The fact that it works in Google App Engine seems to indicate a bug in Hosted Mode.
GWT needs the .java file in addition to the .class file. Additionally, Stock needs to be in the "client" location of a GWT module.
The GWT compiler doesn't know about Stock, because it's not in a location it looks in. You can either move it to the client folder, or if it makes more sense leave it where it is and create a ModuleName.gwt.xml that references any other classes you want, and get your Main.gwt.xml file to inherit from that.
eg: DomainGwt.gwt.xml
<module>
<inherits name='com.google.gwt.user.User'/>
<source path="javapackagesabovethispackagegohere"/>
</module>
and:
<module rename-to="gwt_ui">
<inherits name="com.google.gwt.user.User"/>
<inherits name="au.com.groundhog.groundpics.DomainGwt"/>
<entry-point class="au.com.groundhog.groundpics.gwt.client.GPicsUIEntryPoint"/>
</module>
There's a better answer here: GWT Simple RPC use case problem : Code included
Basically, you can add parameters to your APPNAME.gwt.xml file so the compiler to give the compiler a path to the server-side class.
I was getting the same issue and the "mvn gwt:compile" output was not very helpful.
Instead, when I tried deploying to tomcat (via the maven tomcat plugin: mvn tomcat:deploy) I got helpful error messages.
A few things I had to fix up:
Make the object that is sent from the client to the server implement Serializable
Add an empty-arg constructor to that same object
Yes, it is sure that we need to use the Serialization for getting the server objects to the client. These modile?? file settings won't work to use the Stock class in the client side.
In your case you have only one class Stock and you can create a StockClient in client side. It is easy. But what will be the solution if anyone having more classes. Something like the properties of this class are also some other type of classes.
Example: stock.getEOD(date).getHigh();
getEOD will return another class with the given date and that class has the getHigh method.
What to do in such big cases? I don't think creating all classes implementing serialization in client side is good for that. Then we have to write code in both server and client. all classes two times.
Keying off of rustyshelf's answer above ...
In my case I needed to edit the ModuleName.gwt.xml file and add the following:
<source path='client'/>
<source path='shared'/>
I created my project with the New->Web Application Project wizard but unchecked the Generate project sample code option. I then created the shared package. Had I not unchecked that, the package would have been created for me and the xml file modified per the above.
There is a far more simple and easy solution for that. If you want to send an object of your custom designed class from server side to client side you should define this custom class in shared package.
For example for your case the you just have to carry the Stock.java class (by drag and drop) into
com.google.gwt.sample.stockwatcher.shared
package. However from your package hierarchy screenshot it seems that you had deleted this shared package. Just re-create this package and drop the Stock.java inside it and let the game begin.