GoogleAppEngine+Slim3のファイルアップロードをScalaで書き直す

【変更前】元ネタは、 http://slim3demo.appspot.com/upload/;jsessionid=dBGn20s5q7i28ZwKBGOrFw

package slim3.demo.service;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.slim3.controller.upload.FileItem;
import org.slim3.datastore.Datastore;
import org.slim3.util.ByteUtil;

import slim3.demo.meta.UploadedDataFragmentMeta;
import slim3.demo.meta.UploadedDataMeta;
import slim3.demo.model.UploadedData;
import slim3.demo.model.UploadedDataFragment;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.Transaction;

public class UploadService {
    
    //1つのデータの最大値は1MなのでFragmentのサイズを900000に設定
    private static final int FRAGMENT_SIZE = 900000;
    
    //DataとDataFragmentそれぞれのエンティティへのマッピングクラスを取得
    private UploadedDataMeta d = UploadedDataMeta.get();
    private UploadedDataFragmentMeta f = UploadedDataFragmentMeta.get();
    
    //アップロードしたDataの一覧を取得
    public List<UploadedData> getDataList() {
        return Datastore.query(d).asList();
    }
    
    //DataをDataFragmentに分割しながらアップロード
    public UploadedData upload(FileItem formFile) {
        //ファイルが空だったら、nullを返す
        if (formFile == null) {
            return null;
        }
        List<Object> models = new ArrayList<Object>();
        UploadedData data = new UploadedData();
        //datastoreに保存するデータの
        models.add(data);
        //各種Dataのパラメータ指定
        data.setKey(Datastore.allocateId(d));
        data.setFileName(formFile.getShortFileName());
        data.setLength(formFile.getData().length);
        //formFileをバイト化
        byte[] bytes = formFile.getData();
        //bytesをFragmentのサイズに分割
        byte[][] bytesArray = ByteUtil.split(bytes, FRAGMENT_SIZE);
        //byteArrayの長さ分のKeyを生成
        Iterator<Key> keys =
            Datastore
                .allocateIds(data.getKey(), f, bytesArray.length)
                .iterator();
        for (int i = 0; i < bytesArray.length; i++) {
            //FragmentData用のbyte[]
            byte[] fragmentData = bytesArray[i];
            //インスタンス生成しmodelsに格納
            UploadedDataFragment fragment = new UploadedDataFragment();
            models.add(fragment);
            //Fragmentに各種プロパティを設定
            fragment.setKey(keys.next());
            fragment.setBytes(fragmentData);
            fragment.setIndex(i);
            fragment.getUploadDataRef().setModel(data);
        }
        //トランザクション開始
        Transaction tx = Datastore.beginTransaction();
        //Data,Fragmentを含むモデルオブジェクトの一覧をdatastoreに追加していく
        for (Object model : models) {
            Datastore.put(tx, model);
        }
        //トランザクション終了
        tx.commit();
        //dataのオブジェクトを返す
        return data;
    }
    
    //Dataの情報を取得
    public UploadedData getData(Key key, Long version) {
        return Datastore.get(d, key, version);
    }
    
    //取得したDataの情報からFragmentをかき集めて修復
    public byte[] getBytes(UploadedData uploadedData) {
        //Dataが空だった場合にNullPointExceptionを投げる
        if (uploadedData == null) {
            throw new NullPointerException(
                "The uploadedData parameter must not be null.");
        }
        //DataからFragmentの一覧を取得
        List<UploadedDataFragment> fragmentList =
            uploadedData.getFragmentListRef().getModelList();
        //各々のFragmentからbyteを取得してbyte[][]に格納
        byte[][] bytesArray = new byte[fragmentList.size()][0];
        for (int i = 0; i < fragmentList.size(); i++) {
            bytesArray[i] = fragmentList.get(i).getBytes();
        }
        //bite[][]をjoinしてリターン
        return ByteUtil.join(bytesArray);
    }
    
    //削除するDataのKeyを指定してDataとFragmentの両方を削除
    public void delete(Key key) {
        Transaction tx = Datastore.beginTransaction();
        //削除するエンティティのKey一覧
        List<Key> keys = new ArrayList<Key>();
        //削除するエンティティ一覧にDataとFragmentのKeyを追加
        keys.add(key);
        keys.addAll(Datastore.query(f, key).asKeyList());
        //トランザクション内で削除とトランザクション完了
        Datastore.delete(tx, keys);
        Datastore.commit(tx);
    }
}

【変更後】

package slim3.demo.service;

import java.util.ArrayList
import java.util.Iterator
import java.util.List

import org.slim3.controller.upload.FileItem
import org.slim3.datastore.Datastore
import org.slim3.util.ByteUtil

import slim3.demo.meta.UploadedDataFragmentMeta
import slim3.demo.meta.UploadedDataMeta
import slim3.demo.model.UploadedData
import slim3.demo.model.UploadedDataFragment

import com.google.appengine.api.datastore.Key
import com.google.appengine.api.datastore.Transaction

import scala.collection.JavaConversions._


class UploadService(
        //DataとDataFragmentそれぞれのエンティティへのマッピングクラスを取得
        var d: UploadedDataMeta = UploadedDataMeta.get(),
        var f: UploadedDataFragmentMeta = UploadedDataFragmentMeta.get()
    ){
    
    object UploadService {
        //1つのデータの最大値は1MなのでFragmentのサイズを900000に設定
        val FRAGMENT_SIZE: Int = 900000;
    }
    
    //アップロードしたDataの一覧を取得
    def getDataList(): java.util.List[UploadedData] = {
        Datastore.query(d).asList();
    }
    
    //DataをDataFragmentに分割しながらアップロード
    def upload( formFile: FileItem ): UploadedData = formFile match {
        //★caseにしてみた
        case null => null
        case _ => {
            val models: List[Object] = Nil
            val data: UploadedData = new UploadedData()
            //datastoreに保存するデータの
            models.add(data);
            //☆この辺のオブジェクトのパラメータを指定してくとこをシンプルにする方法ない?
            data.setKey(Datastore.allocateId(d))
            data.setFileName(formFile.getShortFileName())
            data.setLength(formFile.getData().length)
            //Fragmentのサイズに分割
            val bytesArray: Array[Array[Byte]] = ByteUtil.split(formFile.getData, UploadService.FRAGMENT_SIZE);
            //Key一覧を生成
            val keys: Iterator[Key] = Datastore.allocateIds(data.getKey(), f, bytesArray.length).iterator();
            for { i <- 0 to bytesArray.length - 1 } {
                //FragmentData用のbyte[]
                var fragmentData = bytesArray(i)
                //インスタンス生成しmodelsに格納
                var fragment: UploadedDataFragment = new UploadedDataFragment()
                models.add(fragment)
                //★この辺のオブジェクトのパラメータを指定してくとこをシンプルにする方法ない?
                fragment.setKey(keys.next())
                fragment.setBytes(fragmentData)
                fragment.setIndex(i)
                fragment.getUploadDataRef().setModel(data)
            }
            //トランザクション開始
            val tx: Transaction = Datastore.beginTransaction()
            //★modelsのmodelを全部datastoreに追加
            models.map( model => Datastore.put(tx, model) )
            //トランザクション終了
            tx.commit()
            //dataのオブジェクトを返す
            data
        }
    }
    
    //Dataの情報を取得
    def getData(key: Key, version: Long): UploadedData = {
        Datastore.get(d, key, version)
    }
    
    //取得したDataの情報からFragmentをかき集めて修復
    def getBytes(uploadedData: UploadedData): Array[Byte] = uploadedData match {
        //☆ケースにしてみた
        case null => throw new NullPointerException( "The uploadedData parameter must not be null." )
        //★引数のフラグメントをmapで各々バイトに変換してArrayに格納,Arrayを最後にjoin
        case _ => ByteUtil.join(uploadedData.getFragmentListRef().getModelList().map( item => item.getBytes() ).toArray )
    }
    
    //削除するDataのKeyを指定してDataとFragmentの両方を削除
    def delete(key: Key): Unit =  {
        val tx: Transaction = Datastore.beginTransaction();
        //削除するエンティティのKey一覧
        val keys: List[Key] = Nil
        //削除するエンティティ一覧にDataとFragmentのKeyを追加
        keys.add(key);
        //★全部追加するメソッドとか元々ある
        keys.addAll(Datastore.query(f, key).asKeyList());
        //トランザクション内で削除とトランザクション完了
        Datastore.delete(tx, keys);
        Datastore.commit(tx);
    }
}