// 
//     Licensed to the Apache Software Foundation (ASF) under one
//     or more contributor license agreements.  See the NOTICE file
//     distributed with this work for additional information
//     regarding copyright ownership.  The ASF licenses this file
//     to you under the Apache License, Version 2.0 (the
//     "License"); you may not use this file except in compliance
//     with the License.  You may obtain a copy of the License at
// 
//       http://www.apache.org/licenses/LICENSE-2.0
// 
//     Unless required by applicable law or agreed to in writing,
//     software distributed under the License is distributed on an
//     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
//     KIND, either express or implied.  See the License for the
//     specific language governing permissions and limitations
//     under the License.
//

= 在 NetBeans IDE 中使用 CDI 将 OSGi 包作为服务注入
:jbake-type: tutorial
:jbake-tags: tutorials 
:jbake-status: published
:icons: font
:syntax: true
:source-highlighter: pygments
:toc: left
:toc-title:
:description: 在 NetBeans IDE 中使用 CDI 将 OSGi 包作为服务注入 - Apache NetBeans
:keywords: Apache NetBeans, Tutorials, 在 NetBeans IDE 中使用 CDI 将 OSGi 包作为服务注入

本文档介绍了 NetBeans IDE 中对link:http://www.osgi.org/Main/HomePage[+开放服务网关协议+] (OSGi) 的集成支持如何简化创建 OSGi 包并在项目中使用该包的流程。在本教程中，将首先通过 Maven OSGi 包原型创建两个简单的 OSGi 包，然后将这些包部署到 GlassFish Server Open Source Edition 3.1 中。

创建基本的 OSGi 包后，将创建 Web 客户端应用程序，并使用 CDI 将这些包作为服务注入。然后，将 Web 应用程序作为包部署到服务器。本教程接着将介绍如何使用 OSGi 管理控制台处理 OSGi 包。

在企业应用程序中使用 OSGi 包可以为更新各个包提供更大的模块性和灵活性。GlassFish Server 中对 OSGi 的便捷支持可以轻松将包合并到应用程序中。

本教程基于 link:http://blogs.oracle.com/arungupta/entry/totd_154_dynamic_osgi_services[+TOTD #154：GlassFish 3.1 中的动态 OSGi 服务 - 使用 CDI 和 @OSGiService+] 博客帖子和其他博客条目（可在 link:http://blog.arungupta.me/[+Arun Gupta 的博客+]上找到）。请确保访问此博客，并查看许多其他有关使用 OSGi 的优秀博客。

此外，以下资源中包含各种有关在混合应用程序中使用 OSGi 和 CDI 的信息。

* link:http://weblogs.java.net/blog/2009/06/14/developing-hybrid-osgi-java-ee-applications-glassfish[+在 GlassFish 中开发混合应用程序 (OSGi + Java EE)+]
* link:http://blogs.oracle.com/sivakumart/entry/typesafe_injection_of_dynamic_osgi[+在混合 Java EE 应用程序中以类型安全的方式注入动态 OSGi 服务+]
* link:http://weblogs.java.net/blog/2009/06/04/osgi-enabled-web-applications-inglassfish[+GlassFish 中启用 OSGi 的 Web 应用程序+]
* link:http://www.youtube.com/watch?v=vaOpJJ-Xm70[+ YouTube 视频：通过 GlassFish 3.1 和 CDI 以类型安全的方式注入 OSGi 动态服务 +]
* link:http://glassfish.java.net/public/GF-OSGi-Features.pdf[+ 使用 GlassFish Server 进行 OSGi 应用程序开发+] [PDF]

*教程练习*

* <<Exercise_1,创建父 POM 项目>>
* <<Exercise_2,创建 OSGi 包项目>>
* <<Exercise_2a,创建 MavenHelloServiceApi 接口包>>
* <<Exercise_2b,创建 MavenHelloServiceImpl 实现包>>
* <<Exercise_2c,构建和部署 OSGi 包>>
* <<Exercise_3,创建 Web 客户端应用程序>>
* <<Exercise_3a,在 POM 项目中配置依赖关系>>
* <<Exercise_3b,创建 MavenHelloWebClient Web 应用程序>>
* <<Exercise_3c,以包的形式构建 Web 应用程序>>
* <<Exercise_3d,部署 Web 应用程序包>>
* <<Exercise_4,安装和使用 OSGi 管理控制台>>
* <<Exercise_5,参考和深入阅读>>

*要学习本教程，您需要具备以下软件和资源。*

|===
|软件或资源 |要求的版本 

|link:http://download.netbeans.org/netbeans/7.1/beta/[+NetBeans IDE+] |7.2、7.3、7.4、8.0、Java EE 版本 

|link:http://www.oracle.com/technetwork/java/javase/downloads/index.html[+Java 开发工具包 (JDK)+] |版本 7 或 8 

|GlassFish Server Open Source Edition |3.1.x 或 4.x 
|===

*先决条件*

本文档假定您具备以下技术的一些基本知识或编程经验：

* Java 编程
* NetBeans IDE
* Maven 框架

在开始本教程之前，您可以先阅读下面这些文档。

* link:http://wiki.netbeans.org/MavenBestPractices[+NetBeans IDE 中的 Apache Maven 最佳做法+]
* link:http://books.sonatype.com/mvnref-book/reference/introduction.html[+Chapter 1. Introducing Apache Maven+]（第 1 章. Apache Maven 简介，来自 link:http://books.sonatype.com/mvnref-book/reference/index.html[+Maven: The Complete Reference+]（Maven：完整参考））
* link:http://www.osgi.org/javadoc/r4v42/[+OSGi 框架+]

 


== 创建父 POM 项目

在本节中，将为您要在本教程中创建的 OSGi 包创建父 POM 项目。此外，还将编辑项目 POM ( ``pom.xml`` )，以添加要作为依赖关系被子项目继承的依赖关系管理元素。

1. 从主菜单中选择 "New Project"（新建项目）（Ctrl-Shift-N 组合键；在 Mac 中为 ⌘-Shift-N 组合键）。
2. 从 "Maven" 类别中选择 "POM Project"（POM 项目）。

image::images/cdi-newpomproject.png[title="新建项目向导中 Maven POM 项目原型"]



. 键入 *MavenOSGiCDIProject* 作为项目名称。单击 "Finish"（完成）。

单击 "Finish"（完成），此时 IDE 将创建 POM 项目并在 "Projects"（项目）窗口中打开该项目。



. 在 "Projects"（项目）窗口中展开 "Project Files"（项目文件）节点，然后双击  ``pom.xml``  在编辑器中打开该文件。

项目的基本 POM 应类似于以下内容。


[source,xml]
----

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mycompany</groupId>
    <artifactId>MavenOSGiCDIProject</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
</project>
        
----


. 修改父  ``pom.xml``  以添加下列元素。保存所做的更改。

[source,xml]
----

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mycompany</groupId>
    <artifactId>MavenOSGiCDIProject</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    *<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.osgi</groupId>
                <artifactId>org.osgi.core</artifactId>
                <version>4.2.0</version>
                <scope>provided</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>*
</project>
        
----

在本练习中，您显式指定了要在项目中使用的工件和工件版本。通过使用依赖关系管理并在父 POM 中指定工件，可以使子项目中的 POM 更为简单，并确保依赖关系版本在项目中的一致性。

有关使用依赖关系管理的详细信息，请参见link:http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html[+依赖关系机制简介+]。


== 创建 OSGi 包项目

新建项目向导中的 "Maven" 类别包含用于创建 OSGi 包项目的“OSGi 包”原型。创建 OSGi 包项目时，构建的 POM 声明  ``org.osgi.core``  JAR 作为附属项，并指定  ``maven-bundle-plugin``  用于构建项目。


=== 创建 MavenHelloServiceApi 接口包

在本练习中，将使用新建项目向导创建一个 OSGi 包项目，该项目将提供一个由其他包实现的简单接口。创建了包和接口后，将修改 POM 以更新对您在父 POM 项目中指定的  ``org.osgi.core``  工件的依赖关系。

1. 选择 "File"（文件）> "New Project"（新建项目）以打开新建项目向导。
2. 从 "Maven" 类别中选择 "OSGi Bundle"（OSGi 包）。单击 "Next"（下一步）。

image::images/cdi-new-osgiproject.png[title="新建项目向导中的 Maven OSGi 包原型"]



. 键入 *MavenHelloServiceApi* 作为项目名称。


. 单击 "Browse"（浏览），并选择 *MavenOSGiCDIProject* POM 项目作为位置。单击 "Finish"（完成）。

单击 "Finish"（完成），此时 IDE 将创建包项目并在 "Projects"（项目）窗口中打开该项目。如果在编辑器中打开 MavenHelloServiceApi 项目的  ``pom.xml`` ，则可以看到  ``packaging``  元素指定了  ``bundle``  并且构建包时将使用  ``maven-bundle-plugin`` 。


[source,xml]
----

<project>
    <modelVersion>4.0.0</modelVersion>
    <parent>
    <artifactId>MavenOSGiCDIProject</artifactId>
    <groupId>com.mycompany</groupId>
    <version>1.0-SNAPSHOT</version>
    </parent>

    <groupId>com.mycompany</groupId>
    <artifactId>MavenHelloServiceApi</artifactId>
    <version>1.0-SNAPSHOT</version>
    *<packaging>bundle</packaging>*
    <name>MavenHelloServiceApi OSGi Bundle</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>org.osgi.core</artifactId>
            <version>4.3.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.felix</groupId>
                *<artifactId>maven-bundle-plugin</artifactId>*
                <version>2.3.7</version>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <Bundle-Activator>com.mycompany.mavenhelloserviceimpl.Activator</Bundle-Activator>
                        <Export-Package />
                    </instructions>
                </configuration>
            </plugin>

            ...
        </plugins>
    </build>

    ...
<project>
----

此外，您还可以看到，使用 Maven OSGi 包原型创建 OSGi 包项目时，IDE 在默认情况下将  ``org.osgi.core``  工件添加为依赖关系。



. 在 "Projects"（项目）窗口中右键单击 "MavenHelloServiceApi" 项目节点，然后选择 "Properties"（属性）。


. 在 "Project Properties"（项目属性）对话框中选择 "Sources"（源）类别。


. 将 *Source/Binary Format*（源代码/二进制格式）设置为 "1.6"，并确认 *Encoding*（编码）是 "UTF-8"。单击 "OK"（确定）。


. 在 "Projects"（项目）窗口中右键单击 "Source Packages"（源包）节点，然后选择 "New"（新建）> "Java Interface"（Java 接口）。


. 键入 *Hello* 作为类名。


. 在 "Package"（包）中选择 *com.mycompany.mavenhelloserviceapi*。单击 "Finish"（完成）。


. 将以下  ``sayHello``  方法添加到接口中（以粗体显示），然后保存所做的更改。

[source,java]
----

public interface Hello {
    *String sayHello(String name);*
}
----


. 在 "Projects"（项目）窗口中右键单击项目节点，并选择 "Build"（构建）。

构建项目后，如果打开 "Files"（文件）窗口，并展开项目节点，则可以看到在  ``target``  文件夹中创建了  ``MavenHelloServiceApi-1.0-SNAPSHOT.jar`` 。

image::images/cdi-manifest.png[title="在 "Files"（文件）窗口中查看已编译 JAR 的内容"]

构建项目时， ``maven-bundle-plugin``  将处理  ``MANIFEST.MF``  文件的构建。如果打开已编译的 JAR 中的  ``MANIFEST.MF``  文件，将看到此插件已生成一个清单标题，此标题声明了导出包。对于 OSGi，要公开并可供其他包使用的所有包都必须列在  ``MANIFEST.MF``  的  ``Export-Package``  元素中。



. 确认  ``MANIFEST.MF``  包含  ``Export-Package``  元素（以下示例中*粗体*显示的元素）。

[source,java]
----

Manifest-Version: 1.0
Bnd-LastModified: 1395049732676
Build-Jdk: 1.7.0_45
Built-By: nb
Bundle-Activator: com.mycompany.mavenhelloserviceapi.Activator
Bundle-ManifestVersion: 2
Bundle-Name: MavenHelloServiceApi OSGi Bundle
Bundle-SymbolicName: com.mycompany.MavenHelloServiceApi
Bundle-Version: 1.0.0.SNAPSHOT
Created-By: Apache Maven Bundle Plugin
*Export-Package: com.mycompany.mavenhelloserviceapi;uses:="org.osgi.frame
 work";version="1.0.0.SNAPSHOT"*
Import-Package: org.osgi.framework;version="[1.6,2)"
Tool: Bnd-1.50.0
----

OSGi 容器将读取  ``Export-Package``  清单标题以确定可以从包外部访问此包中的类。在此示例中， ``com.mycompany.mavenhelloserviceapi``  包中的类是公开的。

NOTE: 如果  ``MANIFEST.MF``  不包含  ``Export-Package``  元素，则您需要在 "Project Properties"（项目属性）窗口中启用插件的默认插件行为，并重新构建项目。在 "Project Properties"（项目属性）窗口中，选择 "Export Packages"（导出包）类别，然后选择 *Default maven-bundle-plugin behavior*（默认 maven-bundle-plugin 行为）选项。可以使用 "Project Properties"（项目属性）窗口的 "Export Packages"（导出包）面板显式指定应公开的包，或直接在  ``pom.xml``  中指定包。

   


=== 创建 MavenHelloServiceImpl 实现包

在本练习中，您将在 POM 项目中创建 MavenHelloServiceImpl。

1. 选择 "File"（文件）> "New Project"（新建项目）以打开新建项目向导。
2. 从 "Maven" 类别中选择 "OSGi Bundle"（OSGi 包）。单击 "Next"（下一步）。
3. 键入 *MavenHelloServiceImpl* 作为项目名称。
4. 单击 "Browse"（浏览），并选择 *MavenOSGiCDIProject* POM 项目作为位置（如果未选择）。单击 "Finish"（完成）。
5. 右键单击 "Projects"（项目）窗口中的项目节点，然后选择 "Properties"（属性）。
6. 在 "Project Properties"（项目属性）对话框中选择 "Sources"（源）类别。
7. 将 *Source/Binary Format*（源代码/二进制格式）设置为 "1.6"，并确认 *Encoding*（编码）是 "UTF-8"。单击 "OK"（确定）。
8. 在 "Projects"（项目）窗口中右键单击 "Source Packages"（源包）节点，然后选择 "New"（新建）> "Java Class"（Java 类）。
9. 键入 *HelloImpl* 作为类名。
10. 选择 *com.mycompany.mavenhelloserviceimpl* 作为包。单击 "Finish"（完成）。
11. 键入以下内容（以粗体显示），并保存所做的更改。

[source,java]
----

public class HelloImpl *implements Hello {
    
    public String sayHello(String name) {
        return "Hello " + name;*
    }
}
----

实现  ``Hello``  时，IDE 将显示一个错误，您需要将 MavenHelloServiceApi 项目添加为依赖关系才能解决该错误。



. 在 "Projects"（项目）窗口中右键单击 *MavenHelloServiceImpl* 的 "Dependencies"（依赖关系）节点，然后选择 "Add Dependency"（添加依赖关系）。


. 在 "Add Library"（添加库）对话框中，单击 "Open Projects"（打开的项目）标签。


. 选择 "MavenHelloServiceApi OSGi Bundle"（MavenHelloServiceApi OSGi 包）。单击 "ADD"（添加）。

image::images/cdi-add-dependency.png[title="在 "Add Library"（添加库）对话框中打开 "Projects"（项目）标签"]



. 右键单击在编辑器中打开的  ``HelloImpl.java``  类，然后选择 "Fix Imports"（修复导入）（Alt-Shift-I 组合键；在 Mac 上为 ⌘-Shift-I 组合键），以添加  ``com.mycompany.mavenhelloserviceapi.Hello``  的 import 语句。保存所做的更改。


. 展开  ``com.mycompany.mavenhelloserviceimpl``  包并双击  ``Activator.java`` ，即可在编辑器中打开该文件。

image::images/cdi-activator.png[title=""Projects"（项目）窗口中的激活器类"]

IDE 在项目中自动创建了  ``Activator.java``  包激活器类。包激活器用于管理包的生命周期。包激活器类在包的  ``MANIFEST.MF``  中声明，并在容器启动该包时实例化。

OSGi 包不需要包激活器类，但可以使用激活器类中的  ``start()``  方法，例如，用于初始化包所需的服务或其他资源。在此练习中将向类中添加一些代码行，将消息打印到 "Output"（输出）窗口。这将更便于确定包启动和停止的时间。



. 修改包激活器类中的  ``start()``  和  ``stop()``  方法，以添加下列行（以粗体显示）。

[source,java]
----

public class Activator implements BundleActivator {

    public void start(BundleContext context) throws Exception {
        *System.out.println("HelloActivator::start");
        context.registerService(Hello.class.getName(), new HelloImpl(), null);
        System.out.println("HelloActivator::registration of Hello service successful");*
    }

    public void stop(BundleContext context) throws Exception {
        *context.ungetService(context.getServiceReference(Hello.class.getName()));
        System.out.println("HelloActivator stopped");*
    }
}
----

可以看到包激活器类导入了  ``org.osgi.framework.BundleActivator``  和  ``org.osgi.framework.BundleContext`` 。默认情况下，生成的类包含两个方法： ``start()``  和  ``stop()`` 。OSGi 框架调用  ``start()``  和  ``stop()``  方法来启动和停止包提供的功能。启动包时，包提供的服务组件会在 OSGi 服务注册表中进行注册。注册包之后，其他包可以使用注册表进行查找，然后通过包上下文使用活动服务。

如果查看项目的 POM，则可以在  ``maven-bundle-plugin``  的配置元素下方看到用于指定包激活器的  ``<Bundle-Activator>``  元素。


[source,xml]
----

<plugin>
    <groupId>org.apache.felix</groupId>
    <artifactId>maven-bundle-plugin</artifactId>
    <version>2.3.7</version>
    <extensions>true</extensions>
      <configuration>
            <instructions>
                  *<Bundle-Activator>com.mycompany.mavenhelloserviceimpl.Activator</Bundle-Activator>*
            </instructions>
      </configuration>
</plugin>
----

在构建包时，该插件将在 JAR 的包清单文件中构建清单标题，并指定包激活器类。部署包时，OSGi 运行时在清单文件中查找  ``Bundle-Activator``  标题。



. 在  ``Activator.java``  中修复 import 语句，以导入  ``com.mycompany.mavenhelloserviceapi.Hello`` 。保存所做的更改。


. 展开 "Dependencies"（依赖关系）节点，然后确认  ``org.osgi.core``  工件作为依赖关系列出。

NOTE: 通过右键单击该工件并选择 "Remove Dependency"（移除依赖关系），移除 "Dependencies"（依赖关系）节点下列出的该工件的所有早期版本。唯一的依赖关系应是 MavenHelloServiceApi 项目和  ``org.osgi.core``  工件。

image::images/cdi-implproject.png[title=""Projects"（项目）窗口中的激活器类"]
   


=== 构建和部署 OSGi 包

在本练习中，将构建 OSGi 包，并将这些包部署到 GlassFish。

1. 在 "Projects"（项目）窗口中，右键单击 "MavenOSGiCDIProject" 节点，然后选择 "Clean and Build"（清理并构建）。

构建项目时，IDE 将在每个项目的  ``target``  文件夹中创建 JAR 文件，还将在本地资源库中安装快照 JAR。在 "Files"（文件）窗口中，可以展开两个包项目中任一个的  ``target``  文件夹，以查看两个 JAR 档案（ ``MavenHelloServiceApi-1.0-SNAPSHOT.jar``  和  ``MavenHelloServiceImpl-1.0-SNAPSHOT.jar`` ）。



. 启动 GlassFish Server（如果尚未启动）。

此时 "Output"（输出）窗口的 GlassFish Server 日志中应显示类似于以下内容的输出。


[source,java]
----

INFO: Started bundle: file:/glassfish-4.0/glassfish/domains/domain1/autodeploy/bundles/MavenHelloServiceApi-1.0-SNAPSHOT.jar

----

在 "Services"（服务）窗口中右键单击 "GlassFish Server" 节点，然后选择 "View Domain Server Log"（查看域服务器日志）（如果 "Output"（输出）窗口中不显示服务器日志）。



. 重复执行相关步骤，将  ``MavenHelloServiceImpl-1.0-SNAPSHOT.jar``  复制到  ``autodeploy/bundles``  目录。

现在，GlassFish Server 日志中应显示类似于以下内容的输出。


[source,java]
----

INFO: HelloActivator::start
INFO: HelloActivator::registration of Hello service successful
INFO: Started bundle: file:/glassfish-4.0/glassfish/domains/domain1/autodeploy/bundles/MavenHelloServiceImpl-1.0-SNAPSHOT.jar
INFO: Started bundle: file:/glassfish-4.0/glassfish/domains/domain1/autodeploy/bundles/MavenHelloServiceImpl-1.0-SNAPSHOT.jar
        
----

或者，也可以从 GlassFish OSGi 管理控制台安装包。有关详细信息，请参见<<Exercise_4,安装和使用 OSGi 管理控制台>>部分。


== 创建 Web 客户端应用程序

此部分演示如何创建访问 OSGi 包提供的服务的 Java EE Web 客户端。将在 Web 应用程序中创建一个简单 Servlet，然后注入声明的服务。创建项目前，将向父 POM 项目中添加一些依赖关系管理元素。


=== 在父 POM 项目中配置依赖关系

在本练习中，将指定父 POM 项目中的依赖关系元素。此外，还将添加项目要使用的工件的资源库。



. 在 "Projects"（项目）窗口中展开 *MavenOSGiCDIProject* 项目的 "Project Files"（项目文件）节点，然后双击  ``pom.xml``  在编辑器中打开该文件。


. 修改父  ``pom.xml``  以添加下列依赖关系管理元素（以粗体显示）。保存所做的更改。

[source,xml]
----

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mycompany</groupId>
    <artifactId>MavenOSGiCDIProject</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    ...    
            
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.osgi</groupId>
                <artifactId>org.osgi.core</artifactId>
                <version>4.3.0</version>
                <scope>provided</scope>
            </dependency>
            *<dependency>
                <groupId>org.osgi</groupId>
                <artifactId>org.osgi.compendium</artifactId>
                <version>4.2.0</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.glassfish</groupId>
                <artifactId>osgi-cdi-api</artifactId>
                <version>3.1-b41</version>
                <type>jar</type>
                <scope>provided</scope>
            </dependency>*
          
        </dependencies>
    </dependencyManagement>

    ...
</project>

----


. 添加下列元素，将 GlassFish 资源库添加到 POM 中。保存所做的更改。

[source,xml]
----

<project>

    ...

    </dependencyManagement>

    *<repositories>
        <!-- glassfish nexus repo for glassfish dependencies -->
        <repository>
            <id>glassfish-repo-archive</id>
            <name>Nexus repository collection for Glassfish</name>
            <url>http://maven.glassfish.org/content/groups/glassfish</url>
            <snapshots>
                <updatePolicy>never</updatePolicy>
            </snapshots>
        </repository>
    </repositories>*
    <modules>
        <module>MavenHelloServiceApi</module>
        <module>MavenHelloServiceImpl</module>
    </modules>
</project>
            
----

将 GlassFish 资源库添加到 POM 后，如果在 "Services"（服务）窗口中查看 "Maven Repositories"（Maven 资源库）节点下的资源库列表，将会看到 IDE 为 GlassFish 资源库自动添加了一个节点。默认情况下，IDE 会为本地 Maven 资源库显示一个节点。在打开的项目指定资源库时，IDE 将在 "Maven Repositories"（Maven 资源库）节点下为该资源库自动添加一个节点。

image::images/cdi-maven-repositories.png[title=""Maven Repositories"（Maven 资源库）窗口中的 GlassFish 资源库"]

在本练习中，您添加了其他要在项目中使用的工件和工件版本。此外，您还添加了包含  ``osgi-cdi-api``  工件的 GlassFish 资源库。


=== 创建 MavenHelloWebClient Web 应用程序

首先，将创建常规的 Web 应用程序，然后修改项目将其作为 OSGi 包（Web 应用程序包 (WAB)）。

1. 从主菜单中选择 "File"（文件）> "New Project"（新建项目）。
2. 从 "Maven" 类别中选择 "Web Application"（Web 应用程序）。单击 "Next"（下一步）。
3. 键入 *MavenHelloWebClient* 作为项目名称。
4. 单击 "Browse"（浏览），并选择 *MavenOSGiCDIProject* POM 项目作为位置（如果尚未选择）。单击 "Next"（下一步）。
5. 选择 "GlassFish Server" 作为服务器，并选择 "Java EE 6 Web" 或 "Java EE 7 Web" 作为 Java EE 版本。单击 "Finish"（完成）。
6. 右键单击项目节点，然后选择 "New"（新建）> "Servlet"。
7. 键入 *HelloServlet* 作为类名。
8. 选择  ``com.mycompany.mavenhellowebclient``  作为包。单击 "Finish"（完成）。
9. 删除 Servlet 中已由 IDE 生成的默认方法（ ``processRequest`` 、 ``doGet`` 、 ``doPost`` 、 ``getServletInfo`` ）。

NOTE: 需要展开编辑器折叠才能删除 HttpServlet 方法。



. 键入以下将注入服务的代码（以粗体显示）。

[source,java]
----

@WebServlet(name = "HelloServlet", urlPatterns = {"/HelloServlet"})
public class HelloServlet extends HttpServlet {

    *@Inject
    @OSGiService(dynamic=true)
    Hello hello;*
}
----


. 添加以下  ``doGet``  方法。

[source,java]
----

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        PrintWriter out = response.getWriter();
        out.println(hello.sayHello("Duke"));
    }
----


. 右键单击项目节点，然后选择 "New"（新建）> "Other"（其他）。


. 在 "Contexts and Dependency Injection"（上下文和依赖关系注入）类别中选择 *beans.xml*。单击 "Next"（下一步）。


. 使用默认文件名 ( ``beans`` )。单击 "Finish"（完成）。

单击 "Finish"（完成），此时该向导将在 Web 应用程序中创建  ``beans.xml``  文件。如果  ``beans.xml``  是该应用程序的一部分，则系统将自动启用 CDI。



. 修改  ``beans.xml``  文件以将  ``bean-discovery-mode``  的默认值更改为  ``all`` 。

[source,java]
----

bean-discovery-mode="*all*"
----

保存更改并关闭文件。

有关  ``bean-discovery-mode``  值之间的差异的详细信息，请参见以下页：

* Java EE 7 教程中的 link:http://docs.oracle.com/javaee/7/tutorial/doc/cdi-adv001.htm[+25.1：打包 CDI 应用程序+]
* link:http://stackoverflow.com/questions/18107858/cdi-inject-fails-on-maven-embedded-glassfish-plugin-org-jboss-weld-exceptions[+http://stackoverflow.com/questions/18107858/cdi-inject-fails-on-maven-embedded-glassfish-plugin-org-jboss-weld-exceptions+]


. 在 "Projects"（项目）窗口中右键单击 "MavenHelloWebClient" 的 "Dependencies"（依赖关系）节点，然后选择 "Add Dependency"（添加依赖关系）。


. 选择 *Provided* 作为作用域。


. 在 "Add Library"（添加库）对话框中单击 "Open Projects"（打开的项目）标签，然后选择 *MavenHelloServiceApi OSGi Bundle（MavenHelloServiceApi OSGi 包）*。单击 "ADD"（添加）。


. 再次右键单击 "Dependencies"（依赖关系）节点，然后选择 "Add Dependency"（添加依赖关系）。


. 在 "Add Library"（添加库）对话框中单击 "Dependency Management"（依赖关系管理）标签，然后选择在父 POM 项目中指定的  ``osgi-cdi-api``  工件。单击 "ADD"（添加）。

image::images/cdi-add-dependency3.png[title=""Add Library"（添加库）对话框中的 "Dependency Management"（依赖关系管理）标签"]



. 在编辑器中右键单击  ``HelloServlet.java`` ，然后选择 "Fix Imports"（修复导入）（Alt-Shift-I 组合键；在 Mac 上为 ⌘-Shift-I 组合键），以添加  ``com.mycompany.mavenhelloserviceapi.Hello`` 、 ``javax.inject.Inject``  和  ``org.glassfish.osgicdi.OSGiService`` 。保存所做的更改。

NOTE: 如果 IDE 未自动添加  ``com.mycompany.mavenhelloserviceapi.Hello``  的 import 语句，则可能需要手动进行添加。



. 右键单击 "MavenOSGiCDIProject"，然后选择 "Clean and Build"（清理并构建）。

构建项目时，"Output"（输出）窗口中应显示类似于以下内容的输出。


[source,java]
----

Reactor Summary:

MavenOSGiCDIProject ............................... SUCCESS [0.798s]
MavenHelloServiceApi OSGi Bundle .................. SUCCESS [7.580s]
MavenHelloServiceImpl OSGi Bundle ................. SUCCESS [1.142s]
MavenHelloWebClient ............................... SUCCESS [8.072s]
------------------------------------------------------------------------
BUILD SUCCESS
----

NOTE: 如果在构建 MavenOSGiCDIProject 项目时未自动构建 Web 应用程序，则您需要手动构建 Web 应用程序。

在 "Files"（文件）窗口中，展开 Web 应用程序的项目节点，并确认在目标目录中创建了档案  ``MavenHelloWebClient-1.0-SNAPSHOT.war`` 。如果展开 Web 客户端的 WAR 档案并检查  ``MANIFEST.MF`` ，则会看到清单中包含类似于以下内容的行。


[source,java]
----

Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: nb
Build-Jdk: 1.7.0_45
----


=== 以 OSGi 包的形式构建 Web 应用程序

要使用  ``@OSGiService``  并检索已注册的 OSGi 包，您需要将 Web 应用程序作为可访问  ``BundleContext``  的包。要使 WAR 成为 OSGi 包（Web 应用程序包），可将  ``Web-ContextPath``  元数据添加到 WAR 中的  ``MANIFEST.MF`` 。  要执行此操作，请在  ``maven-bundle-plugin``  的说明中指定  ``<Web-ContextPath>``  元素，该元素将包含在由插件生成的清单中。然后，修改  ``maven-war-plugin``  配置，以指示插件将  ``maven-bundle-plugin``  生成的清单添加到 WAR 档案中。

1. 在 "Projects"（项目）窗口中，展开 "MavenHelloWebClient" 下方的 "Project Files"（项目文件）节点，然后双击  ``pom.xml``  在编辑器中打开该文件。
2. 添加以下条目，将  ``maven-bundle-plugin``  添加到 POM 中。

[source,xml]
----

<build> 
    <plugins>
        *<plugin>
             <groupId>org.apache.felix</groupId>
             <artifactId>maven-bundle-plugin</artifactId>
             <version>2.2.0</version>
             <extensions>true</extensions>
             <configuration>
                 <supportedProjectTypes>
                     <supportedProjectType>ejb</supportedProjectType>
                     <supportedProjectType>war</supportedProjectType>
                     <supportedProjectType>bundle</supportedProjectType>
                     <supportedProjectType>jar</supportedProjectType>
                 </supportedProjectTypes>
                 <instructions>
                     <!-- Specify elements to add to MANIFEST.MF -->
                     <Web-ContextPath>/mavenhellowebclient</Web-ContextPath>
                     <!-- By default, nothing is exported -->
                     <Export-Package>!*.impl.*, *</Export-Package>
                 </instructions>
             </configuration>
             <executions>
                 <execution>
                     <id>bundle-manifest</id>
                     <phase>process-classes</phase>
                     <goals>
                         <goal>manifest</goal>
                     </goals>
                 </execution>
                 <execution>
                     <id>bundle-install</id>
                     <phase>install</phase>
                     <goals>
                         <goal>install</goal>
                     </goals>
                 </execution>
             </executions>
         </plugin>*
            
----


. 修改  ``maven-war-plugin``  的配置元素，将包信息添加到  ``MANIFEST.MF``  中。保存所做的更改。

[source,xml]
----

 <plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-war-plugin</artifactId>
     <version>2.3</version>
     <configuration>
         *<archive>
             <!-- add bundle plugin generated manifest to the war -->
             <manifestFile>
                 ${project.build.outputDirectory}/META-INF/MANIFEST.MF
             </manifestFile>
             <!-- For some reason, adding Bundle-ClassPath in maven-bundle-plugin
             confuses that plugin and it generates wrong Import-Package, etc.
             So, we generate it here.-->
             <manifestEntries>
                 <Bundle-ClassPath>WEB-INF/classes/</Bundle-ClassPath>
             </manifestEntries>
         </archive>*
         <failOnMissingWebXml>false</failOnMissingWebXml>
     </configuration>
 </plugin>
----


. 在 "Projects"（项目）窗口中，右键单击 "MavenHelloWebClient" 项目节点，然后选择 "Clean and Build"（清理并构建）。

如果您现在展开 WAR 档案，并在编辑器中打开  ``MANIFEST.MF`` ，则会看到  ``MANIFEST.MF``  现在包含其他信息，包括您在  ``maven-bundle-plugin``  配置和包名称条目中指定的  ``Web-ContextPath: /mavenhellowebclient``  条目。


[source,java]
----

Manifest-Version: 1.0
Export-Package: com.mycompany.mavenhellowebclient;uses:="com.mycompany
 .mavenhelloserviceapi,javax.servlet,org.glassfish.osgicdi,javax.injec
 t,javax.servlet.annotation,javax.servlet.http";version="1.0.0.SNAPSHO
 T"
Bundle-ClassPath: WEB-INF/classes/
Built-By: nb
Tool: Bnd-1.50.0
Bundle-Name: MavenHelloWebClient
Created-By: Apache Maven Bundle Plugin
*Web-ContextPath: /mavenhellowebclient*
Build-Jdk: 1.7.0_45
Bundle-Version: 1.0.0.SNAPSHOT
Bnd-LastModified: 1395053424008
Bundle-ManifestVersion: 2
Import-Package: com.mycompany.mavenhelloserviceapi;version="[1.0,2)",j
 avax.inject,javax.servlet,javax.servlet.annotation,javax.servlet.http
 ,org.glassfish.osgicdi;version="[1.0,2)"
Bundle-SymbolicName: com.mycompany.MavenHelloWebClient
Archiver-Version: Plexus Archiver
----

有关如何以 OSGi 包的形式构建 Web 应用程序的详细信息，请参见以下页。

* link:http://weblogs.java.net/blog/2009/06/04/osgi-enabled-web-applications-inglassfish[+http://weblogs.java.net/blog/2009/06/04/osgi-enabled-web-applications-inglassfish+]
* link:http://felix.apache.org/site/apache-felix-maven-bundle-plugin-bnd.html[+http://felix.apache.org/site/apache-felix-maven-bundle-plugin-bnd.html+]


=== 部署 Web 应用程序包

在本练习中，您需要将 Web 应用程序包复制到 GlassFish 安装中的  ``autodeploy/bundles``  文件夹。

1. 导航至包含  ``MavenHelloWebClient-1.0-SNAPSHOT.war``  的  ``target``  目录。
2. 将  ``MavenHelloWebClient-1.0-SNAPSHOT.war``  复制到 GlassFish 安装的  ``autodeploy/bundles``  文件夹中。

将 WAR 档案复制到目录时，GlassFish Server 日志中将显示类似于以下内容的输出。


[source,java]
----

INFO: Started bundle: file:/glassfish-3.1.1/glassfish/domains/domain1/autodeploy/bundles/MavenHelloWebClient-1.0-SNAPSHOT.war
...
INFO: ---- Injection requested for framework service type interface com.mycompany.mavenhelloserviceapi.Hello and annotated with dynamic=true, serviceCriteria=
INFO: WEB0671: Loading application [com.mycompany.MavenHelloWebClient_1.0.0.SNAPSHOT] at [/mavenhellowebclient]
INFO: Registered ServletContext as a service with properties: {osgi.web.symbolicname=com.mycompany.MavenHelloWebClient, osgi.web.version=1.0.0.SNAPSHOT, osgi.web.contextpath=/mavenhellowebclient} 
        
----

现在，您可以通过单击以下链接 link:http://localhost:8080/mavenhellowebclient/HelloServlet[+http://localhost:8080/mavenhellowebclient/HelloServlet+] 在浏览器中查看 Servlet。


== 安装和使用 OSGi 管理控制台

您可以使用 GlassFish OSGi 管理控制台来安装、启动和停止部署到服务器上的 OSGi 包。在本练习中，将启用 GlassFish OSGi 管理控制台，然后查看已注册的 OSGi 包列表。

执行以下步骤安装所需的 GlassFish 附件以启用 OSGi 控制台，并查看 GlassFish 域管理控制台中的已部署包。

1. 在浏览器中打开 GlassFish 域管理控制台。

在 "Services"（服务）窗口中，右键单击 "GlassFish Server" 节点，然后选择 "View Domain Admin Console"（查看域管理控制台）。



. 单击左侧导航栏中的 "Update Tool"（更新工具）。


. 从可用附件列表中选择  ``glassfish-osgi-gui`` 。

单击 "Install"（安装），然后接受许可证。

image::images/cdi-glassfish-addons.png[title="GlassFish 管理控制台的 "Update Tool"（更新工具）"]



. 重新启动 GlassFish Server。

*重要说明：*如果您运行的是 GlassFish Server 3.1.2.2，则需要修改位于  ``_GLASSFISH-INSTALL_/glassfish/config/``  目录中的  ``osgi.properties``  文件，并将  ``org.osgi.framework.startlevel.beginning``  属性的值设置为 "2" ( ``org.osgi.framework.startlevel.beginning=2`` )。
有关更多详细信息，请参见以下论坛帖子：
link:http://www.java.net/forum/topic/glassfish/glassfish/cannot-start-web-console-glassfish-version-3122[+ 无法在 Glassfish 3.1.2.2 版中启动 Web 控制台+]。



. 再次打开管理控制台，然后在左侧的导航栏中单击 *Server (Admin Server)*（服务器（管理服务器））。


. 单击 "OSGi Console"（OSGi 控制台）标签，以查看已部署的 OSGi 包列表。 

image::images/cdi-glassfish-console.png[title=""Add Library"（添加库）对话框中的 "Dependency Management"（依赖关系管理）标签"]

NOTE: 系统可能会提示您输入用户名和口令来查看 OSGi 包列表。如果您在 "OSGi Console"（OSGi 控制台）标签中看不到包列表，请确认未隐藏授权对话框。如果您在安装 IDE 时安装了 GlassFish 4 Server，则此服务器的默认用户名为  ``admin`` 。默认情况下口令为空。

您可以向下滚动列表，以查看已注册的 OSGi 包的状态，并启动和停止各个包。如果按 ID 对列表进行排序（从高到低），则会看到已部署的三个包显示在该列表的顶部附近。


link:/about/contact_form.html?to=3&subject=Feedback:%20Using%20CDI%20to%20Inject%20OSGi%20Bundles%20as%20Services[+发送有关此教程的反馈意见+]



== 另请参见

有关使用 NetBeans IDE 和 Maven 开发 OSGi 包的更多信息，请参见以下资源：

* link:http://wiki.netbeans.org/OSGiAndNetBeans[+wiki.netbeans.org 上的 OSGi 和 NetBeans+]
* link:http://wiki.netbeans.org/MavenBestPractices[+NetBeans IDE 中的 Apache Maven 最佳做法+]
* link:https://blogs.oracle.com/arungupta/entry/totd_125_creating_an_osgi[+TOTD #125：使用 NetBeans 创建一个 OSGi 包，并在 GlassFish 中部署+]
* link:../../trails/java-ee.html[+Java EE 和 Java Web 学习资源+]

要发送意见和建议、获得支持以及随时了解 NetBeans IDE Java EE 开发功能的最新开发情况，请link:../../../community/lists/top.html[+加入 nbj2ee 邮件列表+]。

