HiveMind 序説 |
HiveMind は、サービスとコンフィギュレーションのマイクロカーネルです:
- サービス:HiveMind サービスは、アクセスと結合が容易なPOJO (Plain Old Java Object) です。各々のサービスは自身が実装するJavaインタフェースを定義します。HiveMind は、必要に応じて各々のサービスのインスタンス化と設定を取り仕切ります。HiveMind は、dependency injection (依存性注入)を通じて相互にサービスが協調しあうようにします。
- コンフィギュレーション:HiveMind により、貴方は、貴方のサービスに貴方が定義するフォーマットに従って複雑なコンフィギュレーション・データを提供する事が出来るようになります。HiveMind は、複数のモジュールからのそのようなデータのコントリビューションを統合し、全てをデータオブジェクトに変換してくれます。HiveMind のコンフィギュレーションは、サービス・アーキテクチャとシームレスに結合した、パワフルなデータ駆動型のソリューションを提供します。
- マイクロカーネル: HiveMind is a framework for creating applications, not an application, or even an application server, itself. The 'core' of HiveMind is the startup logic that knows how to parse and understand the module deployment descriptors, and use that information to instantiate and initialize all those services and configurations.
In HiveMind, a service is an implementation of a Java interface. Unlike other SOA (Service Oriented Architectures:サービス指向アーキテクチャ:SOAPやEJB), HiveMind is explicitly about combining Java code within a single JVM. HiveMind uses a descriptor to describe different services, their lifecycles, and how they are combined. HiveMind takes care of thread-safe, just-in-time creation of singleton service objects so your code doesn't have to.
HiveMind sits between your application code and the underlying J2EE or other APIs:
In this diagram, the application accesses key HiveMind services (the small circles). These services acts as facades; the implementations of the services are dependent on many other services, as well as configurations (the blue stacks of blocks). Service implementations make use of Java and J2EE APIs, and may even "bridge" into other systems such as EJB session beans.
HiveMind is organized around modules: individual building blocks, each providing a particular set of services and configurations. Each module is deployed as its own JAR file, containing its own descriptor. At runtime, HiveMind combines all the modules and their descriptors together ... seemlessly combining the services specific to your application with the services provided by HiveMind and by other third-party libraries.
HiveMind is designed with particular attention to J2EE. Because J2EE applications are multi-threaded, everything in HiveMind is thread-safe. That's one less thing for you to be concerned about.
HiveMind allows you to create more complex applications, yet keep the individual pieces (the individual services) simple and testable. HiveMind enourages the use of common best practices, such as coding to interfaces, seperation of concerns, and keeping code highly testable without a special container.
新着情報
HiveMind のビルドには Ant 1.6.2 が必要です
James Carman が新たに HiveMind のコミッタになりました
HiveMind 1.0 がリリースされました! 1.1 リリースに向けて全力を挙げています
謝辞
HiveMind represents a generous donation of code to the Apache Software Foundation by WebCT.
HiveMind originated from internal requirements for a flexible, loosely-coupled configuration management and services framework for WebCT's industry-leading flagship enterprise e-learning product, Vista.
Several individuals in WebCT's research and development team, in addition to Howard Lewis Ship, contributed to the requirements and concepts behind HiveMind's current set of functionality. These include Martin Bayly, Diane Bennett, Bill Bilic, Michael Kerr, Prashant Nayak, Bill Richard and Ajay Sharda. HiveMind is already in use as a significant part of Vista.
コーディング
Coding using HiveMind is designed to be as succinct and painless as possible. Since services are, ultimately, simple objects (POJOs -- plain old java objects) within the same JVM, all the complexity of J2EE falls away ... no more JNDI lookups, no more RemoteExceptions, no more home and remote interfaces. Of course, you can still use HiveMind to front your EJBs, in which case the service is responsible for performing the JNDI lookup and so forth (which in itself has a lot of value), before forwarding the request to the EJB.
In any case, the code should be short. To external objects (objects that are not managed by HiveMind, such as a servlet) the code for accessing a service is quite streamlined:
Registry registry = RegistryBuilder.constructDefaultRegistry(); MyService service = (MyService) registry.getService("com.mypackage.MyService", MyService.class); service.perform(...);
貴方のコードが受け持つのは:
- Obtaining a reference to the Registry singleton
- Knowing the complete id of the service to access
- Passing in the interface class
HiveMind が受け持つのは:
- Finding the service, creating it as needed
- Checking the type of the service against your code's expections
- Operating in a completely thread-safe manner
- Reporting any errors in a useful, verbose fashion
However, a much more common case is for services to collaborate: that's much simpler, since HiveMind will connect the two services together for you. You'll just need to provide an instance variable and either a setter method or a constructor argument.
ドキュメント
An important part of the HiveMind picture is documentation. Comprehensive documentation about a HiveMind application, HiveDoc, can be automatically generated by your build process. This documentation lists all modules, all extension points (both service and configuration), all contributions (of service constructors, service interceptors and configuration elements) and cross links all extension points to their contributions.
Modules and extension points include a description which is incorporated into the generated documentation.
HiveMind is used to construct very complex systems using a large number of small parts. HiveDoc is an important tool for developers to understand and debug the application.
HiveMind を使う理由
The concept behind HiveMind, and most other dependency-injection microkernels, is to reduce the amount of code in your application and at the same time, make your application more testable. If your applications are like my applications, there is an awful lot of code in place that deals just with creating objects and hooking them together, and reading and processing configuration files.
HiveMind moves virtually all of that logic into the framework, driven by the module deployment descriptors. Inside the descriptor, you describe your services, your configuration data, and how everything is hooked together within and between modules.
HiveMind can do all the grunt work for you; using HiveMind makes it so that the easiest approach is also the correct approach.
課題:ログ取得メソッドの出入
よくあるやり方
public String myMethod(String param) { if (LOG.isDebugEnabled()) LOG.debug("myMethod(" + param + ")"); String result = // . . . if (LOG.isDebugEnabled()) LOG.debug("myMethod() returns " + result); return result; }
This approach doesn't do a good or consistent job when a method has multiple return points. It also creates many more branch points within the code ... basically, a lot of clutter. Finally, it doesn't report on exceptions thrown from within the method.
HiveMind の場合
Let HiveMind add a logging interceptor to your service. It will consistently log method entry and exit, and log any exceptions thrown by the method.
The following descriptor snippet defines a service, provides a core service implementation (using <create-instance>), and adds method logging (using <interceptor>):
<service-point id="MyService" interface="com.myco.MyServiceInterface"> <create-instance class="com.myco.impl.MyServiceImpl"/> <interceptor service-id="hivemind.LoggingInterceptor"/> </service-point>
課題:他のサービスへの参照
よくあるやり方
private SomeOtherService _otherService; public String myMethod(String param) { if (_otherService == null) _otherService = // Lookup other service . . . _otherService.doSomething(. . .); . . . }
How the other service is looked up is specified to the environment; it might be a JNDI lookup for an EJB. For other microkernels, such as Avalon, there will be calls to a specific API.
In addition, this code is not thread-safe; multiple threads could execute it simultaneously, causing unwanted (and possibly destructive) multiple lookups of the other service.
HiveMind の場合
Let HiveMind assign the other service as a property. This is dependency injection. HiveMind can inject dependencies using JavaBeans properties or constructor arguments.
private SomeOtherService _otherService; public void setOtherService(SomeOtherService otherService) { _otherService = otherService; } public String myMethod(String param) { _otherService.doSomething(. . .); . . . }
HiveMind uses a system of proxies to defer creation of services until actually needed. The proxy object assigned to the otherService property will cause the actual service implementation to be instantiated and configured the first time a service method is invoked ... and all of this is done in a thread-safe manner.
課題:設定データの読み込み
よくあるやり方
Find a properties file or XML file (on the classpath? in the filesystem?) and read it, then write code to intepret the raw data and possibly convert it to Java objects.
The lack of a standard approach means that data-driven solutions are often more trouble than they are worth, leading to code bloat and a loss of flexibility.
Even when XML files are used for configuration, the code that reads the content is often inefficient, incompletely tested, and lacking the kind of error detection built into HiveMind.
HiveMind の場合
private List _configuration; public void setConfiguration(List configuration) { _configuration = configuration; } public void myMethod() { Iterator i = _configuration.iterator(); while (i.hasNext()) { MyConfigItem item = (MyConfigItem)i.next(); item.doStuff(. . .); } }
HiveMind will set the configuration property from a configuration point you specify. The objects in the list are constructed from configuration point contributions and converted, by HiveMind, into objects. As with services, a thread-safe, just-in-time conversion takes place.
The type and number of extension points and how and when your code makes use of them is entirely up to you, configured in the module deployment descriptor.
課題:サービスのテスト
よくあるやり方
In complex environments, such as EJB containers, you will often have to deploy your code and then test it from the outside. EJB code in particular is hard to test because collaborating EJBs make use of JNDI to access each other. It is very difficult to "stub out" part of the overall application for testing purposes.
HiveMind の場合
Making code testable is a key concern of HiveMind, and shows up in its general testing strategy:
- Because HiveMind services are simply POJOs, your unit tests can simply instantiate them directly.
- HiveMind services are always identified by interface, so it's easy to provide a mocked-up implementation of the interface.
- Services collaborate via dependency injection, so it's easy for a unit test to wire a service to real or mock implementations of collaborating services.
- Because configuration data is just lists of Java objects, unit tests can easily create objects suitable for testing.