Skip to content

Instantly share code, notes, and snippets.

@shin1ogawa
Created March 23, 2009 04:38
Show Gist options
  • Save shin1ogawa/83426 to your computer and use it in GitHub Desktop.
Save shin1ogawa/83426 to your computer and use it in GitHub Desktop.
/*
* Copyright 2007-2009 Jiemamy Project and the Others.
* Created on 2008/09/25
*
* This file is part of Jiemamy.
*
* 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 org.jiemamy;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jiemamy.dialect.Dialect;
import org.jiemamy.dialect.EmitConfig;
import org.jiemamy.exception.IllegalImplementationException;
import org.jiemamy.exception.JiemamyRuntimeException;
import org.jiemamy.facade.JiemamyFacade;
import org.jiemamy.model.RootModel;
import org.jiemamy.model.sql.SqlStatement;
import org.jiemamy.serializer.JiemamySerializer;
/**
* Jiemamyのコントロール中枢クラス。
*
* <p>Jiemamyは、このクラスのインスタンス毎に、モデル群のコンテキストを定めており、それを「インスタンス空間」と呼ぶ。
* 複数のインスタンス空間をまたがるモデルが関係を持ってはならない。(子モデルとなる等)</p>
*
* <p>また、Jiemamy関連クラスのインスタンスを生成・取得するためのファクトリ({@link JiemamyFactory})のプロバイダとして機能する。<p>
*
* <p>newInstanceの引数には、使用する実装が提供する {@link JiemamyImplementation} のインスタンスを与えることができる。
* この引数により、実装の種類及び、その実装が提供する仕様の範囲が決定される。</p>
*
* @since 0.2
* @author daisuke
*/
public final class Jiemamy {
/**
* Jiemamyインスタンスを生成する。
*
* <p>このメソッドで初期化を行った場合、各動作のデフォルトは下記の通りとなる。</p>
*
* <table style="border:1px solid black; border-collapse: collapse;">
* <tr>
* <td style="border:1px solid black">{@link JiemamyFactory}で使用されるデフォルトのJiemamy実装</td>
* <td style="border:1px solid black">Artemis</td>
* </tr>
* <tr>
* <td style="border:1px solid black">デフォルトの{@link Dialect}プロバイダ</td>
* <td style="border:1px solid black">{@link DefaultInstanceProvider}</td>
* </tr>
* </table>
*
* @return Jiemamyインスタンス
* @throws JiemamyRuntimeException Artemis実装が見つからない、もしくは不正である場合
* @since 0.2
*/
public static Jiemamy newInstance() {
return newInstance(findDefaultImplementation(), new DefaultInstanceProvider<Dialect>());
}
/**
* Jiemamyインスタンスを生成する。
*
* <p>このメソッドで初期化を行った場合、各動作のデフォルトは下記の通りとなる。</p>
*
* <table style="border:1px solid black; border-collapse: collapse;">
* <tr>
* <td style="border:1px solid black">{@link JiemamyFactory}で使用されるデフォルトのJiemamy実装</td>
* <td style="border:1px solid black">引数に指定したもの</td>
* </tr>
* <tr>
* <td style="border:1px solid black">デフォルトの{@link Dialect}プロバイダ</td>
* <td style="border:1px solid black">{@link DefaultInstanceProvider}</td>
* </tr>
* </table>
*
* @param defaultImplementation {@link JiemamyFactory}で使用されるデフォルトのJiemamy実装
* @return Jiemamyインスタンス
* @throws IllegalArgumentException 引数に{@code null}を与えた場合
* @since 0.2
*/
public static Jiemamy newInstance(JiemamyImplementation defaultImplementation) {
return newInstance(defaultImplementation, new DefaultInstanceProvider<Dialect>());
}
/**
* Jiemamyインスタンスを生成する。
*
* <p>このメソッドで初期化を行った場合、各動作のデフォルトは下記の通りとなる。</p>
*
* <table style="border:1px solid black; border-collapse: collapse;">
* <tr>
* <td style="border:1px solid black">{@link JiemamyFactory}で使用されるデフォルトのJiemamy実装</td>
* <td style="border:1px solid black">引数に指定したもの</td>
* </tr>
* <tr>
* <td style="border:1px solid black">デフォルトの{@link Dialect}プロバイダ</td>
* <td style="border:1px solid black">引数に指定したもの</td>
* </tr>
* </table>
*
* @param implementation {@link JiemamyFactory}で使用されるデフォルトのJiemamy実装
* @param defaultDialectProvider {@link JiemamyFactory}で使用されるデフォルトの{@link Dialect}インスタンス取得戦略
* @return Jiemamyインスタンス
* @throws IllegalArgumentException 引数に{@code null}を与えた場合
* @since 0.2
*/
public static Jiemamy newInstance(JiemamyImplementation implementation,
InstanceProvider<Dialect> defaultDialectProvider) {
return new Jiemamy(implementation, defaultDialectProvider);
}
/**
* デフォルトの{@link JiemamyImplementation}実装のインスタンスを取得する。
*
* @return デフォルトのJiemamy実装
* @throws JiemamyRuntimeException 実装がクラスパス上に見つからない場合
*/
private static JiemamyImplementation findDefaultImplementation() {
try {
Class<?> artemisClass = Class.forName("org.jiemamy.Artemis");
return (JiemamyImplementation) artemisClass.newInstance();
} catch (ClassNotFoundException e) {
throw new JiemamyRuntimeException("Jiemamy Artemis implementation not found.", e);
} catch (InstantiationException e) {
throw new JiemamyRuntimeException("Illegal Jiemamy Artemis implementation.", e);
} catch (IllegalAccessException e) {
throw new JiemamyRuntimeException("Illegal Jiemamy Artemis implementation.", e);
}
}
/** デフォルトで使用するJiemamy実装 */
private final JiemamyImplementation defaultImplementation;
/** デフォルトで使用する{@link Dialect}取得戦略 */
private final InstanceProvider<Dialect> dialectProvider;
/**
* インスタンスを生成する。
*
* @param defaultImplementation デフォルトで使用するJiemamy実装
* @param dialectProvider デフォルトで使用する{@link Dialect}取得戦略
* @throws IllegalArgumentException 引数に{@code null}を与えた場合
*/
private Jiemamy(JiemamyImplementation defaultImplementation, InstanceProvider<Dialect> dialectProvider) {
if (defaultImplementation == null || dialectProvider == null) {
throw new IllegalArgumentException();
}
this.defaultImplementation = defaultImplementation;
this.dialectProvider = dialectProvider;
}
/**
* {@link RootModel}を{@link SqlStatement}のリストに変換する。
*
* @param rootModel 変換対象
* @param config 設定オブジェクト
* @return {@link SqlStatement}のリスト
* @throws ClassNotFoundException {@link RootModel}に設定された{@link Dialect}の実装クラスが見つからない場合
* @since 0.2
*/
public List<SqlStatement> emitStatements(RootModel rootModel, EmitConfig config) throws ClassNotFoundException {
return getDialect(rootModel).emitStatements(rootModel, config);
}
/**
* デフォルトで使用するJiemamy実装を取得する。
*
* @return デフォルトで使用するJiemamy実装
* @since 0.2
*/
public JiemamyImplementation getDefaultImplementation() {
return defaultImplementation;
}
/**
* SQL方言を取得する。
*
* @param rootModel 適用対象{@link RootModel}
* @return SQL方言
* @throws ClassNotFoundException
* @since 0.2
*/
public Dialect getDialect(RootModel rootModel) throws ClassNotFoundException {
return dialectProvider.getInstance(rootModel.getDialectClassName());
}
/**
* イベントブローカを取得する。
*
* @return イベントブローカ
* @see JiemamyImplementation#getEventBroker()
* @since 0.2
*/
public EventBroker getEventBroker() {
return defaultImplementation.getEventBroker();
}
/**
* JiemamyFactoryを取得する。
*
* <p>このメソッドで初期化を行った場合、Jiemamy実装及び{@link Dialect}インスタンス取得戦略は、
* デフォルト(インスタンス生成時に決定されたもの)が使用される。</p>
*
* @return {@link JiemamyFactory}
* @since 0.2
*/
public JiemamyFactory getFactory() {
return getFactory(defaultImplementation);
}
/**
* 参照リゾルバを取得する。
*
* @return 参照リゾルバ
* @see JiemamyImplementation#getReferenceResolver()
* @since 0.2
*/
public ReferenceResolver getReferenceResolver() {
return defaultImplementation.getReferenceResolver();
}
/**
* シリアライザを取得する。
*
* @return シリアライザ
* @since 0.2
*/
public JiemamySerializer getSerializer() {
return defaultImplementation.getSerializer(this);
}
/**
* TODO for daisuke
*
* @param <T>
* @return
* @since 0.2
*/
public <T extends JiemamyFacade>T newFacade() {
return defaultImplementation.newFacade(this);
}
/**
* JiemamyFactoryを取得する。
*
* <p>与える{@link JiemamyImplementation}によって、ファクトリが生成する実装の種類及び、
* その実装が提供する仕様のサポート範囲が決定される。</p>
*
* <p>また、与える{@link InstanceProvider}によって、ファクトリが {@link Dialect}のインスタンスを取得する戦略が
* 決定される。</p>
*
* @param implementation 使用するJiemamy実装
* @return {@link JiemamyFactory}
* @throws IllegalStateException Jiemamyが既に初期化済みである場合
* @since 0.2
*/
JiemamyFactory getFactory(JiemamyImplementation implementation) {
return implementation.getFactory(this);
}
/**
* インスタンスにアクセスするデフォルトの方法を示す戦略クラス。
*
* <p>リフレクションを使って、デフォルトコンストラクタでインスタンス生成を行う。
* 一度生成されたインスタンスはキャッシングされ、二度目以降はインスタンス生成を行わない。</p>
*
* @param <T> 対象インスタンスの型
* @since 0.2
* @author daisuke
*/
public static class DefaultInstanceProvider<T> implements InstanceProvider<T> {
/** 新規インスタンス生成時のクラスローダ */
protected ClassLoader classLoader;
/** インスタンスキャッシュ */
private Map<String, T> cache = new HashMap<String, T>();
/**
* インスタンスを生成する。
*
* @since 0.2
*/
public DefaultInstanceProvider() {
this(null);
}
/**
* インスタンスを生成する。
*
* @param classLoader クラスローダ
* @since 0.2
*/
public DefaultInstanceProvider(ClassLoader classLoader) {
this.classLoader = classLoader;
}
public T getInstance(String fqcn) throws ClassNotFoundException {
assert cache != null;
if (fqcn == null) {
throw new ClassNotFoundException();
}
if (cache.get(fqcn) == null) {
Class<?> clazz;
if (classLoader == null) {
clazz = Class.forName(fqcn);
} else {
clazz = Class.forName(fqcn, true, classLoader);
}
try {
@SuppressWarnings("unchecked")
T instance = (T) clazz.newInstance();
cache.put(fqcn, instance);
} catch (ClassCastException e) {
throw new IllegalArgumentException(fqcn);
} catch (InstantiationException e) {
throw new IllegalImplementationException(clazz, e);
} catch (IllegalAccessException e) {
throw new IllegalImplementationException(clazz, e);
}
}
return cache.get(fqcn);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment