Author: gtrasuk
Date: Thu Feb 5 22:37:58 2015
New Revision: 1657706
URL: http://svn.apache.org/r1657706
Log:
Hello example service is complete (but not the client).
Added:
river/river-examples/river-examples/trunk/src/site/resources/hello-service/
river/river-examples/river-examples/trunk/src/site/resources/hello-service/hello-service-cmd.png
(with props)
river/river-examples/river-examples/trunk/src/site/resources/hello-service/hello-svc-in-browser.png
(with props)
Modified:
river/river-examples/river-examples/trunk/pom.xml
river/river-examples/river-examples/trunk/src/site/markdown/hello-service/hello-service.md
river/river-examples/river-examples/trunk/src/site/markdown/index.md
river/river-examples/river-examples/trunk/src/site/markdown/infrastructure/infrastructure.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=1657706&r1=1657705&r2=1657706&view=diff
==============================================================================
--- river/river-examples/river-examples/trunk/pom.xml (original)
+++ river/river-examples/river-examples/trunk/pom.xml Thu Feb 5 22:37:58 2015
@@ -128,5 +128,6 @@
<module>browser</module>
<module>home</module>
<module>hello-api</module>
+ <module>hello-service</module>
</modules>
</project>
\ No newline at end of file
Modified:
river/river-examples/river-examples/trunk/src/site/markdown/hello-service/hello-service.md
URL:
http://svn.apache.org/viewvc/river/river-examples/river-examples/trunk/src/site/markdown/hello-service/hello-service.md?rev=1657706&r1=1657705&r2=1657706&view=diff
==============================================================================
---
river/river-examples/river-examples/trunk/src/site/markdown/hello-service/hello-service.md
(original)
+++
river/river-examples/river-examples/trunk/src/site/markdown/hello-service/hello-service.md
Thu Feb 5 22:37:58 2015
@@ -3,6 +3,8 @@ A Hello Service Example
OK, let's look at a service!
+##The API
+
Actually, before we create a service, we need to create the Application
Programming Interface (API) for a service. We do that in a separate Maven
module,
so that the api will be contained in its own artifact. Then later on, we can
@@ -14,3 +16,282 @@ not have any dependency on the service i
So, if you have a look at the 'hello-api' module of the examples project,
you'll see the
following interface defined:
+ public interface Greeter extends Remote {
+ /**
+ * Say 'Hello' to the client with the name provided.
+ * @param name The name of the client.
+ * @return a greeting message.
+ * @throws IOException
+ */
+ public String sayHello(String name) throws IOException;
+ }
+
+Notice a couple things about this interface:
+
+First, it extends 'java.rmi.Remote'.
+This is typical of interfaces that will be exported as remote services, like
this one.
+'Remote' is called a 'tag' interface, similar to 'java.io.Serializable'. It
doesn't specify
+any methods; the simple fact that an object implements the interface conveys a
message to someone.
+When we eventually instantiate a service object and pass it into the JERI
Exporter, the
+exporter creates a serializable proxy that implements all the interfaces that
extend 'Remote'.
+That proxy is what we'll eventually register with the lookup service.
+
+More importantly, the Remote interface tells a potential user of the interface
that the
+implementation is likely to be accessed remotely over a network connection.
As such, the
+consumer of a 'Remote' interface knows that she has to allow for network
latency,
+temporary failures, call-by-value semantics, and all the other things that go
with using a
+remote service ([A Note on Distributed
Computing](eecs.harvard.edu/~waldo/Readings/waldo-94.pdf)
+provides some interesting background reading).
+
+Second, the methods that the interface specifies throw 'java.io.IOException'.
Again, this
+is to reflect the fact the consumer of this interface should expect to deal
with
+communication-related exceptions, since the interface is likely implemented
using remote
+calls.
+
+## The Service Class
+
+OK, now that we have an API, we can implement it. This implementation is
contained in the
+module called 'hello-service', specifically in the
+ 'org.apache.river.examples.hello.service.GreeterService' class. Let's have a
look at that
+class:
+
+ public class GreeterService implements Greeter, ServiceIDListener {
+ private static final String
GREETER="org.apache.river.container.examples.greeter";
+
+ Configuration config=null;
+ Greeter myProxy=null;
+ Exporter exporter=null;
+ ProxyPreparer registrarPreparer=null;
+ JoinManager joinManager=null;
+ DiscoveryManagement discoveryManager=null;
+ Entry[] attributes=null;
+
+ public GreeterService(String[] args, final LifeCycle lc)
+ throws ConfigurationException, ExportException, IOException {
+ config = ConfigurationProvider.getInstance(args);
+
+ // Get the exporter and create our proxy.
+ exporter = (Exporter) Config.getNonNullEntry(config, GREETER,
"exporter", Exporter.class);
+ myProxy = (Greeter) exporter.export(this);
+
+ /*
+ The "discovery manager" looks after finding one or more service
+ registrars for the join manager to use.
+ */
+ discoveryManager
+ = (DiscoveryManagement) config.getEntry(GREETER,
"discoveryManager",
+ DiscoveryManagement.class);
+ attributes = (Entry[]) config.getEntry(GREETER, "attributes",
Entry[].class);
+
+ /*
+ Publish it using the join manager.
+ The "join manager" takes care of registering our service with
any/all
+ service registrars that appear in the workgroup.
+
+ We don't have to do anything with it - just creating it starts
the join process.
+ */
+ joinManager = new JoinManager(myProxy, attributes, this,
discoveryManager, null, config);
+ System.out.println("Hello service has been started successfully.");
+ }
+
+ public String sayHello(String name) throws IOException {
+ return "Hi there " + name;
+
+ }
+
+ ServiceID sid=null;
+
+ public void serviceIDNotify(ServiceID sid) {
+ this.sid=sid;
+ }
+ }
+
+This class is written to conform to the "Service-Starter" conventions:
primarily, its setup and
+startup is done in the constructor. When we configure the Service Starter to
start this service,
+it will create an instance of the service, supplying a set of arguments and a
"LifeCycle" object.
+
+The arguments are supplied in the configuration for the service starter; they
are essentially
+(and usually) the command line arguments that might have been supplied to the
service.
+
+We should probably note that this is kind of an odd way to start a service,
and potentially could
+induce concurrency issues under the modern Java Memory Model (tl;dr - don't
use final variables in
+your service). It's a bit of a historical relic from the days when a Jini
service would be a
+standalone Java process, and you'd normally have a `main(...)` method in the
service implementation.
+The River community has been discussing a new startup API for a while now.
+
+Anyhow, the constructor
+uses a `Configuration` object to retrieve entries from an editable file, much
the same
+as we've talked about for the Service Starter and the browser.
+
+The basic scenario of establishing a service is as follows:
+
+- Create an instance of the service. The service starter is doing this, which
+is why we're adding code inside the constructor.
+- Use an "exporter" to make the service instance available over a remote
communications
+mechanism. The exporter is defined in the configuration file; different
classes of
+exporters make the service available over different communications mechanisms.
In the
+configuration of this example service, we're using a "JERI" (Java Extensible
Remote
+Invocation) exporter on top of a plain TCP socket endpoint.
+- Establish a "discovery manager" to find all the service registrars that
exist in the
+workgroup, or might start up later. Again, this helper is defined in the
configuration file.
+- Establish a "join manager" that looks after the on-going task of registering
our proxy
+with whatever service registrars are found by the discovery manager. You
guessed it - it's
+defined in the configuration file.
+
+As you can see, most of the real work of setting up the application's object
graph
+is done in the service configuration file. The service's constructor
retrieves the
+configuration using the following code:
+
+ config = ConfigurationProvider.getInstance(args);
+
+The arguments do two things: first, they include the name of the configuration
file.
+Potentially, the arguments could also include overrides to the configuration
file's settings.
+In this case, we're using the arguments that were passed in to the service
class's
+constructor, which means that they are actually set in the service starter's
configuration file,
+"start-hello-service.config". If you look that up, you'll find that the
service's configuration
+file is "hello-service.config".
+
+Let's take a look at "hello-service.config":
+
+ org.apache.river.examples.hello.greeter {
+ discoveryGroup="example-group";
+
+ groups = new String[] {discoveryGroup};
+
+ exporter = new BasicJeriExporter(TcpServerEndpoint.getInstance(0),
+ new BasicILFactory());
+
+ serviceInvocationConstraints=InvocationConstraints.EMPTY;
+
+ registrarPermissions= new Permission[] {
+ new RuntimePermission("accessClassInPackage.com.sun.proxy"),
+ new ReflectPermission("newProxyInPackage.com.sun.jini.reggie")
+ };
+
+ registrarPreparer =
+ new BasicProxyPreparer(false, new
BasicMethodConstraints(serviceInvocationConstraints),
+ registrarPermissions );
+
+ static discoveryManager =
+ new LookupDiscovery( groups, this);
+
+ static attributes = new Entry[] { new Name("greeter-service") };
+
+ }
+
+ net.jini.lookup.JoinManager {
+ registrarPreparer =
org.apache.river.examples.hello.greeter.registrarPreparer;
+ registrationPreparer =
org.apache.river.examples.hello.greeter.registrarPreparer;
+
+ }
+
+We haven't shown the import statements here - like Java import statements,
they define
+the fully qualified class names for classes that we'd like to use by their
short names
+in the configuration.
+
+This file uses a syntax that is reminiscent of Java, but isn't Java. It's a
declarative syntax.
+The configuration file is not executed in any particular order - it's simply a
set of definitions
+that tell a "Configuration" instance how to create an object that a program
might ask for.
+These definitions look like variable initializations in the configuration
file. A declaration can also
+reference other declarations. So the line,
+
+ exporter = new BasicJeriExporter(TcpServerEndpoint.getInstance(0),
+ new BasicILFactory());
+
+tells the configuration instance that if someone asks for an entry called
"exporter", it should
+create an instance of "BasicJeriExporter" using the results of
"TcpServerEndpoint.getInstance(0)"
+and "new BasicILFactory()" as the constructor parameters. What this actually
does is create an exporter
+that creates a JERI proxy that makes a service available on a plain TCP socket
on a randomly-assigned port.
+
+"Hold on," you might be thinking, "if my service is on a randomly-assigned
port, how is the
+service consumer going to know what port to open? Don't I need to configure
that?"
+
+No, you don't need to configure that, unless you want to (maybe in the case of
firewall issues).
+In Jini/River, the service consumer is not going to make contact directly with
the service. What it's going to do
+is find a service registrar, and retrieve a serialized proxy object from the
registrar. The proxy knows how and where to
+contact the real service provider. Even if you choose a specific port, it's
still the
+provider's problem. The service consumer simply makes calls on the proxy that
was registered
+with the registrar, so the consumer doesn't need to be configured for the
port. The configuration is
+implicitly retrieved when the consumer gets the proxy from the registrar.
+
+One other thing that's worth pointing out in the configuration is the
following set of entries:
+
+ registrarPermissions= new Permission[] {
+ new RuntimePermission("accessClassInPackage.com.sun.proxy"),
+ new ReflectPermission("newProxyInPackage.com.sun.jini.reggie")
+ };
+
+ registrarPreparer =
+ new BasicProxyPreparer(false, new
BasicMethodConstraints(serviceInvocationConstraints),
+ registrarPermissions );
+
+Retrieving a proxy is sort of like inviting a guest into your house. You
might not want to
+give them free run of the place (you don't want the cable guy raiding your
fridge, for instance).
+In Jini, we have the concept of "Proxy Preparation". Anytime we get a proxy
from the registrar
+or anywhere else, we hand it to a "Proxy Preparer" object, that typically
gives it the once-over
+to see if it meets our requirements, and then allows it "into the house", by
granting permissions to it.
+So, in the configuration above, we are creating a proxy preparer that checks
the proxy against any
+constraints that we want to impose (in this case the constraints are empty,
but they could be
+things like "needs encryption", or "needs message integrity"), and then grants
the Java security
+permissions that the proxy will need to do it's job.
+
+##Starting the Service
+
+We're going to use the Service Starter again. Here's the configuration for it:
+
+ com.sun.jini.start {
+
+ private static policy = "server.policy";
+ private static classpath =
"lib${/}hello-api.jar:lib${/}hello-service.jar";
+ private static config = "hello-service.config";
+ port="8090";
+ private static codebasePrefix= "http://" + ConfigUtil.getHostAddress()
+ + ":" + port + "/";
+ private static codebase = codebasePrefix + "hello-api.jar";
+
+ static serviceDescriptors = new ServiceDescriptor[] {
+ new NonActivatableServiceDescriptor(
+ "",
+ policy,
+ "lib/tools.jar",
+ "com.sun.jini.tool.ClassServer",
+ new String[]{"-port", port, "-dir",
+ "lib-dl"}),
+ new NonActivatableServiceDescriptor(
+ codebase, policy, classpath,
+ "org.apache.river.examples.hello.service.GreeterService",
+ new String[] { config })
+ };
+
+ }//com.sun.jini.start
+
+Note that this configuration starts up the service provider as well as a
+"ClassServer". Remote Method Invocation requires that the bytecode for any
+marshalled objects is available for download. The class server makes that
available.
+
+In this case, as a practical matter, the service is exporting a
dynamically-created
+proxy, so strictly-speaking, no code download is required at the client side.
+However, we would like the service to appear in the service browser, and for
that to happen, the
+service browser will need to be able to download the "hello-api" jar file (the
client will
+generally have this jar file in its local classpath, so it won't need to
download it).
+As such, we are setting up a class server for the service.
+
+With all that said, we can start the service. First navigate to the directory
+that's built by the 'home' module:
+
+ cd ${river-examples-home}/home/target/home-${version}-bin
+
+and then execute the service-starter with the required configuration file:
+
+ java -Djava.security.manager -Djava.security.policy=server.policy -jar
lib/start.jar start-hello-service.config
+
+You should see the service start up:
+
+
+And, if you still have the browser running from earlier, your new service
should appear
+in the browser.
+
+
+
+Next, we'll need a [client](../hello-client/hello-client.html).
+
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=1657706&r1=1657705&r2=1657706&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 Thu
Feb 5 22:37:58 2015
@@ -68,3 +68,12 @@ Because services are run by the service
[Read More...](infrastructure/infrastructure.html)
+##A Hello Service Example
+
+OK, let's look at a service!
+
+Actually, before we create a service, we need to create the
+Application Programming Interface...
+
+[Read More...](hello-service/hello-service.html)
+
Modified:
river/river-examples/river-examples/trunk/src/site/markdown/infrastructure/infrastructure.md
URL:
http://svn.apache.org/viewvc/river/river-examples/river-examples/trunk/src/site/markdown/infrastructure/infrastructure.md?rev=1657706&r1=1657705&r2=1657706&view=diff
==============================================================================
---
river/river-examples/river-examples/trunk/src/site/markdown/infrastructure/infrastructure.md
(original)
+++
river/river-examples/river-examples/trunk/src/site/markdown/infrastructure/infrastructure.md
Thu Feb 5 22:37:58 2015
@@ -9,7 +9,7 @@ to be a service registrar in order for c
use a transaction manager. They may want to use messaging intermediaries, and
so on.
All these supporting functions are implemented by River's infrastructure
services.
-Because services are run by the service starter, we need run the service
starter with the
+Because services are run by the service starter, we need to run the service
starter with the
list of infrastructure services that we want to run.
In some cases, we only want one instance of an infrastructure service to
running in a
Added:
river/river-examples/river-examples/trunk/src/site/resources/hello-service/hello-service-cmd.png
URL:
http://svn.apache.org/viewvc/river/river-examples/river-examples/trunk/src/site/resources/hello-service/hello-service-cmd.png?rev=1657706&view=auto
==============================================================================
Binary file - no diff available.
Propchange:
river/river-examples/river-examples/trunk/src/site/resources/hello-service/hello-service-cmd.png
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
Added:
river/river-examples/river-examples/trunk/src/site/resources/hello-service/hello-svc-in-browser.png
URL:
http://svn.apache.org/viewvc/river/river-examples/river-examples/trunk/src/site/resources/hello-service/hello-svc-in-browser.png?rev=1657706&view=auto
==============================================================================
Binary file - no diff available.
Propchange:
river/river-examples/river-examples/trunk/src/site/resources/hello-service/hello-svc-in-browser.png
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream