Author: gtrasuk
Date: Fri Jul 3 20:41:36 2015
New Revision: 1689085
URL: http://svn.apache.org/r1689085
Log:
hello-webapp-client application and documentation are complete.
Added:
river/river-examples/river-examples/trunk/src/site/markdown/hello-webapp-client/
river/river-examples/river-examples/trunk/src/site/markdown/hello-webapp-client/hello-webapp-client.md
- copied, changed from r1657985,
river/river-examples/river-examples/trunk/src/site/markdown/hello-client/hello-client.md
river/river-examples/river-examples/trunk/src/site/resources/hello-webapp-client/
river/river-examples/river-examples/trunk/src/site/resources/hello-webapp-client/hello-webapp-client-homepage.png
(with props)
river/river-examples/river-examples/trunk/src/site/resources/hello-webapp-client/hello-webapp-client-response.png
(with props)
Modified:
river/river-examples/river-examples/trunk/pom.xml
river/river-examples/river-examples/trunk/src/site/markdown/index.md
Modified: river/river-examples/river-examples/trunk/pom.xml
URL:
http://svn.apache.org/viewvc/river/river-examples/river-examples/trunk/pom.xml?rev=1689085&r1=1689084&r2=1689085&view=diff
==============================================================================
--- river/river-examples/river-examples/trunk/pom.xml (original)
+++ river/river-examples/river-examples/trunk/pom.xml Fri Jul 3 20:41:36 2015
@@ -130,5 +130,6 @@
<module>hello-api</module>
<module>hello-service</module>
<module>hello-client</module>
+ <module>hello-webapp-client</module>
</modules>
</project>
\ No newline at end of file
Copied:
river/river-examples/river-examples/trunk/src/site/markdown/hello-webapp-client/hello-webapp-client.md
(from r1657985,
river/river-examples/river-examples/trunk/src/site/markdown/hello-client/hello-client.md)
URL:
http://svn.apache.org/viewvc/river/river-examples/river-examples/trunk/src/site/markdown/hello-webapp-client/hello-webapp-client.md?p2=river/river-examples/river-examples/trunk/src/site/markdown/hello-webapp-client/hello-webapp-client.md&p1=river/river-examples/river-examples/trunk/src/site/markdown/hello-client/hello-client.md&r1=1657985&r2=1689085&rev=1689085&view=diff
==============================================================================
---
river/river-examples/river-examples/trunk/src/site/markdown/hello-client/hello-client.md
(original)
+++
river/river-examples/river-examples/trunk/src/site/markdown/hello-webapp-client/hello-webapp-client.md
Fri Jul 3 20:41:36 2015
@@ -1,35 +1,235 @@
-# The Hello Client
+# The Hello WebApp Client
-Finally, we are ready to code a service consumer, also known as a client.
+Web Applications deployed to Apache Tomcat or TomEE can access Jini services.
+This might be the use case if we wanted to present a RESTful service that an
+external client like a mobile device could connect to over the open internet.
Or we
+might have a rich GUI implemented in a JavaScript framework.
-To summarize, so far, we've done the following:
+To recall the architecture we have:
-- Started a service browser so we can see what's on the network in our djinn.
-- Started up a service registrar.
-- Started up a "hello-world" service.
+- A service browser so we can see what's on the network in our djinn.
+- A service registrar.
+- A "hello-world" service.
-By now, the pattern should be coming clear - we run Jini components inside a
+The Jini components are running inside a
container that is setup by the "service starter" framework. This container
handles
the details of setting up the class loaders and the dynamic security policy
provider.
We configure this container with a "start-xyz.config" file. The component
itself
loads up its configuration from a "xyz.config" file.
-So let's finish it off by looking at the client. The code part is in the
module
-"hello-client" inside the examples project. The main code is in the class
-"org.apache.river.examples.hello.client.App":
-
- public class App {
-
- private static final String MODULE=App.class.getPackage().getName();
+In addition, let's assume that you have Apache TomEE installed and are able to
run
+it successfully (installing TomEE is outside the scope of this document -
+see [Apache TomEE](http://tomee.apache.org) for more information).
+
+## Running in a Web Application
+
+When we get to a webapp, things get slightly more complicated. One of the
things that
+is critical in a Jini application is the setup of the classloader and the
classloader
+hierarchy. In the web container, we have limited control over the
classloader, since
+the Java EE specifications include classloader configurations. Also, in order
to provide
+the environment, the container needs to use its own customized class loaders,
which in
+general won't support the required behaviour for Jini classes. In particular,
it is usually not
+possible (or at least not convenient) to setup the codebase annotation to
support mobile code.
+As a result, it isn't possible to use a web container to host Jini services
(for the sake of
+completeness, there has been some work in this area, but it was long ago and
seems to have
+dropped off the web).
+
+It is, however, quite possible for a web application
+to act as a consumer of Jini services. The fundamental problem
+we face is that any objects instantiated in the servlet container will not
carry a usable
+codebase annotation. We can work around this issue by applying some minor
constraints to the
+interfaces used in a webapp. Basically, we need to limit parameters and types
that transit
+the network to those that are part of the
+core Java language, or any other types that are expected to be present in both
the service consumer and
+provider's classpath. This restriction is no more onerous than similar
restrictions on
+Enterprise Java Bean interfaces (in fact it's the same restriction - EJBs have
no concept of
+mobile code).
+
+Security is another consideration - since Jini works on a mobile-code
architecture, we need to be
+able to load and run code that is loaded remotely. Jini's infrastructure
imposes the requirement
+that we need to have a security manager running, in order to control this
mobile code.
+In addition, the proxy preparation process requires a customized security
policy provider.
+
+Java EE applications and application servers seldom bother to turn on the
security manager,
+since they don't run "untrusted" code that is downloaded from the network. As
such, it may be
+difficult or impossible to implement the correct security policies and policy
provider.
+
+Luckily, with Apache TomEE, the configuration is not that bad.
+
+* Add the appropriate security policies to conf/catalina.policy.
+* Edit conf/server.xml to start properly with a security manager
+* Start up TomEE with the security manager
+
+Let's look at these steps individually...
+
+### Add Security Polices to conf/catalina.policy
+
+In your TomEE installation directory, navigate to the 'conf' folder and open
the
+file 'catalina.policy'. Look for the "Web Application Permissions" section...
+
+ // ========== WEB APPLICATION PERMISSIONS
=====================================
+
+
+ // These permissions are granted by default to all web applications
+ // In addition, a web application will be given a read FilePermission
+ // and JndiPermission for all files and directories in its document root.
+ grant {
+ // Required for JNDI lookup of named JDBC DataSource's and
+ // javamail named MimePart DataSource used to send mail
+ permission java.util.PropertyPermission "java.home", "read";
+ permission java.util.PropertyPermission "java.naming.*", "read";
+ permission java.util.PropertyPermission "javax.sql.*", "read";
+
+ // OS Specific properties to allow read access
+ permission java.util.PropertyPermission "os.name", "read";
+ permission java.util.PropertyPermission "os.version", "read";
+ permission java.util.PropertyPermission "os.arch", "read";
+ permission java.util.PropertyPermission "file.separator", "read";
+ permission java.util.PropertyPermission "path.separator", "read";
+ permission java.util.PropertyPermission "line.separator", "read";
+ (cont'd...)
+
+This section outlines the permissions that are granted to all web applications.
+The format is in the familiar policy file format.
+
+Find the bottom of this section, and just under the part where it says
"Required for TomEE",
+add the lines below, so it looks like:
+
+ // Required for TomEE
+ permission java.util.PropertyPermission "tomee.skip-tomcat-log",
"read";
+
+ // Required to allow Jini clients
+ permission java.lang.RuntimePermission "createSecurityManager";
+ permission java.security.SecurityPermission "getDomainCombiner";
+ permission java.lang.RuntimePermission "getProtectionDomain";
+ permission java.security.SecurityPermission
"createAccessControlContext";
+ permission java.security.SecurityPermission "getPolicy";
+ permission com.sun.jini.discovery.internal.EndpointInternalsPermission
"get";
+ permission com.sun.jini.discovery.internal.EndpointInternalsPermission
"set";
+ permission net.jini.discovery.DiscoveryPermission "*";
+ permission java.net.SocketPermission "*",
"connect,accept,listen,resolve";
+ permission java.lang.RuntimePermission "modifyThreadGroup";
+ permission com.sun.jini.thread.ThreadPoolPermission
"getSystemThreadPool";
+ permission java.lang.RuntimePermission "getClassLoader";
+ permission java.lang.RuntimePermission "modifyThread";
+ permission java.lang.RuntimePermission "setContextClassLoader";
+ }
- public App(final String[] args, LifeCycle lc) {
- main(args);
- }
+These lines add the various permissions required to access simple Jini/JERI
services
+ to all web applications.
- public static synchronized void main(String[] args) {
+Note - we haven't done anything to enable dynamic permission grants in
Tomcat/TomEE.
+So if you try to use a service whose proxy needs any extra permissions, it
probably
+won't work.
+
+What are dynamic permission grants, you ask? When the JERI subsystem loads a
proxy, it grants
+no permissions to that proxy by default. As part of the proxy preparation
process,
+the proxy preparer grants additional permissions after validating the proxy.
So the
+actual permissions granted to the proxy are specified in the web app's
configuration file
+under the initialization for the BasicProxyPreparer. But in order for that to
work,
+we need to enable Jini's DynamicPolicyProvider. Service containers like the
Service Starter framework,
+River Container or Rio, enable this policy provider when they setup the
security manager,
+but TomEE just uses the default policy file provider. It's possible to
override it, but
+it's outside the scope of this basic tutorial.
+
+### Edit conf/server.xml to start properly with a security manager
+
+Tomcat has a slightly funny behavior when started up with a security manager -
it
+ignores the 'META-INF/context.xml' file that is normally packaged in the
'.war' file,
+and in fact rejects deployment of the application. This behavior reflects
Tomcat's
+assumptions about why you'd want to use a security manager. It assumes that
you've
+enabled J2EE security because your webapps contain untrusted code, and you
want to
+control what they can do. In that scenario, you would also want to control
the deployment
+of the webapps, so you probably wouldn't want to allow the webapps to include
a 'context.xml'
+file.
+
+In practice, it's very rare to deploy untrusted web applications, which is why
you
+have probably never run Tomcat, TomEE, WebSphere or any other application
server with
+J2EE security enabled. In our case, we trust the web application; the
security manager is
+there to control code that might be downloaded with a proxy.
+
+We can override the default behavior by editing Tomcat/TomEE's 'server.xml'
file.
+
+Open the 'conf/server.xml' file in your TomEE installation folder, and locate
the
+'Host' element with the name attribute equal to 'localhost'. Edit it so the
'deployXML' attribute
+is 'true', as below:
+
+ <Host name="localhost" appBase="webapps"
+ unpackWARs="true" autoDeploy="true" deployXML="true">
+
+Now TomEE will deploy the application successfully.
+
+### Start up TomEE with the security manager
+
+If you're starting up TomEE manually, you simply need to add the '-security'
option to
+'catalina.sh start'. If you're starting TomEE through your IDE, you need to
alter its
+configuration to start with the security manager.
+
+## The Client Web Application
+
+Assuming you've built the examples project with 'maven install', you should be
+able to find the '.war' file under 'river-examples/hello-webapp-client/target'.
+The file will be named something like 'hello-webapp-client-1.0-SNAPSHOT.war',
as per
+Maven's artifact naming convention.
+You can either rename this file and copy it to TomEE's 'webapps' folder, or
you may be
+able to run the 'hello-webapp-client' subproject directly under your IDE
(under NetBeans, simply
+right-click on the project and then select 'Run').
+
+This is a simple Java Server Faces application - the main page looks like:
+
+
+
+If you enter a name and then click 'Update', you should see the reply from the
+hello-world service, similar to the client we saw before.
+
+
+
+Lets look at how this application works...
+
+First, the actual JSF page (index.xhtml):
+
+ <?xml version='1.0' encoding='UTF-8' ?>
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+ <html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:f="http://java.sun.com/jsf/core">
+ <h:head>
+ <title>Hello WebApp Client</title>
+ </h:head>
+ <h:body>
+ <h:form>
+ Please enter your name:<h:inputText
value="#{helloBean.name}"/><br/>
+ Message is: #{helloBean.message}.
+ <h:commandButton value="Update"
action="#{helloBean.updateMessage()}"/>
+ </h:form>
+ </h:body>
+ </html>
+
+The form references a managed bean called 'helloBean'. This bean is defined
in the
+class 'org.apache.river.examples.hello.webapp.client.HelloBean':
+
+ /**
+ * A Managed Bean that uses a Jini Service.
+ */
+ @Named
+ @RequestScoped
+ public class HelloBean {
+
+ private static final Logger log =
Logger.getLogger(HelloBean.class.getName());
+ private static final String
MODULE=HelloBean.class.getPackage().getName();
+
+ String name;
+ String message="Default";
+
+ /* Getters and setters elided... */
+
+ @Inject
+ private Configuration config;
+
+ public void updateMessage() {
+ log.info("Running updateMessage()...");
+ log.info("Config is " + config);
try {
- // Get the config
- Configuration config = ConfigurationProvider.getInstance(args);
// From the config, get the ServiceDiscoveryManager
ServiceDiscoveryManager sdm=
(ServiceDiscoveryManager)
@@ -37,11 +237,6 @@ So let's finish it off by looking at the
// We'll also need a proxy preparer.
ProxyPreparer preparer=(ProxyPreparer) config.getEntry(MODULE,
"greeterPreparer", ProxyPreparer.class);
- // While the sdm is finding registrars, let's ask the user for
their
- // name.
- Scanner in = new Scanner(System.in);
- System.out.println("Please enter your name:");
- String name = in.nextLine();
// Query the sdm for Greeter services.
ServiceTemplate template=new ServiceTemplate(
null,
@@ -50,97 +245,101 @@ So let's finish it off by looking at the
);
ServiceItem[] serviceItems=sdm.lookup(template, 5, null);
if (serviceItems.length==0) {
- System.out.println("We didn't find any greeter services.");
- System.exit(0);
+ message="We didn't find any greeter services.";
+ return;
}
// Pick a service item
ServiceItem chosen=serviceItems[0];
// Prepare the proxy.
Greeter greeter=(Greeter)
preparer.prepareProxy(chosen.service);
// Make the call
- String message=greeter.sayHello(name);
- // Print the result
- System.out.println("Greeter replied '" + message + "'.");
- } catch (Exception ex) {
+ message=greeter.sayHello(name);
+ } catch (Throwable ex) {
+ message=ex.getMessage();
ex.printStackTrace();
- } finally {
- System.exit(0);
- }
+ }
}
}
-The only thing that looks a little funny is that we call the main(...) method
-from the constructor. This quirk is because of the way the ServiceStarter
works, by
-instantiating the object.
-
-Apart from that, we can see the usual pattern of getting a Configuration
object based
-on the command-line parameters, and pulling entries out of the configuration.
In this case,
-the program shows the basic pattern for using a service:
-
-1 - Get a ServiceDiscoveryManager from the configuration. Inside the
configuration, the
-ServiceDiscoveryManager is configured to use a LookupDiscoveryManager to find
all the service
-registrars that appear on the network.
-2 - Use the ServiceDiscoveryManager to look up the service itself. Note that
we lookup the
-service by its interface, as defined in the API module.
-3 - Select a proxy (in this case, we simply use the first one we find), and
prepare it
-using a ProxyPreparer.
-4 - Invoke methods on the proxy, which in turn communicates with the real
service.
-
-The configuration for the client is as follows:
-
- org.apache.river.examples.hello.client {
-
- discoveryGroup="example-group";
-
- groups = new String[] {discoveryGroup};
-
- exporter = new BasicJeriExporter(TcpServerEndpoint.getInstance(0),
- new BasicILFactory());
-
- serviceInvocationConstraints=InvocationConstraints.EMPTY;
-
- basicPreparer =
- new BasicProxyPreparer(false, new
BasicMethodConstraints(serviceInvocationConstraints),
- new Permission[] { new
RuntimePermission("accessClassInPackage.com.sun.proxy") } );
+This code is quite similar to the stand-alone client code. It retrieves the
+service discovery manager and proxy preparer from the Configuration, then
looks up the
+Greeter service, selects the first service item returned, prepares the proxy,
and calls
+'sayHello(...)' on the proxy.
+
+The only small mystery here - where does the Configuration come from? Note
the
+'@Inject' annotation on the configuration field. Also, if you look carefully,
you'll
+notice that there's a 'beans.xml' file inside the 'WEB-INF' folder in the web
application.
+The presence of this file triggers the Java EE 6 Contexts and Dependency
Injection functionality.
+
+The Configuration is supplied by the
'org.apache.river.examples.hello.webapp.client.ConfigHolder'
+class:
+
+ /**
+ * This class acts as a ServletContextListener to load the configuration
on
+ * startup of the application, and then acts as a CDI resource provider to
allow
+ * automatic injection of the Configuration to any other CDI components.
+ */
+ @WebListener
+ public class ConfigHolder implements ServletContextListener {
+
+ private static Logger log =
Logger.getLogger(ConfigHolder.class.getName());
+
+ private static Configuration config;
+
+ @Override
+ public void contextInitialized(ServletContextEvent sce) {
+ ServletContext sc = sce.getServletContext();
+ log.info("Got the servlet context in contextInitialized(...):" +
sc);
+ log.info("This is " + this);
+ try {
+ // Load the configuration
+ log.info("servletContext=" + sc);
+ Object configResourceUrl
+ = sc.getResource("/WEB-INF/client.config");
+ String configResource = configResourceUrl.toString();
+
+ log.info("Attempting to open config at "
+ + configResource);
+ config
+ = new ConfigurationFile(new String[]{configResource});
+ log.info("Got configuration.");
+ /* Get the ServiceDiscoveryManager. This call effectively
causes discovery to
+ begin at application startup.
+ */
+ config.getEntry(this.getClass().getPackage().getName(),
+ "sdm", ServiceDiscoveryManager.class);
+ } catch (Throwable t) {
+ log.log(Level.SEVERE, "Got a throwable on startup", t);
+ }
- static discoveryManager =
- new LookupDiscovery( groups, this);
+ }
- greeterPreparer = basicPreparer;
- registrarPreparer = basicPreparer;
+ @Override
+ public void contextDestroyed(ServletContextEvent sce) {
- static sdm = new ServiceDiscoveryManager(discoveryManager, null);
+ }
+ @Produces Configuration getConfig() {
+ return config;
+ }
}
-And the 'starter' configuration is:
-
- com.sun.jini.start {
-
- private static policy = "server.policy";
- private static classpath =
"lib${/}hello-api.jar:lib${/}hello-client.jar";
- private static config = "hello-client.config";
- private static codebase="";
-
- static serviceDescriptors = new ServiceDescriptor[] {
- new NonActivatableServiceDescriptor(
- codebase, policy, classpath,
- "org.apache.river.examples.hello.client.App",
- new String[] { config })
- };
-
- }//com.sun.jini.start
-
-Finally, run the client:
-
- java -Djava.security.manager -Djava.security.policy=server.policy
-Djava.rmi.server.useCodebaseOnly=false -jar lib/start.jar
start-hello-client.config
+This class includes the '@WebListener' annotation, which sets up the class
+to have its 'contextInitialized(...)' method called when the application
starts up. In
+this method, the Configuration is loaded from the file 'WEB-INF/client.config'
+using the ConfigurationFile class, and placed into a static field.
+
+The getter method 'getConfig()' is marked with the '@Produces' annotation which
+sets it up as the source for any 'Configuration' objects. Recall that the
'HelloBean' class
+had its configuration field marked with the '@Inject' annotation. So when the
'HelloBean' gets
+initialized, TomEE's CDI subsystem creates an instance of ConfigHolder and
then calls
+the 'getConfig()' method to get the configuration, and then injects that
configuration into
+the 'HelloBean' instance.
+
+That's it! That's the core of a web application that accesses Jini/River
services.
+As an aside, TomEE implements the JAX-RS specification, so this same framework
can be
+used to create RESTful services that delegate to Jini services for the real
work.
-The client will ask you to enter your name on the command line, and then call
the
-service's 'sayHello(..)' method, and print out the results.
-
-Now, for extra points, copy the 'home' binary to another machine that has Java
-installed, and run the client. You should see that it still works, with no
real
-configuration other than the name of the discovery group.
Modified: river/river-examples/river-examples/trunk/src/site/markdown/index.md
URL:
http://svn.apache.org/viewvc/river/river-examples/river-examples/trunk/src/site/markdown/index.md?rev=1689085&r1=1689084&r2=1689085&view=diff
==============================================================================
--- river/river-examples/river-examples/trunk/src/site/markdown/index.md
(original)
+++ river/river-examples/river-examples/trunk/src/site/markdown/index.md Fri
Jul 3 20:41:36 2015
@@ -82,3 +82,9 @@ Application Programming Interface...
Finally, we are ready to code a service consumer, also known as a client.
[Read More...](hello-client/hello-client.html)
+
+## The Hello Web App Client
+
+Web applications running under Apache Tomcat or TomEE can access Jini services.
+
+[Read More...](hello-webapp-client/hello-webapp-client.html)
Added:
river/river-examples/river-examples/trunk/src/site/resources/hello-webapp-client/hello-webapp-client-homepage.png
URL:
http://svn.apache.org/viewvc/river/river-examples/river-examples/trunk/src/site/resources/hello-webapp-client/hello-webapp-client-homepage.png?rev=1689085&view=auto
==============================================================================
Binary file - no diff available.
Propchange:
river/river-examples/river-examples/trunk/src/site/resources/hello-webapp-client/hello-webapp-client-homepage.png
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added:
river/river-examples/river-examples/trunk/src/site/resources/hello-webapp-client/hello-webapp-client-response.png
URL:
http://svn.apache.org/viewvc/river/river-examples/river-examples/trunk/src/site/resources/hello-webapp-client/hello-webapp-client-response.png?rev=1689085&view=auto
==============================================================================
Binary file - no diff available.
Propchange:
river/river-examples/river-examples/trunk/src/site/resources/hello-webapp-client/hello-webapp-client-response.png
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream