GWT Maven build with maven profiles - gwt

I'm attempting to use capabilities provided by maven profiles to build customized builds for different server environments. What I'm attempting to do is combine maven resource filtering
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
with it's profile mechanism
<profiles>
<profile>
<id>mock</id>
<properties>
<application-url>http://mock-server-url</application-url>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
</profiles>
to convert this value in a file named server.cfg
${application-url}
to something I can use here:
public interface ServerResource extends ClientBundle {
public static final ServerResource INSTANCE = GWT.create(ServerResource.class);
#Source("server.cfg")
public TextResource server();
}
I can see that the value's been replaced in WEB-INF/classes but it doesn't appear that GWT used the file with the replacement to create the application javascript. How can I do this?

Using GWT compiler permutations to apply this kind of configuration is in my opinion a very bad idea. One of the most common complaints about GWT is the time it takes to compile, and by doing this you're just adding to the problem.
Configuration should usually be read from configuration files (surprise!), like shown here.
Anyway, what you're trying to do seems to me impossible. You cannot tell the client-side code to which server it should connect. This would violate the same-origin policy! The app can only communicate with the server it came from.
To have different apps running in different URLs, you would need to deploy several GWT apps with different names (even if they are basically the same). Then, you would just have to type the correct URL for each app (version) in the browser, and it will "look" at the right app. So you could have URLs like this:
http://myserver.com/app1
http://myserver.com/app2
In order to make a request to a different app running in the same server as the GWT application, you can do something like this:
String serviceUrl = "/app2/someService"; // or some other String sourced from a config file, using a GWT ClientResource for example
RequestBuilder rb = new RequestBuilder(RequestBuilder.GET,
serviceUrl);
try {
// send request from app1 to app2
rb.sendRequest(null, new RequestCallback() {
#Override
public void onResponseReceived(Request request,
Response response) {
log.info("Response: " + response.getStatusText());
// if response is 200 it's ok, you can read the outputStream to see what's in there
}
#Override
public void onError(Request request, Throwable exception) {
log.warning("Request Error", exception);
// do something more
}
});
} catch (RequestException e) {
log.warning("Request Exception", e);
// getting here means trouble with the connection or service!
}

I solved what I was trying to accomplish without the use of maven profiles or the GWT ClientBundle (which I never did get to work in the way I had intended when I wrote the question).
Here were the main issues I hoped to solve using maven profiles and the workaround I employed to solve the issue at hand:
Use Mock MVP Models in Hosted Mode
// inside the initialization for my model locator
boolean hostedMode = GWT.getPermutationStrongName().equals("HostedMode");
if (hostedMode) {
// instantiate mock models
} else {
// instantiate real models to call REST web services
}
Provide real models with correct RESTful server URL
I was able to accomplish this because my GWT app and the RESTful web service url follow a set naming convention. I basically strip the trailing '/' from the URL and append '_services"
String createServicesBaseUrl() {
StringBuffer baseUrl = new StringBuffer(GWT.getHostPageBaseURL());
int length = baseUrl.length();
baseUrl.replace(length-1, length, "_services");
return baseUrl.toString();
}
Enable testing of as much of the MVP Presenter (Activities & Places) as I could
I was already injecting the model locator into my Activity classes, so replacing that with a mock model locator for use by JUnit was straightforward. I did the same for my views and abstracted away some of the other code which didn't seem to work outside of the browser (like the GWT PlaceController).
All in all my build is much the same, but I learned how to gain a lot of flexibility in testing, configuring the server instance my GWT application connects with, and which model my application uses (dependent on hosted vs server mode).

Related

Keycloak: Add custom extension io.undertow.servlet.ServletExtension

I wanted to add a custom servlet extension to Keycloak which would install a http handler that gets invoked on every request sent to Keycloak and sets up some logging MDC context that our custom SPI code can use for logging the incoming request traces correctly.
Following the docs here I created a custom extension class:
public class UndertowHandlerExtension implements ServletExtension {
#Override
public void handleDeployment(DeploymentInfo deploymentInfo, ServletContext servletContext) {
deploymentInfo.addInnerHandlerChainWrapper(TraceIdCapturingHandler::new);
}
}
And have defined my custom http handler TraceIdCapturingHandler in the same JAR file. I also added a file to META-INF/services/io.undertow.servlet.ServletExtension and set the fully qualified reference to the extension class. I also updated my deployments jboss-deployment-structure.xml and added the following 2 entries as dependencies:
<module name="io.undertow.servlet" />
<module name="javax.servlet.api" />
However, when my deployment is created the extension is not being invoked and my filter is not executing. Is there something I am missing in terms of how to configure Wildfly for Keycloak so that my extension and handler are installed and used correctly?
EDIT:
After doing a bit of digging I realized I was headed down the wrong path. Looked at this repository and I think I need a custom RealResourceProvider as shown here which in turn can install my filter by obtaining an instance of ResteasyProviderFactory and invoking getContainerRequestFilterRegistry().registerSingleton().
Will try this out and report back.
Please see the edit above for my question. I was able to implement a RealmResourceProviderFactory instance that initialized the filters I needed on startup in the init() method:
#Override
public void init(Config.Scope scope) {
log.info("Initializing");
initializeKeycloakFilters();
}
private void initializeKeycloakFilters() {
ResteasyProviderFactory providerFactory = ResteasyProviderFactory.getInstance();
TraceIdCapturingFilter filter = new TraceIdCapturingFilter();
providerFactory.getContainerRequestFilterRegistry().registerSingleton(filter);
}

Adding a filter to find out remote IP address of requester using JdkHttpServerFactory

Is there any way to get the remote IP address while sticking to the JdkHttpServerFactory framework?
I have a very light RESTful server which uses JdkHttpServerFactory to create a server, essentially with one line of code:
JdkHttpServerFactory.createHttpServer(baseUri, config);
My dependencies are:
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
<version>2.23.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-jdk-http</artifactId>
<version>2.23.2</version>
</dependency>
It appears it is not trivial to add a filter to this framework. I have tried it by bringing in HttpServletRequest but this means I need to bring in the servlet framework, and this actually didn't work for me with JdkHttpServerFactory, I end up getting null as my request.
I found another suggestion, but that suggestion also meant switching over to using Grizly2, which agains means I have to abandon the simple/light JdkHttpServerFactory method.
The only way I can see to access the IP is through the HttpExchange. Unfortunately, this is not something that is exposed to us. You can either make a feature request to expose the IP or you can just modify it yourself and build your own artifact. In either case, the change I would suggest making is to just add a property in to the ContainerRequest, that you could pull out in your filter.
If you look here in the source code, you will see the ContainerRequest. I would just do something like
final ContainerRequest requestContext = new ContainerRequest(baseUri, requestUri,
exchange.getRequestMethod(), getSecurityContext(exchange.getPrincipal(), isSecure),
new MapPropertiesDelegate());
requestContext.setProperty(JdkServerProperties.REMOTE_IP_ADDR, exchange.getRemoteAddr());
Then in your ContainerRequestFilter, you can just do
#Override
public void filter(ContainerRequestContext requestContext) {
// ContainerRequest implements ContainerRequestContext
InetSockAddress remoteAddr = (InetSocketAddress) requestContext.getProperty(JdkServerProperties.REMOTE_IP_ADDR);
}
Yeah, so if I were to make a feature/pull request or just build my own artifact, this is probably the change I would make. Or if you don't want to expose the InetSocketAddress, you can just build the IP string and add that as the property value.

Apache CXF different ClassLoader Issue

I'm building a SOAP WebService.
In this SOAP Webservice, I want to save the XML request to a file.
To do so, I know I can simply use a chain handler, however, I need other infos that aren't transmitted in the request itself, but rather calculated in the WebService, wich is why I feel like I should retreive the MessageContext in my WebService.
Hopefully, I can use #Resource to get the WebServiceContext and retreive the Message Context. However, I can't seem to be able to cast the MessageContext to the class I want. Here is the code of my endpoint :
import org.apache.cxf.jaxws.context.WrappedMessageContext;
[...]
public MyClass [...] {
[...]
#Resource
WebServiceContext wsContext;
public WSResponse lister(SecurityInfo security, MessageId messageID, RequestBody body) {
if(wsContext.getMessageContext() instanceof WrappedMessageContext) {
//The code never goes in this
}
//However this :
wsContext.getMessageContext().getClass().toString(); //equals this : "org.apache.cxf.jaxws.context.WrappedMessageContext"
//And this is where the problem is
wsContext.getMessageContext().getClass().getClassLoader().toString(); // is : ModuleClassLoader for Module "org.apache.cxf.impl:main" [...]
this.getClass().getClassLoader().toString(); // is : ModuleClassLoader for Module "deployment.my_ws_module.war:main" [...]
}
I didn't even know about CXF 5 days ago, and discovered classloaders yesterday, but I think I kinda understand why is this happening, however, when it comes to solving it, I'm quite clueless...
Worth noting :
The project uses Maven, Spring, CXF and Java 1.8

Send localized email in GWT [duplicate]

I have an interface that extends the com.google.gwt.i18n.client.Messages class, which I use for retrieving i18n messages in my GWT application. It looks like this:
public interface MyMessages extends com.google.gwt.i18n.client.Messages {
#DefaultMessage("Hello world")
#Key("message1")
String message1();
#DefaultMessage("Hello again")
#Key("message2")
String message2();
//...
}
Normally, I create an instance of it using GWT.create() like so:
private MyMessages messages = GWT.create(MyMessages.class);
However, this does not work with server-side code, only client-side code (it throws an error saying that GWT.create() is only usable in client-side code).
The answer to a similar question points to a separate library that you can download which will let you access the i18n messages on the server, but I don't want to download any extra libraries (this seems like a simple problem, there must be a simple solution).
In summary: How can I access my i18n messages in server-side code? Thanks.
On the server side you can use the standard Java localization tools like ResourceBundle.
Look here for a tutorial how to use it.
// Create a ResourceBundle out of your property files
ResourceBundle labels =
ResourceBundle.getBundle("LabelsBundle", currentLocale);
// Get localized value
String value = labels.getString(key);
The GWT specific way of creating an interface out of your property files and providing implementations via deferred binding can not be used on sever side Java.
If you are fearless and willing to spend the time, you can implement a code generation step to read your property files and generate implementation classes for your message interface. That's exactly what the Google GWT compiler does behind the scene.
I agree with Michael.. I was having this problem of trying to "localize" messages generated on the server.... but I decided to instead just throw an Exception on the server (because it is an error message which should only happen exceptionally) which contains the message code, which the client code can then look up and show the correct localized message to the user.
There's a great library for GWT internationalization gwt-dmesg. It allows you to 'share' .properties files between clent and server. However, project looks to be abandoned by author and you must recompile it manually for use with GWT versio >= 2.1.0.
GWT.create() can only be used in client-side code.
The good thing to do is that you provide your own I18NProvider class/interface, from which then you can extend to server side I18N factory and client side I18N factory read the same resource bundle.
After that you can simply use it all over your system, unify your code.
Hope that helps.
Following vanje's answer, and considering the encoding used for the properties files (which can be troublesome as ResourceBundle uses by default "ISO-8859-1", here is the solution I came up with:
import java.io.UnsupportedEncodingException;
import java.util.Locale;
import java.util.ResourceBundle;
public class MyResourceBundle {
// feature variables
private ResourceBundle bundle;
private String fileEncoding;
public MyResourceBundle(Locale locale, String fileEncoding){
this.bundle = ResourceBundle.getBundle("com.app.Bundle", locale);
this.fileEncoding = fileEncoding;
}
public MyResourceBundle(Locale locale){
this(locale, "UTF-8");
}
public String getString(String key){
String value = bundle.getString(key);
try {
return new String(value.getBytes("ISO-8859-1"), fileEncoding);
} catch (UnsupportedEncodingException e) {
return value;
}
}
}
The way to use this would be very similar than the regular ResourceBundle usage:
private MyResourceBundle labels = new MyResourceBundle("es", "UTF-8");
String label = labels.getString(key)
Or you can use the alternate constructor which uses UTF-8 by default:
private MyResourceBundle labels = new MyResourceBundle("es");

GWT rpc failing - base url not what I expected

I am trying to become familiar with using the GWT api to create web based applications. I have been following some tutorials on GWT and have not yet been able to make an RPC call. Looking at the problem with a broad scope, my goals are to make a server call to run a series of database tests that I know work (ive tested this code).
---EDIT---
I think that the problem here is that the resource is being looked for here:
/MatesWeb/org.matesweb.Main/peopleService
when I think it should be looked for here:
/MatesWeb/peopleService
---END_EDIT---
Here is the info and code I feel is relevant:
-using netbeans
-error that I am getting is "/MatesWeb/org.matesweb.Main/PeopleService - description - The requested resource is not available."
-GWT.getModuleBaseURL() returns: :8080/MatesWeb/org.matesweb.Main/
-URL in browser is: :8080/MatesWeb/
from web.xml file
<servlet>
<servlet-name>peopleService</servlet-name>
<servlet-class>org.matesweb.server.PeopleServiceImpl</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>peopleService</servlet-name>
<url-pattern>/peopleService</url-pattern>
</servlet-mapping>
From PeopleService Service
package org.matesweb.client;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
#RemoteServiceRelativePath("PeopleService")
public interface PeopleService extends RemoteService {
String[] saveGetPerson(String[] persInfo);
int runTests();
}
From PeopleServiceImpl
package org.matesweb.server;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import org.matesweb.client.PeopleService;
import org.matesweb.server.tests.DbTest;
class PeopleServiceImpl extends RemoteServiceServlet implements PeopleService {
#Override
public String[] saveGetPerson(String[] persInfo) {
throw new UnsupportedOperationException("Not supported yet.");
}
#Override
public int runTests()
{
int retInt;
DbTest dbTest = new DbTest();
retInt = dbTest.runTests();
return retInt;
}
}
From PeopleServiceAsync
package org.matesweb.client;
import com.google.gwt.user.client.rpc.AsyncCallback;
public interface PeopleServiceAsync
{
void saveGetPerson(String[] persInfo, AsyncCallback<String[]> persInformation);
void runTests(AsyncCallback<Integer> retInt);
}
Any idea of whats going on here?
Cheers,
Nick
#RemoteServiceRelativePath("PeopleService")
The #RemoteServiceRelativePath annotation is used to decide what url to hit. This path to the server is relative to the compiled module itself - the gwt app loaded from the path /MatesWeb/org.matesweb.Main/, so the service is being sought out at /MatesWeb/org.matesweb.Main/PeopleService. I assume this means you have an html file in the MatesWeb/ directory (probably the .war file is called MatesWeb?), and inside of there exists the compiled app in org.matesweb.Main/, including the initial JS file, org.matesweb.Main.nocache.js.
If you want to tell the service to be found at /MatesWeb/peopleService, you have two options. The first is to modify the annotation to back up a directory, something like this:
#RemoteServiceRelativePath("../peopleService")
Using .., I indicate the parent directory, and I also changed the case of the path part 'peopleService' - this may or may not matter. A second option is to set the url programmatically:
PeopleServiceAsync service = GWT.create(PeopleService.class);
((ServiceDefTarget)service).setServiceEntryPoint("/MatesWeb/peopleService");
As referenced in the #RemoteServiceRelativePath javadocs http://google-web-toolkit.googlecode.com/svn/javadoc/latest/com/google/gwt/user/client/rpc/RemoteServiceRelativePath.html.
If, instead, you want to leave the client as is and tell the server that this service should be at the path the client expects, you can modify the web.xml to make the servlet available at the path that the client is currently expecting to find it:
<servlet-mapping>
<servlet-name>peopleService</servlet-name>
<url-pattern>/MatesWeb/org.matesweb.Main/PeopleService</url-pattern>
</servlet-mapping>
Note again that I've changed the case - it may not matter, but I generally like to be consistent.
First hunch is PeopleService must be peopleService in the #RemoteServiceRelativePath . Please use firebug to monitor your rpc requests. You can observe and verify request url issues like these easily.
Update your URL pattern in web.xml as in here
<url-pattern>/org.matesweb.Main/greet</url-pattern>