2012年3月16日金曜日


暫く日にちが経ちましたが、その間に、enumを使った crudはうまくいくようになりました。
JDOを利用したサーバ側のDAOプログラムをどうすればいいかの道筋が立って来ました。

そして、UIが少し変わりました。

そう、ボタンをつくったのと、そして、 


 ログイン画面をつくりました。
ある程度の実装も終わっています。


 ↑ここで登録をクリックすると、メールが飛んできて、
↓認証コードを入力して、登録完了です。

このログイン画面は他のアプリケーションでも使い回しができるように、作ってみました。
ちょっと作業(どちらかと言うとブログをアップする作業の方)が停滞気味ですが、次回にこのへんのソースを貼ることにします。

2012年3月12日月曜日

自社用業務システムを簡単に作ってみる AIR+BlazeDS+Spring その6

enumにトラブル発生。
これまでのプログラムで、Java+BlazeDS → AIR(Flex) の場合は、enumからテキストに変換さていましたが、AIR側からのテキストは、Javaでうまくenumとして受け取ってくれません。

ちょっと古いですが、こちらによると、
http://flexblog.faratasystems.com/2007/09/16/adding-enum-support-to-flex-amf-protocol
もともと、AMF(BlazeDSで使う通信フォーマット)は、Java1.4に対応して作られており、1.5で追加されたenumには対応していないとのこと。
このページでは、LCSD(有料版BlazeDSのようなもの)での解決策は記されていますが、BlazeDSとは別物のようでそのまま利用はできないようです。

そこで、オープンソースのwrenched http://code.google.com/p/wrenched/ というものをみつけて利用してみようとしましたが、正直使い方があまり良くわかりませんでした。

どうしようかと思っていたところ、このページにたどり着きようやく解決しました。
https://bugs.adobe.com/jira/browse/BLZ-17

単純な話、ActtionScript でもきちんと書けば enum のようになるということですな。

JobStatus.as
 [RemoteClass(alias=“jp.co.bzc.biz.xxxx.model.JobStatus”)]
 public class JobStatus
 {
  public static const 未着手:JobStatus = new JobStatus("未着手");
  public static const 着手:JobStatus = new JobStatus("着手");
  public static const 完了:JobStatus = new JobStatus("完了");
  
  public var val:String;
  public function JobStatus(v:String="unset"){
   val = v;
  }
  public function equals(other:JobStatus):Boolean{
   return other.val == val;
  }
   
  public function toString():String{
   return val;
  }
  
  public static function getAll():ArrayCollection{
   return new ArrayCollection([未着手,着手,完了]);
  }
 }


UserService.java に下記を加えます。
   static {
        PropertyProxyRegistry.getRegistry().register(Enum.class, new EnumProxy());
   } 

EnumProxyは、上記adobeのバグサイトに載っているものを使用しました。

これで、enumが使えるようになりました。
気持ちのよいプログラミングができそうです。

現在の課題は、JDOを使ったオブジェクトのアップデート(更新)です。
db4oのときもそうでしたが、同じIDを持っている別オブジェクトで「簡単に更新」するためにはどうすればいいのかが課題です。
atached detached などJDOの基本をやってみます。

2012年3月9日金曜日

自社用業務システムを簡単に作ってみる AIR+BlazeDS+Spring その5

JDOもOneToManyが必要なんだ...

OneToManyに難癖をつけて、JPAからJDOに変えましたが、JDOでもOneToManyが必要のようです。腹をくくるしかないようですね。

SiteMaking.java
@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class SiteMaking implements IWork,Serializable{
	private static final long serialVersionUID = -2668621914571969922L;
	@PrimaryKey
	@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
	private Long id;
	@Persistent
	@Unique
	private String name;
	@Persistent
	private Calendar deliveryDate;
	@Persistent(mappedBy = "siteMaking")
	private List jobs;
	@Persistent
	private String remark;
..

Job.java
@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class Job implements IJob,Serializable {
	private static final long serialVersionUID = 5763990372991734275L;
	@PrimaryKey
	@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
	private Long id;
	@Persistent
	private String name;
	@Persistent
	private Calendar startDate;
	@Persistent
	private Calendar endDate;
	@Persistent
	@Enumerated(EnumType.STRING)
	private Enum jobStatus;
	@Persistent
	private Calendar lastUpdated;
	@Persistent
	private SiteMaking siteMaking;

そして、DAOですが、前回はとりあえず保存まででしたので、もう少し書き加えてみます。
新規にお仕事を登録する際のメソッドとして、newSiteMakingPersistというものを作ってみました。

SiteMakingDAO.java
public interface SiteMakingDAO {
	public void newSiteMakingPersist(SiteMaking siteMaking);
	public List getSiteMakingNow();
}

SiteMakingDAOImpl.java 抜粋
public void newSiteMakingPersist(SiteMaking siteMaking){
        PersistenceManager pm = this.persistenceManagerFactory.getPersistenceManager();
        Transaction tx = pm.currentTransaction();
        try {
            tx.begin();
   		List list = new ArrayList();
   		for(SiteMakingJob j : SiteMakingJob.values()){
			Job job = new Job(j.toString(),siteMaking);
			list.add(job);
    	}
    	siteMaking.setJobs(list);
            pm.makePersistent(siteMaking);
            tx.commit();
        } finally {
            if (tx.isActive()) {
                tx.rollback();
            }
        }
}

クライアント側は、ジョブステータスを一覧から簡単に変えられるようにアイテムレンダラーを作成してみました。
JobItem.mxml


	
だんだんとそれっぽくなってきました。
	
	
	





2012年3月8日木曜日

自社用業務システムを簡単に作ってみる AIR+BlazeDS+Spring その4

JDOに変更する
これまで、JPAを使っていました。
情報と実装が多いのはいいことなのかもしれませんが、@OneToManyなどのなくてもいいようなアノテーションを使用する必要があります。
1対多の関係は、クラスにおいて
List<Object> obj;
などのフィールドをつくったら自明のはず。
こんな冗長に思えるアノテーションを書かなくても良いAPIや実装があるはずです。
そんな思いと過去の苦い体験があったので、@OneToManyなどのアノテーションについては、時間がかかりそうなことが発生すればすぐに他の方法に変えようと思っていました。
今回、@OneToManyと単純に書くだけでは思ったように動かないことが判明して、あっさりとJDOに移行することにしました。

application-context.xml の修正
Springの設定を変更します。
↓削除部
 
  
  
         
  
 
 
  
 
 
上記部分は削除し、代わりに、
     
        
            
                xxx
                xxx
                xxx
                xxx
            
        
    
 
        
            
        
    
 
  
  
 

 
  
 

を追加します。

を参考にしました。
これによると、アクティブトランザクション(?)を使う場合は、allowCreateをfalseにしろとあります。
With such DAOs that rely on active transactions, it is recommended that you enforce active transactions through turning off TransactionAwarePersistenceManagerFactoryProxy's allowCreate flag:
この時、例としてあげられている、DAOプログラムはこんな感じ、
        public Collection loadProductsByCategory(String category) {
        PersistenceManager pm = this.persistenceManagerFactory.getPersistenceManager();
        Query query = pm.newQuery(Product.class, "category = pCategory");
        query.declareParameters("String pCategory"); 
        return query.execute(category);
    }
これを参考に、ごく単純に、
PersistenceManager pm = this.persistenceManagerFactory.getPersistenceManager();
pm.makePersistent(obj);

としてみましたが、
No JDO PersistenceManager bound to thread, and configuration does not allow creation of non-transactional one here
というエラーが出ます。

http://forum.springsource.org/showthread.php?39628-Transactions-IllegalStateException このページをみて、allowCreate を true で行くことにしました。アクティブトランザクション(?)をやめたことになるんだと思います。

トランザクションの記述

それでも、Attempt to persist an object when no transaction is active
というエラーがでますので、DAOにトランザクションを記述しました。

        PersistenceManager pm = this.persistenceManagerFactory.getPersistenceManager();
        Transaction tx = pm.currentTransaction();
        try {
            tx.begin();
            pm.makePersistent(obj);
            tx.commit();
        } finally {
            if (tx.isActive()) {
                tx.rollback();
            }
        }
DAOの記述は、下記からになります。

実際のDAO

慣例に従い、XxxxDAO はインタフェースに変更。クラスは XxxxDAOImpl にしました。
JDOのアノテーションがうまく使えたら、もっとすっきりするかもしれません。

public interface SiteMakingDAO {
 public void persist(SiteMaking siteMaking);
}

@Componentのアノテーションはつけていません。

実装する側は、

public class SiteMakingDAOImpl implements SiteMakingDAO {
    private PersistenceManagerFactory persistenceManagerFactory;
    public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) {
        this.persistenceManagerFactory = pmf;
    }
 public void persist(SiteMaking siteMaking){
        PersistenceManager pm = this.persistenceManagerFactory.getPersistenceManager();
        Transaction tx = pm.currentTransaction();
        try {
            tx.begin();
            pm.makePersistent(siteMaking);
            tx.commit();
        } finally {
            if (tx.isActive()) {
                tx.rollback();
            }
        }
 }
}

としました。@Component アノテーションはつけていません。Spring設定ファイルの記述によりインスタンス化されます。

モデルの修正

@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class SiteMaking implements IWork,Serializable{
 private static final long serialVersionUID = 1L;
 @PrimaryKey
 @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
 private Long id;
 @Persistent
 private String name;
 @Persistent
 private List jobs;
 @Persistent
 private String remark;
 public SiteMaking(){
 }
 // 以下、セッター、ゲッター
}

JPAで使用する、@Column アノテーションを残していたらエラーが出ました。
単純に消します。

2012年3月7日水曜日

自社用業務システムを簡単に作ってみる AIR+BlazeDS+Spring その3

すべての工程と備考を表示するようにしてみます。
備考欄は設けていなかったので、サーバ側の IWork から修正します。

public interface IWork {
 public Long getId();
 public void setId(Long id);
 public String getName();
 public void setName(String name);
 public List getJobs();
 public void setJobs(List jobs);
 public String getRemark();
 public void setRemark(String remark);
}
SiteMaking.javaも修正
@Entity
public class SiteMaking implements IWork{
 @Id @GeneratedValue
 private Long id;
 private String name;
 private List jobs;
 private String remark;
 
 public SiteMaking(){
  jobs = new ArrayList();
  for(SiteMakingJob job : EnumSet.allOf(SiteMakingJob.class)){
   jobs.add(new Job(job.toString()));
  }
 }
 
 public Long getId() {
  return this.id;
 }

 public void setId(Long id) {
  this.id = id;
 }

 public String getName() {
  return this.name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public List getJobs() {
  return this.jobs;
 }

 public void setJobs(List jobs) {
  this.jobs = jobs;
 }

 public String getRemark() {
  return this.remark;
 }

 public void setRemark(String remark) {
  this.remark = remark;
 }

}
UserServiceも変更しておきます。
 public List getWorks(){
  List list = new ArrayList();
  SiteMaking work = new SiteMaking();
  work.setId(1L);
  work.setName("どこどこ店");
  work.setRemark("備考です");
  list.add(work);
  return list;
 }
クライアント側にも備考 remark を追加
 [Bindable]
 [RemoteClass(alias=“jp.co.bzc.biz.xxxx.model.SiteMaking”)]
 public class SiteMaking
 {
  public var id:Number;
  public var name:String;
  public var jobs:ArrayCollection;
  public var remark:String;
 }
AS3では、enumがないので、以下でお茶を濁します。
 [Bindable]
 public class SiteMakingJob
 {
  public static var 受注:String = "受注";
  public static var 原稿:String = "原稿";
  public static var ドメイン取得:String = "ドメイン取得";
  public static var サーバ構築:String = "サーバ構築";
  public static var 証明写真:String = "証明写真";
  public static var 作業写真:String = "作業写真";
  public static var 制作作業:String = "制作作業";
  public static var 仮仮アップ:String = "仮仮アップ";
  public static var 仮アップ:String = "仮アップ";
  public static var 公開:String = "公開";
  public static var GA設定:String = "GA設定";
 }
WorkList.mxml をいじります。

抜粋 DataGrid部
 
  
   
    
    
    
    
    
    
    
    
    
    
    
    
    
   
  
 
抜粋 ラベルファンクション
   public function label_job(item:Object,gc:GridColumn):String{
    for each(var job:Job in item.jobs){
     if(job.name == gc.headerText){
      return job.jobStatus;
     }
    }
    return "";
   }
それっぽくなってきました。


2012年3月6日火曜日

自社用業務システムを簡単に作ってみる AIR+BlazeDS+Spring その2

きょうはあまり進みませんでしたが...
まずは、AIRプログラムからBlazeDSをつかってサーバとの連携確認です。

とりあえずこんなプログラムで確認















↑コンソールに server:abc とでたらOK

もう少し進めてみます
サーバプログラムのUserServiceにメソッド追加
 public List getWorks(){
  List list = new ArrayList();
  SiteMaking work = new SiteMaking();
  work.setId(1L);
  work.setName("どこどこ店");
  list.add(work);
  return list;
 }
AIR側にモデルを追加
 [Bindable]
 [RemoteClass(alias=“jp.co.bzc.biz.xxxx.model.SiteMaking”)]
 public class SiteMaking
 {
  public var id:Number;
  public var name:String;
  public var jobs:ArrayCollection;
 }

 [Bindable]
 public class Job
 {
  public var id:Number;
  public var name:String;
  public var startDate:Date;
  public var endDate:Date;
  public var jobStatus:String;
  public var lastUpdated:Date;
 }
Flex側にメインとビューのMXML追加

 
  
  
 
 
  
   
   
  
  
   
  
 
 
  
 
 

ビューとなるパネルコンポーネントはこちら

 
  
 
 
  
   
    
   
  
 

import文は省略


とりあえず、サーバから受け取ったデータをAIRで表示するところまでできました。

自社用業務システムを簡単に作ってみる AIR+BlazeDS+Spring


サイト制作に限らず、仕事が重なってくると進捗管理が大変になってきます。
これまで、じっくりと作る仕事が多かったため、それほど仕事が重なることとはありませんでしたが、最近はライトウェイトなサイト制作の仕事が増えてきました。これらを上手にこなすように管理プログラムを作ってみます。

プログラム動作環境

クライアント側:AIRによるプログラム
サーバ側:Java + Spring + BlazeDS による開発
という構成にしてみます。

AIRの採用の理由は、(自分にとってですが)HTMLベースのサイトよりも時間をかけずに見栄えのするものができるということです。
また、AIRはWindowsとMacに対応していて、さらにはスマートフォンやiPhoneへの展開も可能であることも魅力です。
そして、インストールして使うのでWebサイトよりも不特定多数の人に晒されないということも挙げられます。

Eclipse環境構築

Eclipseと一部 maven を利用することにします。
具体的には、Spring系のライブラリは maven を利用することにしました。ただ、BlazeDS と Spring-BlazeDSは、ユーザーライブラリで管理します。

mavenは、ライブラリの管理だけで利用し、開発時のテストなどは、Eclipseのサーバ管理を利用します。
そのために、m2eとともに、m2e-wtpをインストールします。


pom.xmlについて

  4.0.0
  jp.co.bzc.biz
  xxxxxxx
  0.0.1-SNAPSHOT
  war
  Business Manager Server
  http://maven.apache.org
  
   UTF-8
  
  
   
    objectdb
    ObjectDB Repository
    http://m2.objectdb.com
   
  
  
   
    com.objectdb
    objectdb
    2.3.7_02
   
   
    javax.servlet
    servlet-api
    2.5
    provided
   
   
    javax.servlet.jsp
    jsp-api
    2.1
    provided
   
   
    org.springframework
    spring-webmvc
    3.0.5.RELEASE
   
   
    org.springframework
    spring-tx
    3.0.5.RELEASE
   
   
    org.springframework
    spring-orm
    3.0.5.RELEASE
   
   
    aopalliance
    aopalliance
    1.0
   
   
    cglib
    cglib
    2.2
   
   
    org.aspectj
    aspectjweaver
    1.6.10
   
    
      junit
      junit
      3.8.1
      test
    
  
  
          
            
                org.apache.maven.plugins
                maven-compiler-plugin
                2.3.2
                
                    1.6
                    1.6
                    UTF-8
                
            
            
                 org.mortbay.jetty
                 maven-jetty-plugin
                 6.1.10
                 
                     10
                     foo
                     9999
                 
                 
                     
                         start-jetty
                         pre-integration-test
                         
                             run
                         
                         
                             0
                             true
                         
                     
                     
                         stop-jetty
                             post-integration-test
                             
                                 stop
                             
                     
                 
             
         
    xxxxxxx
  
persistence.xmlについて
/META-INFにpersistence.xmlを用意します。

  
    xxxxx
    
      
      
      
    
  

web.xmlについて



 xxxx
 
  spring
  org.springframework.web.servlet.DispatcherServlet
  
   contextConfigLocation
   /WEB-INF/spring/application-config.xml
  
  1
 
 
  spring
  /messagebroker/*
 
 
  index.html
  index.htm
  index.jsp
 


Springの設定について
/WEB-INF/spring に application.xmlを置きます。

 

 
  
 


モデルの設計
 public interface IJob{
 public Long getId();
 public void setId(Long id);
 public String getName();
 public void setName(String name);
 public Calendar getStartDate();
 public void setStartDate(Calendar startDate);
 public Calendar getEndDate();
 public void setEndDate(Calendar endDate);
 public Enum getJobStatus();
 public void setJobStatus(Enum jobStatus);
 public Calendar getLastUpdated();
 public void setLastUpdated(Calendar lastUpdated);
}
public interface IWork {
 public Long getId();
 public void setId(Long id);
 public String getName();
 public void setName(String name);
 public List getJobs();
 public void setJobs(List jobs);
}
@Entity
public class Job implements IJob {

 @Id @GeneratedValue
 private Long id;
 private String name;
 private Calendar startDate;
 private Calendar endDate;
 private Enum jobStatus;
 private Calendar lastUpdated;
 
 public Job(String name){
  this.name = name;
  this.lastUpdated = Calendar.getInstance();
 }
 // Getter setterが続く
}
public enum JobStatus {
 未着手, 着手, 完了
}
@Entity
public class Person implements Serializable {
 private static final long serialVersionUID = 1L;
 @Id @GeneratedValue
 private Long id;
 private String name;
 private String email;
}
@Entity
public class SiteMaking implements IWork{
 @Id @GeneratedValue
 private Long id;
 private String name;
 private List jobs;
 
 public SiteMaking(){
  jobs = new ArrayList();
  for(SiteMakingJob job : EnumSet.allOf(SiteMakingJob.class)){
   jobs.add(new Job(job.toString()));
  }
 }
 // Getter setterが続く
}
public enum SiteMakingJob {
 受注,原稿,ドメイン取得,サーバ構築,写真,制作作業,仮仮アップ,仮アップ,公開,GA設定
}


DAO
@Component
public class SiteMakingDAO {
 @PersistenceContext
 private EntityManager em;
 
 @Transactional
 public void persist(SiteMaking siteMaking){
  em.persist(siteMaking);
 }
 
 @Transactional
 public List getSiteMakingNow(){
        TypedQuery query = em.createQuery(
                "SELECT g FROM SiteMaking g ORDER BY g.id", SiteMaking.class);
            List siteMakingList = query.getResultList();
            return siteMakingList;
 }
}

サービス
クライアントから呼び出されるメソッドを定義します。 このクラスもDIされるようにアノテーションで宣言できると思いますが、動かすことを優先ということで、application-context.xml で宣言しています。
public class UserService {
 @Autowired
 private SiteMakingDAO siteMakingDAO;
 
 public String echo(String txt){
  return "server:"+txt;
 }
 
 public void InitSiteMaking(SiteMaking siteMaking){
  siteMakingDAO.persist(siteMaking);
 }
}