Autodesk ForgeのJava APIについて

Autodesk Forgeの[ウェブサイト](https://forge.autodesk.com/)がURL含めて最近大幅に変わっていて、チュートリアルも更新されています。今までは、NodeJSとかC#とかのチュートリアルしかなかったのですが、GoとかPHPなんかも増えています。JavaのSDKも少し前に出ていたらしいけれど、Servletをサンプルがしっかり公開されています。素晴らしい。

最近、Play Frameworkを触っていることもあってServletは避けたいなということで、Javaのライブラリを使ってScalaでサンプルを書いてみました。

# forge-api-java-client
公式サイトは[コチラ](https://github.com/Autodesk-Forge/forge-api-java-client)です。基本的には、ForgeのREST APIのラッパーなのですが、同期的なAPIになっていたり、セッションの自動更新機能が実装されていたりで、結構使いやすい印象です（少なくともNodeJSよりは）。
ちなみに公式サイトのサンプルは、Servletのサンプルとは少し違った使い方で、単独アプリケーションであればこちらを参考にするのが正解と思います。

## scalaプロジェクトの準備
例のごとくScalaプロジェクトを立ち上げて、build.sbtに依存関係を記載します。SBTを使った場合、だと、公式サイトに載っている参照だけだと失敗するので注意が必要です。

```build.sbt
resolvers += Resolver.mavenLocal
// https://mvnrepository.com/artifact/com.autodesk/forge-java-sdk
libraryDependencies += "com.autodesk" % "forge-java-sdk" % "1.0.1"
libraryDependencies += "com.typesafe" % "config" % "1.3.2"
// https://mvnrepository.com/artifact/com.sun.jersey/jersey-core
libraryDependencies += "com.sun.jersey" % "jersey-core" % "1.9.1"
```

# Forge APIの使い方
Forgeには、DataManagement API、ModelDerivative API、DesignAutomation APIなど色々とありますが、とりあえずはDataManagement APIを試してみます。その中でもOAuthとOSSへのアクセスをやってみます。

使い方としては、まず、2-Legged、3-LeggedなどのOAuth認証を、Forgeアプリで作成したIDとSecretから生成し、それを使って各種APIを介して、Forgeの機能にアクセスします。

ここで使われるのが[BucketAPI](https://github.com/Autodesk-Forge/forge-api-java-client/blob/master/docs/BucketsApi.md)と[ObjectAPI](https://github.com/Autodesk-Forge/forge-api-java-client/blob/master/docs/ObjectsApi.md)です。簡単化のために、接続用のクラスを作成します。なお、設定ファイルのアクセスにはTypesafe.configを利用します。

```forge/Forge.scala
package forge

import java.io.File

import com.typesafe.config.ConfigFactory
import com.autodesk.client.ApiResponse
import com.autodesk.client.api.{BucketsApi, HubsApi, ObjectsApi}
import com.autodesk.client.auth.OAuth2TwoLegged
import com.autodesk.client.model._
import com.autodesk.client.model.PostBucketsPayload
import java.util

class Forge(scope:String) {
  val configuration = ConfigFactory.load()
  val clientId = configuration.getString("forge.CLIENT_ID")
  val secret = configuration.getString("forge.CLIENT_SECRET")
  val scopeInternal = configuration.getStringList("forge.scope.internal")
  val scopePublic = configuration.getStringList("forge.scope.public")

  // 最後の引数はトークンの自動更新
  val oauth2TwoLegged = new OAuth2TwoLegged(clientId, secret, if (scope == "internal") scopeInternal else scopePublic , true)
  val twoLeggedCredentials = oauth2TwoLegged.authenticate

  def createBucket(payload: PostBucketsPayload, xAdsRegion:String)(implicit  bucketsApi: BucketsApi) : ApiResponse[Bucket] = {
    bucketsApi.createBucket(payload, "", oauth2TwoLegged, twoLeggedCredentials)
  }

  def getBuckets (region:String, limit:Integer, startAt:String)(implicit  bucketsApi: BucketsApi) : ApiResponse[Buckets] = {
    bucketsApi.getBuckets(region, limit, startAt, oauth2TwoLegged, twoLeggedCredentials)
  }

  def getBucketDetail(bucketkey: String)(implicit bucketsApi: BucketsApi): ApiResponse[Bucket] = {
    bucketsApi.getBucketDetails(bucketkey, oauth2TwoLegged, twoLeggedCredentials)
  }

  def getObjects(bucketKey:String, limit:Int, beginsWith:String, startAt:String )(implicit objectsApi: ObjectsApi):ApiResponse[BucketObjects] = {
    objectsApi.getObjects(bucketKey, limit, beginsWith, startAt, oauth2TwoLegged, twoLeggedCredentials)
  }
  
  def uploadObject(bucketKey:String, objectName:String, contentLength:Int, body:File, contentDisposition:String, ifMatch:String)(implicit objectsApi:ObjectsApi): ApiResponse[ObjectDetails] = {
    objectsApi.uploadObject(bucketKey, objectName, contentLength, body, contentDisposition, ifMatch, oauth2TwoLegged, twoLeggedCredentials)
  }

}

```

設定ファイルは例えば、以下のように設定します。

```resources/application.conf
forge {
  CLIENT_ID = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  CLIENT_SECRET = "xxxxxxxxxxxxxxxxxx"
  scope {
    internal = ["bucket:create", "bucket:read","bucket:create", "data:read", "data:create", "data:write"]
    public = ["viewables:read"]
  }
}
```
## Bucketの生成・取得、Objectのアップロード・取得
上記のクラスを使って、それぞれの機能にアクセスしてみます。implicitを使っているので、少しはすっきりしているかと思います。

```forge/ForgeTest.scala
package forge

import com.autodesk.client.api.{BucketsApi, ObjectsApi}
import com.autodesk.client.model.PostBucketsPayload
import java.io.File

object ForgeTest {

  def main(args: Array[String]) {
    import scala.collection.JavaConversions._

    val forge = new Forge("internal")
    implicit val bucketsApi = new BucketsApi
    implicit val objectsApi = new ObjectsApi

    try {
      // Backetを作る。Key既存のものと被らないように。
      val payload = new PostBucketsPayload

      payload.setBucketKey("test_bucket_key_180903")
      payload.setPolicyKey(PostBucketsPayload.PolicyKeyEnum.TRANSIENT)
      val createBucketResponse= forge.createBucket(payload, "")
      println(createBucketResponse.getData.getBucketKey)

      // Fileのアップロード
      val file = new File("upload\\HelloWallIfc4.ifc")
      val name = file.getName
      val uploadObjectRes = forge.uploadObject(payload.getBucketKey, name, file.length().toInt, file, null, null)
      println(uploadObjectRes.getStatusCode)

      // Bucketをすべて取得する
      val getBucketsResponse = forge.getBuckets(null, null, null)
      getBucketsResponse.getData.getItems.foreach(bucket =>{
        val getBucketsResponse =forge.getBucketDetail(bucket.getBucketKey)
        println(getBucketsResponse.getData)
        val getObjectsResponse = forge.getObjects(getBucketsResponse .getData.getBucketKey, 10, null, null)
        getObjectsResponse.getData.getItems.foreach(obj => {
          println(obj)
        })
      })

    } catch {
      case e: Exception =>
        System.err.println("Exception when calling Forge APIs")
        e.printStackTrace()
    }
  }
}
```

上記を実行すると以下のような出力が得られます。

```
class Bucket {
    bucketKey: test_bucket_key_180903
    bucketOwner: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    createdDate: 1535847337066
    permissions: [class Permission {
        authId: hxDJCnkcqjba9bUkuAUs7nm6DjZIE8HY
        access: full
    }]
    policyKey: transient
}
class ObjectDetails {
    bucketKey: test_bucket_key_180903
    objectId: urn:adsk.objects:os.object:test_bucket_key_180903/HelloWallIfc4.ifc
    objectKey: HelloWallIfc4.ifc
    sha1: [B@5ef6ae06
    size: 6033
    contentType: null
    location: https://developer.api.autodesk.com/oss/v2/buckets/test_bucket_key_180903/objects/HelloWallIfc4.ifc
}

```

#おわりに
NodeJSの場合は、簡単ではありますが非常にメンテナンス性が悪い印象がありました。こちらのAPIはまだ今後アップデートがありそうですが、結構使いやすいので、こちらをメインに使おうかなと考えています。

## 参考サイト
- [Upload file to OSS](https://learnforge.autodesk.io/#/datamanagement/oss/java)
- [Forge Java SDK](https://github.com/Autodesk-Forge/forge-api-java-client)