読者です 読者をやめる 読者になる 読者になる

tohokuaikiのチラシの裏

技術的ネタとか。

Confluenceのプラグイン開発を承ります。ご連絡はこちらのホームページからお願いいたします。

Atlassian Licenseの実装周辺について

confluence

Atlassianマーケットプレイスで有料出品するにはライセンス認証が必須になる。
Paid via Atlassianならライセンスのパッケージもあるし、集金周りもやってくれる*1。Confluenceの場合でやってみた。

参考ドキュメント
Tutorial: Adding licensing support to your add-on (up to UPM version 2.0) - Atlassian Developers

モックを作る

SDKによりモックが作れます。

SDKのコマンド atlas-create-confluence-plugin-module を実行する。
Choose Plugin Module:
1:  Blueprint
2:  Component Import
3:  Component
4:  Keyboard Shortcut
5:  Downloadable Plugin Resource
6:  Gadget Plugin Module
7:  Licensing API Support
8:  Module Type
9:  REST Plugin Module
10: Servlet Context Listener
11: Servlet Context Parameter
12: Servlet Filter
13: Servlet
14: Template Context Item
15: Web Item
16: Web Panel
17: Web Panel Renderer
18: Web Resource
19: Web Resource Transformer
20: Web Section
Choose a number (1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20): 7

とでるので、7を選択してEnter

あと、パッケージ名やライセンス確認のためのサーブレットのPathを聞かれるので適当に答えます。サーブレットとか、実際には不要なのですがそこから得られるサンプルコードが有用なので作ります。

Enter Package Name jp.example.confluence.plugins.servlet: : jp.example.confluence.plugins.hogehoge.servlet
Enter License Servlet URL Path (not including /plugins/servlet/) jp.example.confluence.plugins.hogehoge/license: :
Include Example Code? (Y/y/N/n) N: : y
Enter Hello World Servlet URL Path (not including /plugins/servlet/) jp.example.confluence.plugins.hogehoge/licensehelloworld: :


ちなみに最初、こっちTutorial: Adding licensing support to your add-on - Atlassian Developersを見ていて作業したのだけど、これはPluginのバージョン1の頃のもので現在は使えない。

実行結果

追加されたファイル
下記の3つのファイルが追加されます。

  • src/main/java/jp/example.plugin.name/servlet/LicenseServlet.java
  • src/main/java/jp/example.plugin.name/servlet/LicenseHelloWorldServlet.java
  • src/main/resources/license-admin.vm

pom.xmlの変更

/project/dependencies

        <dependency>
            <groupId>com.atlassian.upm</groupId>
            <artifactId>plugin-license-storage-lib</artifactId>
            <version>${upm.license.compatibility.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.upm</groupId>
            <artifactId>plugin-license-storage-plugin</artifactId>
            <version>${upm.license.compatibility.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.upm</groupId>
            <artifactId>licensing-api</artifactId>
            <version>${upm.license.compatibility.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.upm</groupId>
            <artifactId>upm-api</artifactId>
            <version>${upm.license.compatibility.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.sal</groupId>
            <artifactId>sal-api</artifactId>
            <version>${sal.api.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.templaterenderer</groupId>
            <artifactId>atlassian-template-renderer-api</artifactId>
            <version>${atlassian.templaterenderer.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.4</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.osgi</groupId>
            <artifactId>spring-osgi-core</artifactId>
            <version>1.1.3</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.4</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.6.6</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.1.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-all</artifactId>
            <version>1.8.5</version>
            <scope>test</scope>
        </dependency>

/project/build/plugins/plugin[0]/configurationo

                    <instructions>
                        <Private-Package>com.atlassian.upm.license.storage.lib*</Private-Package>
                        <DynamicImport-Package>com.atlassian.upm.api.license.entity;version="2.0.1", com.atlassian.upm.api.license;version="2.0.1", com.atlassian.upm.api.util;version="2.0.1", com.atlassian.upm.license.storage.plugin;version="${upm.license.compatibility.version}"</DynamicImport-Package>
                    </instructions>
                    <bundledArtifacts>
                        <bundledArtifact>
                            <groupId>com.atlassian.upm</groupId>
                            <artifactId>plugin-license-storage-plugin</artifactId>
                            <version>${upm.license.compatibility.version}</version>
                        </bundledArtifact>
                    </bundledArtifacts>

/project/build/plugins

            <plugin>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-storage-plugin</id>
                        <phase>process-resources</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.outputDirectory}</outputDirectory>
                            <includeArtifactIds>plugin-license-storage-plugin</includeArtifactIds>
                            <stripVersion>true</stripVersion>
                        </configuration>
                    </execution>
                </executions>
            </plugin>


/project/properties

        <upm.license.compatibility.version>2.4.1</upm.license.compatibility.version>
        <sal.api.version>2.4.0</sal.api.version>
        <atlassian.templaterenderer.version>1.0.5</atlassian.templaterenderer.version>

このsal.api.version/atlassian.templaterender.versionについては、先にプロジェクトの作り方でdependencyを入れてしまってると出ない。この辺りからして、手でXML埋め込むよりcmd使った方が良い気はする。


ちなみに、先ほど参考にと挙げたURLでは

Increment your upm.license.compatibility.version property to the latest stable target version, such as 2.15.1.

とあるので、2.15.1にした方がいいのかな?・・・・と思ったのだけど、そうしたらエラーが出まくったのでおとなしく2.4.1のままにした。

atlassian-plugin.xmlの変更

ライセンス制であることの指定。plugin-infoに

<param name="atlassian-licensing-enabled">true</param>

を追加する。

servlet情報の公開

  <servlet name="License Servlet" i18n-name-key="license-servlet.name" key="license-servlet" class="jp.example.confluence.plugins.hogehoge.servlet.LicenseServlet">
    <description key="license-servlet.description">The License Servlet Plugin</description>
    <url-pattern>/jp.example.confluence.plugins.hogehoge/license</url-pattern>
  </servlet>
  <servlet name="License Hello World Servlet" i18n-name-key="license-hello-world-servlet.name" key="license-hello-world-servlet" class="jp.example.confluence.plugins.hogehoge.servlet.LicenseHelloWorldServlet">
    <description key="license-hello-world-servlet.description">The License Hello World Servlet Plugin</description>
    <url-pattern>/jp.example.confluence.plugins.hogehoge/licensehelloworld</url-pattern>
  </servlet>

componentのインポート
なんでこんなに・・・?あるいは既にあるじゃん・・・と思ったけど・・・

  <component-import key="pluginAccessor" interface="com.atlassian.plugin.PluginAccessor"/>
  <component-import key="pluginController" interface="com.atlassian.plugin.PluginController"/>
  <component-import key="templateRenderer" interface="com.atlassian.templaterenderer.TemplateRenderer"/>
  <component-import key="pluginSettingsFactory" interface="com.atlassian.sal.api.pluginsettings.PluginSettingsFactory"/>
  <component-import key="loginUriProvider" interface="com.atlassian.sal.api.auth.LoginUriProvider"/>
  <component-import key="userManager" interface="com.atlassian.sal.api.user.UserManager"/>
  <component-import key="i18nResolver" interface="com.atlassian.sal.api.message.I18nResolver"/>
  <component key="thirdPartyPluginLicenseStorageManager" class="com.atlassian.upm.license.storage.lib.ThirdPartyPluginLicenseStorageManagerImpl"/>
  <component key="pluginLicenseStoragePluginInstaller" class="com.atlassian.upm.license.storage.lib.PluginLicenseStoragePluginInstaller"/>
  <component key="atlassianMarketplaceUriFactory" class="com.atlassian.upm.license.storage.lib.AtlassianMarketplaceUriFactoryImpl"/>

この時点で、再起動

開発用のインスタンスを再起動します。atlas-debugかatlas-runで。

すると、なんやかんやエラー出たりしたのですが数回インスタンスの再起動を行ったらでなくなりました。

サーブレットを付ける。

ドキュメントには

<param name="configure.url">/plugins/servlet/com.example.plugins.tutorial.plugin-license-compatibility-tutorial/license</param>

を付けてやるってありますが、これはUPM*2から当該のプラグインの情報を見た際に「設定」というボタンが現れて、その先がこのサーブレットですという意味になる。

ただ、このサーブレットはどうも何も大したものがでないし、別にボタンも要らないので追加しなくてもいいと思う。

ただし、HelloWorldサーブレットの方は非常に参考になるので素でアクセスしてみるといい。

テスト用ライセンス

テストに使うライセンスはこちら。
Timebomb Licenses for Testing - Atlassian Developers

ライセンスの有無によってモジュールの有効・無効を切り分けるには

ドキュメント
Tutorial: Omit web-resources when your plugin is unlicensed - Atlassian Developers

ざっくり言うと

  • Component化したConditionEvaluatorというクラスを作成し、atlassian-plugin.xmlに追加
  • 上記Componentを使ったClassを使い、atlassian-plugin.xmlに<condition class="com.example.plugins.conditions.IsLicensedCondition" />という条件付にしてやる。

*1:手数料は25%

*2:Confluenceのプラグイン管理画面