大和株式会社の狩野です。
前回に引き続いて、Sling Modelsにおける重要な概念であるAnnotationについて解説いたします。
今回の記事では、 @Self
, @ChildResource
, @RequestAttribute
, @OSGiService
について説明します。
前回の記事:「Sling Modelsに出てくるAnnotationって何!? Part.01」
Annotationという言葉の意味、概要
前回の記事 でも同じことを書きましたが、この記事から読んでいる場合もあるので、こちらでも書いておきます。
辞書的にいうと、「Annotation」という単語には「注釈」という意味があります。
転じて、プログラミング的には、変数やメソッドに注釈を付けて意味をもたせるという役割があります。
コードで説明すると、以下のようになります。
@Component( service = Servlet.class, property = { "sling.servlet.methods=post", "sling.servlet.paths=" + "/bin/any/path", } ) public class TutorialClass extends SlingAllMethodsServlet { // any code }
TutorialClass
に @Component
というAnnotationを付与することによって、このクラスはComponentとして扱われます。
丸かっこ内に書かれた記述によって、この TutorialClass
はServletであり、Postメソッドで /bin/any/path
というパスに対してリクエストを受けたときに動作しますという意味です。
更に、以下のようなこともできます。
@Model( adaptables = {SlingHttpServletRequest.class}, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL) public class TutorialClass2 { @ValueMapValue private String rootPath; }
2つ目の例では、 @ValueMapValue
Annotationにより、ダイアログに入力した値をこれだけの記述で取得することができます。
一旦 properties
などで取ってきて、そこから properties.get()
とかする必要すら無く、記述量も少なく済むので是非覚えましょう。
Sling Modelsで使われる各Annotationについての説明
@Self
org.apache.sling.models.annotations.injectorspecific.Self
@Self
Annotationは @SlingObject
や @ScriptVariable
などと同じく、付与したフィールドにオブジェクトを挿入するAnnotationです。
Selfという名前が示すように、当該Sling Models自身のadaptableであるか、 adaptTo()
メソッドで適合可能なオブジェクトを指定可能です。
例に挙げた@SlingObject
や @ScriptVariable
と異なり、あらかじめ規定されたクラスだけでなく、自作のJavaクラスも使用可能です。
Javaコード
import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.models.annotations.Model; import org.apache.sling.models.annotations.injectorspecific.SlingObject; @Model(adaptables = SlingHttpServletRequest.class) public class MyRequestAdapter { @SlingObject private SlingHttpServletRequest request; public String getResolver() { return request.getResourceResolver(); } }
import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.models.annotations.Model; import org.apache.sling.models.annotations.injectorspecific.Self; @Model(adaptables = SlingHttpServletRequest.class) public class TutorialClassForSelf { @Self private MyRequestAdapter requestAdapter; public String fetchResourceFromPath(String path) { return requestAdapter.getResolver().getResource(path); } }
上記のコードでは、自作の MyRequestAdapter
というクラスを他のSling Modelsで @Self
Annotationを用いて呼び出しています。
この例では、 MyRequestAdapter
クラスと TutorialClasForSelf
クラス共に adaptables = SlingHttpServletRequest.class
であり@Self
の利用が可能になっています。
また下記の例では、コアコンポーネントを継承したカスタムモデルにおいて@Self
と@Via
を利用して継承元のモデルを参照し、コアモデルのメソッドを利用しています。
... import com.adobe.cq.wcm.core.components.models.Title; import org.apache.sling.models.annotations.Via; import org.apache.sling.models.annotations.injectorspecific.Self; import org.apache.sling.models.annotations.via.ResourceSuperType; ... @Model(adaptables = SlingHttpServletRequest.class, adaptors = Title.class resourceType = MyTitle.RESOURCE_TYPE) public class MyTitle implements Title { protected static final String RESOURCE_TYPE = "yamato/components/mytitle"; @Self @Via(type = ResourceSuperType.class) private Title coreTitle; @Override public String getText() { return coreTitle.getText(); } .... }
どんな時に使うか
前述したように、当該Sling Models自身のadaptableであるか adaptTo()
メソッドで適合可能なオブジェクトを指定可能ですので、それら適合するクラスのメソッドやフィールドを利用したい場合に使います。
@ChildResource
org.apache.sling.models.annotations.injectorspecific.ChildResource
@ChildResource
Annotationは、自分自身の子リソースを取得するためのAnnotationです。
以下、コード及び実際のノード構造を用いた例をあげます。
Javaコード
import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.resource.Resource; import org.apache.sling.models.annotations.injectorspecific.ChildResource; import org.apache.sling.models.annotations.Model; @Model(adaptables = SlingHttpServletRequest.class) public class TutorialClassForChildResource { @ChildResource(name = "childresources") Resource items; }
ノード構造
componentnode +-- childresources +-- item0 +-- item1 +-- item2
結果
この時、変数 items
には
+-- childresources +-- item0 +-- item1 +-- item2
ノード情報を格納した Resource
クラスのオブジェクトが格納されます。
このitemsは配列やListではありませんが、 getChildren()
などで子のResourceを取得できます。
どんな時に使うか
ダイアログのmultifieldで保存したノード構造を扱う時に便利です。
@RequestAttribute
org.apache.sling.models.annotations.injectorspecific.RequestAttribute
@RequestAttribute
Annotationは、HTMLに記述した文字列をSling Modelsクラス内で扱うためのAnnotationです。以下、コードを用いた例を挙げます。
Javaコード
import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.resource.Resource; import org.apache.sling.models.annotations.injectorspecific.RequestAttribute; import org.apache.sling.models.annotations.Model; @Model(adaptables = SlingHttpServletRequest.class) public class TutorialClassForRequestAttribute { @RequestAttribute(name = "fromHTL1") private String param; @RequestAttribute private String fromHTL2; public String getConcatenatedValue() { return param + fromHTL2; } }
htmlコード
<sly data-sly-use.comp="${ 'path.to.TutorialClassForRequestAttribute' @ fromHTL1='Adobe', fromHTL2='ExperienceManager' }"></sly> <div>${comp.concatenatedValue}</div>
このように記述すると、divタグの中には AdobeExperienceManager
という文字列が描画されます。
こんな時に使う
Javaコード上でHTL上に出力された文字列を扱いたい時に使えます。 例えば、同じSling Modelsを複数のコンポーネントから呼び出した時に、どのコンポーネントから呼び出されたかをJava上で区別したい時などがあります。
@OSGiService
org.apache.sling.models.annotations.injectorspecific.OSGiService
@OSGiService
は、OSGiServiceを付与したフィールドに挿入するAnnotationです。以下にコードを用いた例を挙げます。
以下のコードは、ImageコンポーネントやTitleコンポーネントで呼ばれる可能性があるSling Modelsで、異なるコンポーネントから「リンク」というプロパティを取得することができるコードです。
この例では、 @OSGiService
Annotationは、Titleコンポーネントのモデルを取得するための ModelFactory
サービスを取得するために使います。
Javaコード
import org.apache.commons.lang3.StringUtils; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.resource.Resource; import org.apache.sling.models.annotations.injectorspecific.RequestAttribute; import org.apache.sling.models.annotations.injectorspecific.OSGiService; import org.apache.sling.models.annotations.Model; import org.apache.sling.models.factory.ModelFactory; import com.adobe.cq.export.json.ExporterConstants; import com.adobe.cq.wcm.core.components.models.Image; import com.adobe.cq.wcm.core.components.models.Title; @Model( adaptables = SlingHttpServletRequest.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL ) @Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION) public class TutorialClassForOSGiService { @OSGiService private ModelFactory modelFactory; @RequestAttribute private String componentName; @Self private SlingHttpServletRequest request; public String getLink() { Title title = modelFactory.getModelFromWrappedRequest(request, request.getResource(), Title.class); if (StringUtils.equals(componentName, "title")) { return title.getLink().getURL(); } Image image = modelFactory.getModelFromWrappedRequest(request, request.getResource(), Image.class); if (StringUtils.equals(componentName, "image")) { return image.getLink(); } } }
こんな時に使う
AEMに登録されている他のサービスを参照する時に使います。
今回の例では、 ModelFactory
というサービスを参照して呼び出しています。
今回の @OSGiService
Annotation の例ではSling Modelsからサービスを参照していますが、
Sling Modelsからではなく、サービスから他のサービスを参照する場合は @Reference
Annotationを使います。
最後に
以上となります。
これらのAnnotationの具体的な使い方を網羅的に書いてある記事があまり見つからなかったので、実務で用いたコードなどを参考に執筆いたしました。