/*
* Copyright 2012 Kazumune Katagiri. (http://d.hatena.ne.jp/nemuzuka)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package jp.co.nemuzuka.service.impl;
import java.util.ConcurrentModificationException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import jp.co.nemuzuka.common.Authority;
import jp.co.nemuzuka.common.UniqueKey;
import jp.co.nemuzuka.core.controller.AbsController;
import jp.co.nemuzuka.core.entity.mock.UserServiceImpl;
import jp.co.nemuzuka.dao.MemberDao;
import jp.co.nemuzuka.entity.MemberKeyEntity;
import jp.co.nemuzuka.exception.AlreadyExistKeyException;
import jp.co.nemuzuka.form.MemberForm;
import jp.co.nemuzuka.form.PersonForm;
import jp.co.nemuzuka.model.MemberModel;
import jp.co.nemuzuka.service.MemberService;
import jp.co.nemuzuka.utils.ConvertUtils;
import jp.co.nemuzuka.utils.CurrentDateUtils;
import jp.co.nemuzuka.utils.DateTimeChecker;
import jp.co.nemuzuka.utils.DateTimeUtils;
import org.apache.commons.lang.StringUtils;
import org.slim3.datastore.Datastore;
import org.slim3.memcache.Memcache;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.Text;
/**
* MemberServiceの実装クラス.
* @author kazumune
*/
public class MemberServiceImpl implements MemberService {
MemberDao memberDao = MemberDao.getInstance();
private static MemberServiceImpl impl = new MemberServiceImpl();
/**
* インスタンス取得.
* @return インスタンス
*/
public static MemberServiceImpl getInstance() {
return impl;
}
/**
* デフォルトコンストラクタ.
*/
private MemberServiceImpl(){}
/* (non-Javadoc)
* @see jp.co.nemuzuka.service.MemberService#checkAndCreateMember(java.lang.String, java.lang.String, jp.co.nemuzuka.common.Authority)
*/
@Override
public void checkAndCreateMember(String mail, String nickName, Authority authority) {
MemberModel model = getModel(mail);
if(model != null) {
return;
}
//存在しないので、登録する
MemberForm form = new MemberForm();
form.mail = mail;
form.name = nickName;
form.timeZone = jp.co.nemuzuka.common.TimeZone.GMT_P_9.getCode();
form.authority = authority.name();
put(form);
}
/* (非 Javadoc)
* @see jp.co.nemuzuka.service.MemberService#get(java.lang.String)
*/
@Override
public MemberForm get(String keyString) {
MemberForm form = new MemberForm();
if(StringUtils.isNotEmpty(keyString)) {
//Key情報が設定されていた場合
Key key = Datastore.stringToKey(keyString);
MemberModel model = memberDao.get(key);
setForm(form, model);
}
return form;
}
/* (非 Javadoc)
* @see jp.co.nemuzuka.service.MemberService#getList(java.lang.String, java.lang.String)
*/
@Override
public List<MemberModel> getList(String name, String mail) {
return memberDao.getList(name, mail);
}
/* (非 Javadoc)
* @see jp.co.nemuzuka.service.MemberService#put(jp.co.nemuzuka.form.MemberForm)
*/
@Override
public void put(MemberForm form) {
MemberModel model = null;
if(StringUtils.isNotEmpty(form.keyToString)) {
//更新の場合
Key key = Datastore.stringToKey(form.keyToString);
Long version = ConvertUtils.toLong(form.versionNo);
//versionとKeyで情報を取得
model = memberDao.get(key, version);
if(model == null) {
//該当レコードが存在しない場合、Exceptionをthrow
throw new ConcurrentModificationException();
}
} else {
//新規の場合、入力されたKey項目相当が存在するかチェック
if (Datastore.putUniqueValue(UniqueKey.member.name(), form.mail) == false) {
throw new AlreadyExistKeyException();
}
model = new MemberModel();
reSetMemberKeyEntity();
}
//取得した情報に対してプロパティを更新
setModel(model, form);
memberDao.put(model);
}
/* (非 Javadoc)
* @see jp.co.nemuzuka.service.MemberService#delete(jp.co.nemuzuka.form.MemberForm)
*/
@Override
public void delete(MemberForm form) {
//versionとKeyで情報を取得
Key key = Datastore.stringToKey(form.keyToString);
Long version = ConvertUtils.toLong(form.versionNo);
MemberModel model = memberDao.get(key, version);
if(model == null) {
//該当レコードが存在しない場合、Exceptionをthrow
throw new ConcurrentModificationException();
}
//削除
memberDao.delete(model.getKey());
//一意制約チェック用のModelからも削除する
Datastore.deleteUniqueValue(UniqueKey.member.name(), model.getMail());
//キャッシュ初期化
reSetMemberKeyEntity();
}
/* (非 Javadoc)
* @see jp.co.nemuzuka.service.MemberService#getAllList()
*/
@Override
public List<MemberModel> getAllList() {
List<MemberModel> list = memberDao.getAllList();
return list;
}
/* (non-Javadoc)
* @see jp.co.nemuzuka.service.MemberService#getPersonForm(java.lang.String)
*/
@Override
public PersonForm getPersonForm(String email) {
PersonForm form = new PersonForm();
MemberModel model = getModel(email);
if(model == null) {
return form;
}
form.keyToString = model.getKeyToString();
if(model.getMemo() != null) {
form.memo = model.getMemo().getValue();
}
form.name = model.getName();
form.versionNo = ConvertUtils.toString(model.getVersion());
return form;
}
/* (non-Javadoc)
* @see jp.co.nemuzuka.service.MemberService#put(jp.co.nemuzuka.form.PersonForm)
*/
@Override
public void put(PersonForm form) {
//更新の場合
Key key = Datastore.stringToKey(form.keyToString);
Long version = ConvertUtils.toLong(form.versionNo);
//versionとKeyで情報を取得
MemberModel model = memberDao.get(key, version);
if(model == null) {
//該当レコードが存在しない場合、Exceptionをthrow
throw new ConcurrentModificationException();
}
//入力値を設定して、登録
model.setName(form.name);
model.setMemo(new Text(StringUtils.defaultString(form.memo)));
memberDao.put(model);
}
/* (non-Javadoc)
* @see jp.co.nemuzuka.service.MemberService#getMap(com.google.appengine.api.datastore.Key[])
*/
@Override
public Map<Key, MemberModel> getMap(Key... keys) {
return memberDao.getMap(keys);
}
/* (non-Javadoc)
* @see jp.co.nemuzuka.service.MemberService#getKey(java.lang.String)
*/
@Override
public Key getKey(String mail) {
return getMemberKeyEntity().map.get(mail);
}
/* (non-Javadoc)
* @see jp.co.nemuzuka.service.MemberService#getKeyString(java.lang.String)
*/
@Override
public String getKeyString(String mail) {
return getMemberKeyEntity().keyStringMap.get(mail);
}
/* (non-Javadoc)
* @see jp.co.nemuzuka.service.MemberService#getTimeZone(java.lang.String)
*/
@Override
public String getTimeZone(String mail) {
return getMemberKeyEntity().timeZoneMap.get(mail);
}
/* (non-Javadoc)
* @see jp.co.nemuzuka.service.MemberService#getModel(java.lang.String)
*/
@Override
public MemberModel getModel(String mail) {
Key key = getKey(mail);
return memberDao.get(key);
}
/**
* MemberKeyEntityリセット.
* 更新時刻をリセットし、次回アクセスには最新情報を取得するようにします。
* 新規ユーザ登録時に呼ばれることを想定しています。
*/
private void reSetMemberKeyEntity() {
MemberKeyEntity entity = Memcache.get(MemberKeyEntity.class.getName());
if(entity == null) {
return;
}
entity.refreshStartTime = null;
Memcache.put(MemberKeyEntity.class.getName(), entity);
}
/**
* MemberKeyEntity取得.
* Memcacheよりデータを取得し、存在ない or 期限切れの場合、最新情報に更新します。
* @return MemberKeyEntityインスタンス
*/
private MemberKeyEntity getMemberKeyEntity() {
//Memcacheよりデータを取得する
MemberKeyEntity entity = Memcache.get(MemberKeyEntity.class.getName());
if(entity == null || entity.refreshStartTime == null ||
DateTimeChecker.isOverRefreshStartTime(entity.refreshStartTime)) {
//存在しない or 更新期限切れの場合、リフレッシュ
entity = createMemberKeyEntity();
Memcache.put(MemberKeyEntity.class.getName(), entity);
}
return entity;
}
/**
* MemberKeyEntity生成.
* @return MemberKeyEntityインスタンス
*/
private MemberKeyEntity createMemberKeyEntity() {
MemberKeyEntity entity = new MemberKeyEntity();
//登録されているMember情報を取得
List<MemberModel> list = getList("", "");
for(MemberModel target : list) {
entity.map.put(target.getMail(), target.getKey());
entity.keyStringMap.put(target.getMail(), target.getKeyToString());
entity.timeZoneMap.put(target.getMail(), target.getTimeZone());
}
//トライアル版でかつ、ダミーユーザが未アクセスの場合、ダミーユーザのKeyを追加
if(AbsController.trialMode) {
if(entity.map.get(UserServiceImpl.DUMMY_EMAIL) == null) {
//まだダミーユーザがMemberModelに登録されていない状態を想定
entity.map.put(UserServiceImpl.DUMMY_EMAIL,
Datastore.createKey(MemberModel.class, UserServiceImpl.DUMMY_EMAIL));
}
}
//現在時刻に加算分の時刻(分)を加算し、設定する
Date date = CurrentDateUtils.getInstance().getCurrentDateTime();
int min = ConvertUtils.toInteger(System.getProperty("jp.co.nemuzuka.member.map.refresh.min", "15"));
date = DateTimeUtils.addMinutes(date, min);
entity.refreshStartTime = date;
return entity;
}
/**
* Form情報設定.
* @param form 設定対象Form
* @param model 設定Model
*/
private void setForm(MemberForm form, MemberModel model) {
if(model == null) {
return;
}
form.keyToString = model.getKeyToString();
form.mail = model.getMail();
form.name = model.getName();
form.timeZone = model.getTimeZone();
form.authority = model.getAuthority().getCode();
form.versionNo = ConvertUtils.toString(model.getVersion());
}
/**
* Model情報設定.
* メールアドレスは、更新不可なのでパラメータを変更されても無視する。
* @param model 設定対象Model
* @param form 設定Form
*/
private void setModel(MemberModel model, MemberForm form) {
if(model.getKey() == null) {
//新規の場合
model.createKey(form.mail);
}
model.setName(form.name);
Authority authority = Authority.fromCode(form.authority);
if(authority != null) {
model.setAuthority(authority);
} else {
model.setAuthority(Authority.normal);
}
//タイムゾーンの設定
String timeZone = form.timeZone;
if(StringUtils.isEmpty(timeZone)) {
timeZone = jp.co.nemuzuka.common.TimeZone.GMT_P_9.getCode();
}
model.setTimeZone(TimeZone.getTimeZone(timeZone).getID());
}
}