Quantcast
Channel: Eclipse RCP, RAP Blog
Viewing all 71 articles
Browse latest View live

Eclipse 4 アプリケーションの作成 (2) ギャラリービューの作成

$
0
0
今回は、前回作成したサービスを利用して、ビデオ一覧を表示するビューを作成します。
ビューを作成する前に、自動生成された Application.e4xmi をオープンして、アプリケーションモデルの初期状態を変更します:

  • ウィンドウの位置、サイズを変更: Bounds(5,5,1000,600)
  • ウィンドウのラベルを変更:マイビデオ
  • PartSashContainer の 分割方向(Orientation) を水平(Horizontal)に変更。
  • 自動的に作成されたパートスタックを削除。
  • PartSashContainer に2つのパートを追加:
    ギャラリーパート (Id = "com.itrane.myvideo.GalleryPart")
    ビデオパート (Id = "com.itrane.myvideo.VideoPart")

これらの変更結果は以下のようになります。


次は、追加したギャラリーパートにビュー(GalleryView) をリンクします。

必須プラグインの追加
ギャラリービューでは、ビデオ一覧の表示に Nebula Gallery Widgetを使用します。 更新サイト:http://download.eclipse.org/technology/nebula/snapshot から Gallery Widget をインストールします。 次に、plugin.xml を開いて依存関係ページで、必須プラグインとして、org.eclipse.nebula.widgets.gallery を追加してください。 また、ギャラリービューは依存性注入により、前回作成したビデオサービスファクトリを使用します。以下のプラグインも追加する必要があります:
  • org.eclipse.e4.ui.di
  • com.itrane.myvideo.videoservice
  • com.itrane.myvideo.videoservice.impl

ビュークラスの作成
com.itrane.myvideo プラグインに com.itrane.myvideo.views パッケージを作成します。作成したパッケージに GalleyView クラスを作成します。E4 では、ビューはプラットフォームに依存しません。 作成した GalleryView のコードは以下のようになります:
publicclass GalleryView {

private List<String> fGalleryGroups;
private Map<String, List<IVideo>> fVideoMap;

private Gallery fGallery;
private DefaultGalleryGroupRenderer fGalleryGroupRenderer;
private DefaultGalleryItemRenderer fGalleryItemRenderer;

private IVideoHomeFactory zVideoHomeFactory;
private IVideoHome fVideoHome;

@Inject
public GalleryView(IVideoHomeFactory videoHomeFactory) {
zVideoHomeFactory = videoHomeFactory;
}

/**
* ビューパートのコンテンツを作成.
*/

@PostConstruct
publicvoid createControls(Composite parent) {
initData();

Composite composite = new Composite(parent, SWT.NONE);
composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));

createGallery(composite);

}

privatevoid initData() {
String root = "C:/$mydoc/video/music";
fVideoHome = zVideoHomeFactory.createHome(
root,
new String[] {"mp4", "divx", "flv", "avi", "wmv"});
fVideoMap = new HashMap<String, List<IVideo>>();
for (IVideo video: fVideoHome.getVideoList()) {
String artist = video.getGroup();
List<IVideo> videos = fVideoMap.get(artist);
if (videos == null) {
videos = new ArrayList<IVideo>();
videos.add(video);
fVideoMap.put(artist, videos);
} else {
videos.add(video);
}
}
fGalleryGroups = new ArrayList<String>(fVideoMap.keySet());
Collections.sort(fGalleryGroups);
}

privatevoid createGallery(Composite composite) {
composite.setLayout(new FillLayout());
fGallery = new Gallery(composite, SWT.VIRTUAL | SWT.BORDER | SWT.MULTI | SWT.V_SCROLL);

//項目をダブルクリックしたときの処理
fGallery.addMouseListener(new MouseAdapter() {
@Override
publicvoid mouseDoubleClick(MouseEvent e) {
       // TODO doubleclick
}
});

// グループレンダラーの作成
fGalleryGroupRenderer = new DefaultGalleryGroupRenderer();
fGalleryGroupRenderer.setMaxImageWidth(60);
fGalleryGroupRenderer.setMinMargin(0);
// fGroupRenderer.setAlwaysExpanded(true);

// 項目レンダラーの作成
fGalleryItemRenderer = new DefaultGalleryItemRenderer();

// サムネールの表示方法を設定
fGalleryGroupRenderer.setItemSize(120, 90);
fGalleryItemRenderer.setShowLabels(true);

//レンダラーをギャラリーに設定
fGallery.setGroupRenderer(fGalleryGroupRenderer);
fGallery.setItemRenderer(fGalleryItemRenderer);

//パフォーマンスのため仮想グループにする
fGallery.setVirtualGroups(true);
fGallery.addListener(SWT.SetData, new Listener() {

publicvoid handleEvent(Event event) {
String imgPath = fVideoHome.getCaptureDir();
GalleryItem item = (GalleryItem) event.item;
int pindex = 0, cindex = 0;
if (item.getParentItem() != null) {
//%%% ビデオ項目を作成する %%%%%
String key = item.getParentItem().getText();
cindex = item.getParentItem().indexOf(item);
item.setItemCount(0);

List<IVideo> videos = fVideoMap.get(key);
if (videos != null) {
IVideo video = videos.get(cindex);
if (video != null) {
//項目のイメージとテキストの表示
item.setText(video.getTitle());
//item.setData(video);
String fileName = fVideoHome
.getCaptureFileName(video);
setImage(item, imgPath + fileName);
if (cindex == 0) {
item.getParentItem().setImage(item.getImage());
}
}
} else {
item.setText("Video " + item.getParentItem().getText() + " - " + cindex); //$NON-NLS-1$
}

} else {
//%%% グループ項目を作成する %%%%%
pindex = fGallery.indexOf(item);
String key = fGalleryGroups.get(pindex);
List<IVideo> videos = fVideoMap.get(key);
if (videos != null) {
IVideo video = videos.get(0);
if (video != null) {
//グループのイメージとテキストの表示
String fileName = fVideoHome
.getCaptureFileName(video);
File imgFile = new File(imgPath + fileName);
if (imgFile.exists()) {
final Image img = PluginImages.getImage(imgPath + fileName);
item.setImage(img);
} else {
item.setImage(PluginImages.NOCAPTURE_IMG.createImage());
}
}
item.setItemCount(videos.size());
}
setGroupToItem(fGalleryGroups.get(pindex), item);
}
}

});
fGallery.setItemCount(fGalleryGroups.size());
}

privatevoid setImage(GalleryItem item, String filePath) {
System.out.println("filePath="+filePath);
File imgFile = new File(filePath);
if (imgFile.exists()) {
final Image img = PluginImages.getImage(filePath);
item.setImage(img);
} else {
item.setImage(PluginImages.NOCAPTURE_IMG.createImage());
}
}

privatevoid setGroupToItem(String group, GalleryItem item) {
item.setText(group);
Image groupImage = getGroupImage(group);
if (groupImage != null) {
item.setImage(groupImage);
}
}
private Image getGroupImage(String group) {
return null;
}

@PreDestroy
publicvoid dispose() {
}

@Focus
publicvoid setFocus() {
fGallery.setFocus();
}
}

上記コードでは、次の処理を行っています。
  1. @Inject アノテーションにより、コンストラクタに VideoHomeFactoryのインスタンスが渡されます。
  2. 続いて createControls() メソッドが実行されます(@PostConstruct アノテーションは、DIが完了した後にこのメソッドが呼ばれることを保障します)。
  3. createControls では、最初に initData() メソッドを実行します。
  4. initData() メソッドでは、VideoHomeFactory により VideoHomeインスタンスが生成されます。 このとき、ビデオファイル一覧を取得する、トップディレクトリとビデオファイルのタイプを指定します。 VideoHome#getVideoList() で指定されたパス以下のビデオ情報を取得し、グループ別のビデオ一覧マップ(fVideoMap)を作成します。
  5. createControls の残りの部分で、このマップを使って、Nebula Gallery Widgetにより、グループ別のビデオギャラリーを作成します。
ビューとパートをリンクする
Application.e4xmi を開き、先に追加したギャラリーパートを選択して、"Class URI" 項目の "Find" ボタンを押して、作成したギャラリービューを選択します(下図):

また、e4 テンプレートによって作成された、ハンドラやコマンド、メニューバー、ツールバーは使用しないので、モデルから削除します。変更を保存して、アプリケーションを起動します。アプリケーションを起動する前に、製品構成ファイル(com.itrane.myvideo.puroduct) を開き、"Dependencies"ページで "Add Required Plug-ins" ボタンを押して、追加した必須プラグインが確実に反映されるようにします。 これで、ビデオサービスを利用して、ビデオの一覧を表示するところまで完成しました(下図):

次回は選択ビデオを再生する機能を追加します。

Eclipse 4 アプリケーションの作成 (3) ビデオビューの作成

$
0
0
今回は、ビデオ再生のためのビデオビューを作成します。 ビデオ再生には、以前「vlcjを使ってビデオプレーヤーを作成する」で紹介した vlcjを使用します。

vlcj を使うための準備
VideoLANからVLCメディアプレーヤーをダウンロードして、インストールします。 次に、vlcj のダウンロードページから vlcj-2.1.0.jar, jna-3.4.0.jar, platform-3.4.0.jar をダウンロードして、com.itrane.myvideo の lib フォルダにコピーします。 そして、plugin.xml のランタイムページで、クラスパスにこれらの jar を追加します。

ビデオビューの作成
com.itrane.myvideo.views パッケージに、以下の内容の VideoView クラスを作成します。
publicclass VideoView {

private VideoViewer fVideoViewer;

public VideoView() {
}

@PostConstruct
publicvoid createControls(Composite parent) {
parent.setLayout(new FillLayout());
fVideoViewer = new VideoViewer(parent);
}
// ....
}

さらに、ビデオビューが使うビデオビューアクラス(com.itrane.myvideo.views.VideoViewer)も作成します:
publicclass VideoViewer extends Composite {

// VLCJ の設定
privatestaticfinal String VLCJ_LOG_LEVEL = "INFO";
privatestaticfinal String DUMP_NATIVE_MEMORY = "false";
privatestaticfinalint MAX_POSITION = 1000;

// 操作ボタン
private ToolBar fToolBar;
private ToolItem fTItmPlay;
private ToolItem fTItmStop;

// 再生位置表示コントロール
private ProgressBar fProgressBar;

private Composite fToolBarPanel;
private Composite fPlayerPanel;

// VLCJ メディアプレイーやー
private MediaPlayerFactory fMediaPlayerFactory;
private EmbeddedMediaPlayer fMediaPlayer;
private CanvasVideoSurface fVideoSurface;
private Canvas fVideoCanvas;
private Frame fVideoFrame;

// 現在表示しているビデオ
private IVideo fVideo;

private Label lblTitle;
private Color fTitleColor;
privateboolean fFullscreen;

/** コンストラクタ. */
public VideoViewer(Composite parent) {
super(parent, SWT.NONE);
System.setProperty("vlcj.log", VLCJ_LOG_LEVEL);
System.setProperty("jna.dump_memory", DUMP_NATIVE_MEMORY);
System.setProperty("jna.library.path", "VLCメディアプレーヤーのインストールディレクトリ");
createControl();
}

// UIの構築.
privatevoid createControl() {

this.setLayout(createGridLayout(1, false, 0, 0));

// 操作パネル
fToolBarPanel = new Composite(this, SWT.NONE);
fToolBarPanel.setLayout(createGridLayout(3, false, 0, 0));
GridData gdToolBar = new GridData(SWT.FILL, SWT.FILL, false, false, 1, 1);
gdToolBar.heightHint = 22;
fToolBarPanel.setLayoutData(gdToolBar);

lblTitle = new Label(fToolBarPanel, SWT.NONE);
lblTitle.setForeground(fTitleColor);
lblTitle.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));

createPositionControl(fToolBarPanel);
createButtons(fToolBarPanel);
createPlayer(this);

setToolItemEnabled(false);
}

// 操作パネルにボタンを作成する.
privatevoid createButtons(final Composite composite) {
fToolBar = new ToolBar(composite, SWT.FLAT | SWT.RIGHT);
fTItmPlay = new ToolItem(fToolBar, SWT.NONE);
fTItmPlay.setImage(PluginImages.PLAY_IMG.createImage());
fTItmPlay.setToolTipText("ビデオ再生");
fTItmPlay.addSelectionListener(new SelectionAdapter() {
@Override
publicvoid widgetSelected(SelectionEvent e) {
if (fVideo != null) {
play(fVideo, 0, false);
}
}
});
fTItmStop = new ToolItem(fToolBar, SWT.NONE);
fTItmStop.setImage(PluginImages.STOP_IMG.createImage());
fTItmStop.setToolTipText("ビデオ停止");
fTItmStop.addSelectionListener(new SelectionAdapter() {
@Override
publicvoid widgetSelected(SelectionEvent e) {
stop();
}
});
}

// 再生位置コントローラを作成する.
privatevoid createPositionControl(Composite composite) {

fProgressBar = new ProgressBar(composite, SWT.SMOOTH);
GridData gd = new GridData();
gd.horizontalAlignment = SWT.LEFT;
gd.verticalAlignment = SWT.CENTER;
gd.widthHint = 200;
gd.heightHint = 10;
gd.horizontalSpan = 1;
gd.verticalSpan = 1;
fProgressBar.setLayoutData(gd);
fProgressBar.setMaximum(MAX_POSITION);
fProgressBar.setMaximum(0);

fProgressBar.addMouseListener(new MouseAdapter() {
@Override
publicvoid mouseDown(MouseEvent e) {
setPos(e.x);
}
});
}

// 再生位置を設定する.
privatevoid setPos(int x) {
float tx = fProgressBar.getSize().x;
float pos = (float) x / tx;
fMediaPlayer.setPosition(pos);
}

// vlcj メディアプレーヤーを作成する.
privatevoid createPlayer(Composite composite) {
final Display display = composite.getDisplay();

composite.setLayout(createGridLayout(1, false, 0, 0));

fPlayerPanel = new Composite(composite, SWT.EMBEDDED | SWT.NO_BACKGROUND);
fVideoFrame = SWT_AWT.new_Frame(fPlayerPanel);
fVideoCanvas = new Canvas();
fVideoFrame.add(fVideoCanvas);
fPlayerPanel.setVisible(true);

GridData gd = new GridData();
gd.grabExcessHorizontalSpace = true;
gd.grabExcessVerticalSpace = true;
gd.horizontalAlignment = SWT.FILL;
gd.verticalAlignment = SWT.FILL;
fPlayerPanel.setLayoutData(gd);

fMediaPlayerFactory = new MediaPlayerFactory("--no-video-title-show");
fMediaPlayer = fMediaPlayerFactory.newEmbeddedMediaPlayer();
fVideoSurface = fMediaPlayerFactory.newVideoSurface(fVideoCanvas);
fMediaPlayer.setVideoSurface(fVideoSurface);

fMediaPlayer.addMediaPlayerEventListener(new MediaPlayerEventAdapter() {
@Override
publicvoid finished(MediaPlayer mediaPlayer) {
// 終了時の処理
if (display.isDisposed())
return;

display.asyncExec(new Runnable() {
@Override
publicvoid run() {
fProgressBar.setSelection(0);
//TODO 次のビデオを再生
}
});
super.finished(mediaPlayer);
}

@Override
publicvoid positionChanged(MediaPlayer mediaPlayer,
finalfloat newPosition) {
if (display.isDisposed())
return;

display.asyncExec(new Runnable() {
@Override
publicvoid run() {
float pos = newPosition * MAX_POSITION;
if (!fProgressBar.isDisposed()) {
fProgressBar.setSelection((int) pos);
}
}
});
super.positionChanged(mediaPlayer, newPosition);
}
});

}

private String getVideoFilePath(IVideo v) {
String fpath = v.getParentpath();
String vkey = v.getKey();
fpath = fpath + "/" + vkey;
return fpath;
}

// 全画面表示を行う
publicvoid fullScreen() {
fFullscreen = !fFullscreen; // 画面モードを設定
Shell shell = fToolBarPanel.getShell();
shell.setFullScreen(fFullscreen);
}

privatevoid play(IVideo video, double dpos, boolean first) {
fVideo = video;
String filePath = getVideoFilePath(video);
fPlayerPanel.setVisible(false);
System.out.println("filepath="+filePath);
fMediaPlayer.playMedia(filePath);
if (dpos != -1.0) {
fMediaPlayer.setPosition((float) dpos);
}
setVideoTitle(video);

try {
Thread.sleep(800);
} catch (InterruptedException e) {
}
fPlayerPanel.setVisible(true);
setToolItemEnabled(true);
}

privatevoid setVideoTitle(IVideo video) {
lblTitle.setText(video.getGroup()+" - "+video.getTitle());
}

privatevoid setToolItemEnabled(boolean enable) {
fTItmStop.setEnabled(enable);
fTItmPlay.setEnabled(!enable);
}

publicvoid play(IVideo video, double dpos) {
play(video, dpos, true);
}

publicvoid stop() {
fToolBarPanel.setVisible(true);
fMediaPlayer.stop();
fProgressBar.setSelection(0);
setToolItemEnabled(false);
}

publicvoid shutdown() {
stop();
}
private GridLayout createGridLayout(int numCols, boolean makeColEqual,
int margin, int spacing) {
GridLayout layout = new GridLayout(numCols, makeColEqual);
layout.horizontalSpacing = spacing;
layout.verticalSpacing = spacing;
layout.marginWidth = margin;
layout.marginHeight = margin;
return layout;
}
}

パートとビューをリンクする
前回同様、Application.e4xmiを開き、ビデオーパートを選択します。Class URI フィールドに "bundleclass://com.itrane.myvideo/com.itrane.myvideo.views.VideoView" を設定して、ビデオパートとビデオビューをリンクします。

アプリケーションの実行
com.itrane.myvideo.product.launch を使って、アプリケーションを起動します。ビデオビューは表示されますが、まだギャラリーからビデオを選択してもビデオは再生されません。

選択サービスを使って、選択したビデオをビデオビューに渡す
GalleryView を次のように修正してください:
publicclass GalleryView {
// ...

@Inject
privateESelectionService zSelectionService;

// ...

privatevoid createGallery(Composite composite) {
//...

//項目をダブルクリックしたときの処理
fGallery.addMouseListener(new MouseAdapter() {
@Override
publicvoid mouseDoubleClick(MouseEvent e) {
GalleryItem item = fGallery.getItem(new Point(e.x, e.y));
if (item != null) {
String groupKey = item.getParentItem().getText();
int selectIndex = item.getParentItem().indexOf(item);
IVideo selectVideo = fVideoMap.get(groupKey).get(selectIndex);
zSelectionService.setSelection(selectVideo);
} else {
GalleryItem group = fGallery.getGroup(new Point(e.x, e.y));
if (!group.isExpanded()) {
group.setExpanded(true);
} else {
group.setExpanded(false);
}
}
}
});
//...
}
//...
}
@Inject アノテーションにより、選択サービス(ESelectionService) が、フィールド zSelectionService にセットされます。ギャラリー項目をダブルクリックすると、ESelectionService#setSelection() により選択ビデオがサービスに一旦セットされます。選択サービスから、選択通知を受け取れるようにビデオビューを次のように修正します:
publicclass VideoView {
// ...

/*
* GalleryView で選択サービスにセットされたIVideo をビデオビューにセットする。
*/

@Inject
privatevoid selectVideo(
@Optional @Named(IServiceConstants.ACTIVE_SELECTION) IVideo video) {
if (video != null) {
fVideoViewer.play(video, 0);
}
}
}

selectVideo() メソッドでは、選択オブジェクトのタイプが IVideo の場合、@Named パラメータで現在の選択を取得します。そして、ビデオビューアの play() メソッドを呼び出して、ビデオを再生します:

次回はCSS を使って外観を改善する方法を試します。

Eclipse 4 アプリケーションの作成 (4) CSSテーマ

$
0
0
E4 では、CSSを使ってアプリケーションの外観を柔軟に設定できます。 今回は、アプリケーションに CSS テーマを適用する方法を試してみることにします。 ただ残念なことに、今回UIとして使用した、Nebula Gallery Widget も vlcj も、この方法で簡単にスタイルを設定することができませんでした。 そのため、この方法の利点をあまり実感できないサンプルとなってしまいました。

簡単なスタイルシートの作成
com.itrane.myvideo プラグインの css フォルダに、以下の2つの CSS ファイルを作成します。 2つの違いは背景色だけです。
blue.css:
Gallery {
background-color: #5b99d6;
}
.MPart {
background-color: #5b99d6;
}
.MPartLabel {
color: #ffffff;
font: 'arial'11px;
font-weight: bold;
}

red.css:
Gallery {
background-color: #cc7665;
}
.MPart {
background-color: #cc7665;
}
.MPartLabel {
color: #ffffff;
font: 'arial'11px;
font-weight: bold;
}

テーマの定義
plugin.xml を開いて、拡張ページで、org.eclipse.e4.ui.css.swt.theme 拡張ポイントを追加して、上記の2つの CSS に対応するテーマ(テーマ青とテーマ赤)を登録します。 この拡張ポイントを使うには、依存関係ページで、必須プラグインとして org.eclipse.e4.ui.css.swt.theme を追加しておく必要があります。修正した plugin.xml は次のようになります:
<?xml version="1.0" encoding="UTF-8"?>
<plugin>
<extension
id="product"
point="org.eclipse.core.runtime.products">
<product
name="com.itrane.myvideo"
application="org.eclipse.e4.ui.workbench.swt.E4Application">
<property
name="appName"
value="com.itrane.myvideo">
</property>
<property
name="applicationXMI"
value="com.itrane.myvideo/Application.e4xmi">
</property>
<property
name="cssTheme"
value="blue.theme">
</property>
</product>
</extension>
<extension
point="org.eclipse.e4.ui.css.swt.theme">
<theme
basestylesheeturi="css/blue.css"
id="blue.theme"
label="テーマ青">
</theme>
<theme
basestylesheeturi="css/red.css"
id="red.theme"
label="テーマ赤">
</theme>
</extension>
</plugin>

テーマ変更のためのコマンド、ハンドラ、メニューの追加
Application.e4xmi を開いて、パラメータを持つコマンドを追加します:
コマンド:(Id:command.switchTheme、Name:switchTheme)
パラメータ:(Id:commandparameter.themeId、Name:themeId)

このコマンドに対応するハンドラを追加します:
ハンドラ:(Command:switchTheme - command.switchTheme)

ハンドラの "Class URI" をクリックして、com.itrane.myvideo.handlers パッケージに SwitchThemeHandler クラスを作成します:
publicclass SwitchThemeHandler {
@Execute
publicvoid execute(
@Named("commandparameter.themeId") String themeId,
IThemeEngine engine,
EPartService service) {
MPart galleryPart = (MPart)service.findPart("com.itrane.myvideo.GalleryPart");
MPart videoPart = (MPart)service.findPart("com.itrane.myvideo.VideoPart");
if (galleryPart!=null && videoPart!=null) {
if (galleryPart.getObject() instanceof GalleryView &&
videoPart.getObject() instanceof VideoView) {
engine.setTheme(themeId,true);
GalleryView galleryView = (GalleryView)galleryPart.getObject();
VideoView videoView = (VideoView)videoPart.getObject();
galleryView.setGroupTitleBackgroud();
videoView.setCanvasBackground();
}
}
}
}

@Execute アノテーションが付加された execute メソッドが実行されるときに、パラメータIdが "commandparameter.themeId" であるパラメータの値が themId にセットされます。 依存性注入により、IThemeEngine と EPartService もセットされます。 渡された themeId を使って、IThemeEngine#setTheme メソッドにより、指定テーマに変更します。 上述したように、Nebula Widget と vlcj プレーヤーは、CSSの適用だけでは、完全にスタイルを制御できません。 よって、パートサービス (EPartService) を使って、要素IDから、ギャラリーパートと、ビデオパートを検索し、MPart#getObject で、リンクされているビューを取得します。 最終的にそれらのビューのメソッドを呼び出して、ギャラリーのグループタイトルとプレーヤーキャンバスの背景色を変更します。 ここでは、パートサービスの使い方を見るため、かなり分かりにくいコードになってしまいましたが、イベントブローカーを使えばもっとすっきりしたコードになります。これについてはまた後で説明します。

ギャラリービューに、ハンドラから呼ばれる、setGroupTitleBackgroud() メソッドを追加します:
publicvoid setGroupTitleBackgroud() {
fGalleryGroupRenderer.setTitleBackground(
createTitleBackground(fComposite.getBackground()));
}

private Color createTitleBackground(Color color) {
int r = color.getRed() < 10 ? 0 : color.getRed() - 10;
int g = color.getGreen() < 10 ? 0 : color.getGreen() - 10;
int b = color.getBlue() < 10 ? 0 : color.getBlue() - 10;
Display display = Display.getCurrent();
returnnew Color(display, new RGB(r,g,b));
}
ギャラリーのグループタイトルは CSS で設定された背景色よりも若干暗めの色を設定しています。
また、テーマ変更時だけでなく、アプリケーションの起動時にも、初期テーマの背景色がグループタイトルに反映されるように、ギャラリービューの createGallery メソッドを以下のように修正します:
privatevoid createGallery(Composite composite) {
// ...

// グループレンダラーの作成
fGalleryGroupRenderer = new DefaultGalleryGroupRenderer();
fGalleryGroupRenderer.setMaxImageWidth(60);
fGalleryGroupRenderer.setMinMargin(0);
fGalleryGroupRenderer.setTitleForeground(
ResourceManager.getColor(SWT.COLOR_WHITE));
setGroupTitleBackgroud();
// ...
}

同じく、ビデオビューに setCanvasBackground() メソッドを追加します:
publicvoid setCanvasBackground() {
fVideoViewer.setCanvasBackground();
}

さらに、VideoViewer に 以下のメソッドを追加します:
publicvoid setCanvasBackground() {
Color color = this.getBackground();
java.awt.Color jcolor = new java.awt.Color(color.getRed(),
color.getGreen(), color.getBlue());
fVideoCanvas.setBackground(jcolor);
}

こちらも起動時に反映されるように、VideoViewer の createPlayer メソッドを以下のように修正します:
privatevoid createPlayer(Composite composite) {
// ...

setCanvasBackground();
}
テーマ変更のためのメニューを追加します:
メインメニューに Menu 要素(Label: テーマ)を追加して、子要素として、2つの HandledMenuItem 要素を追加して、それぞれにパラメータを追加します。
1つ目の HandledMenuItem:(Label: テーマ赤, Command:switchTheme - command.switchTheme)
 パラメータ:(Name:commandparameter.themeId, Value:red.theme)
2つ目の HandledMenuItem:(Label: テーマ青, Command:switchTheme - command.switchTheme)
 パラメータ:(Name:commandparameter.themeId, Value:blue.theme)

上記のメニューを選択すると、テーマ変更コマンドに対応するハンドラが実行され、ハンドラの execute メソッドにパラメータの値(red.theme または blue.theme) が渡されます。そして、それぞれのテーマId に対応する CSS(red.css または blue.css) が適用されます。アプリケーションを実行すると、最初のテーマのテーマ青が適用されます:

メニューから、テーマ > テーマ赤を選択して、テーマを変更します:

次回は、ビデオのトップディレクトリを変更、保存するために、E4 プリファレンスを使用します。

Eclipse 4 アプリケーションの作成 (5) CSSテーマ

$
0
0
前回、CSSを使ってスタイルを設定する方法を試しましたが、一番前面の UI コンポーネントである Nebula Gallery Widgetvlcjプレーヤーの両方とも、この方法では直接スタイル設定ができなかったため、期待したような効果が得られませんでした。広い領域を占めるギャラリーの背景にグラデーションを設定しようと色々と試行錯誤したのですが思うようになりませんでした。仕方なく、ツールバーにグラデーションを設定してみました。
blue.css
Gallery {
background-color: #5b99d6;
}
.MPart {
background-color: #5b99d6;
}
.MPartSashContainer {
background-color: #4b79bf;
}
.MPartLabel {
background-color: #5b99d6#2b599f100%false;
color: #ffffff;
font: 'arial'11px;
font-weight: bold;
}
.MPartComposite {
background-color: #2b599f;
}
ToolBar {
background-color: #2b599f#5b99d6100%false;
}



red.css
Gallery {
background-color: #cc7665;
}
.MPart {
background-color: #cc7665;
}
.MPartSashContainer {
background-color: #ac5645;
}
.MPartLabel {
background-color: #cc7665#8c3625100%false;
color: #ffffff;
font: 'arial'11px;
font-weight: bold;
}
.MPartComposite {
background-color: #8c3625;
}
ToolBar {
background-color: #8c3625#cc7665100%false;
}



垂直方向のグラデーションを設定:
blue.css :
Gallery {
background-color: #5b99d6;
}
.MPart {
background-color: #5b99d6;
}
.MPartSashContainer {
background-color: #4b79bf;
}
.MPartLabel {
background-color: #2b599f#5b99d6100%;
color: #ffffff;
font: 'arial'11px;
font-weight: bold;
}
.MPartComposite {
background-color: #2b599f#5b99d6100%;
}
ToolBar {
background-color: #2b599f#5b99d6100%;
}



CSSを使ったスタイル設定により、気に入ったテーマが作成できれば、それを簡単に他のプロジェクトで再利用できます。ただ、個別のウィジェットに対して、状態(有効、無効、選択など)ごとにスタイルを設定するなど、より複雑な設定を行うには、もっと掘り下げて調べる必要がありそうです。

Eclipse 4 アプリケーションの作成 (6) プリファレンス

$
0
0
ビデオ一覧を取得するフォルダはユーザー環境にあわせて自由に設定できる必要があります。 プリファレンスを使って、ユーザーが設定したビデオフォルダの位置を保存できるようにします。

設定ダイアログの作成
まず、ビデオフォルダを設定するダイアログを作成します:
publicclass PreferenceDialog extends TitleAreaDialog {

private String fRootDir;
private DirectoryFieldEditor fRootDirFld;
private FieldEditorPreferencePage fPage;

/**
* ダイアログの作成.
* @param parentShell
*/

public PreferenceDialog(Shell parentShell) {
super(parentShell);
}

/**
* ダイアログ・コンテンツの作成
* @param parent
*/

@Override
protected Control createDialogArea(Composite parent) {
getShell().setText("マイビデオ設定");
setTitle("マイビデオ設定");
setMessage("ご使用の環境にあわせて設定してください");

Composite area = (Composite) super.createDialogArea(parent);
GridLayout layout = (GridLayout) area.getLayout();
layout.marginTop = 10;
layout.marginBottom = 10;
layout.marginLeft = 10;
layout.marginRight = 10;
area.layout();

Preferences pref = ConfigurationScope.INSTANCE.getNode("com.itrane.myvideo");
fRootDir = pref.get("root.dir", "c:/$mydoc/video/music");

fPage = new FieldEditorPreferencePage(FieldEditorPreferencePage.GRID) {
@Override
publicvoid createControl(Composite parent) {
noDefaultAndApplyButton();
super.createControl(parent);
}

@Override
protectedvoid createFieldEditors() {
fRootDirFld = new DirectoryFieldEditor("root.dir",
"ビデオルートディレクトリ:", getFieldEditorParent());
fRootDirFld.setStringValue(fRootDir);
addField(fRootDirFld);
}

@Override
protectedvoid updateApplyButton() {
updateButtons(isValid());
super.updateApplyButton();
}
};
fPage.createControl(area);
Control pageControl = fPage.getControl();
pageControl.setLayoutData(new GridData(GridData.FILL_BOTH));
return area;
}

/**
* ボタン・バーの作成
* @param parent
*/

@Override
protectedvoid createButtonsForButtonBar(Composite parent) {
createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,
true);
createButton(parent, IDialogConstants.CANCEL_ID,
IDialogConstants.CANCEL_LABEL, false);
updateButtons(fPage.isValid());
}

/** ダイアログの初期サイズ */
@Override
protected Point getInitialSize() {
returnnew Point(450, 350);
}

@Override
protectedvoid okPressed() {
Preferences pref = ConfigurationScope.INSTANCE.getNode("com.itrane.myvideo");
pref.put("root.dir", fRootDirFld.getStringValue());
try {
pref.flush();
super.okPressed();
} catch (BackingStoreException e) {
}
}

privatevoid updateButtons(boolean isValid) {
Button okButton = getButton(IDialogConstants.OK_ID);
if (okButton != null) {
okButton.setEnabled(isValid);
}
}
}

OK ボタンが押されたとき、Prefences#put メソッドにより、キー "root.dir" に対してビデオフォルダを設定します。 Prefences#flush メソッドを実行して、プリファレンス・ストアに保存します。

設定ダイアログを開くためのコマンド、ハンドラ、メニューの追加
前回投稿のテーマ変更の場合と同じように、Application.e4xmi を開いて、コマンド(Id: command.openPreference)、ハンドラ(Command: openPreference) を追加します。 このハンドラを実行するメニュー項目(Label: 設定, Command: openPreference) も追加します(下図):

さらに、変更したディレクトリの内容でギャラリーを更新する必要があります。 GalleryView に次のメソッドを追加します:
publicvoid initGallery() {
fGallery.clearAll();
initData();
fGallery.setItemCount(fGalleryGroups.size());
}

追加したハンドラの "Class UI" をクリックして、com.itrane.myvideo.handlers パッケージに、次の内容の OpenPreferenceHandler クラスを作成します:
publicclass OpenPreferenceHandler {
@Execute
publicvoid execute(@Named(IServiceConstants.ACTIVE_SHELL) Shell shell,
EPartService partService) {
PreferenceDialog dlg = new PreferenceDialog(shell);
int result = dlg.open();
if (result==Window.OK) {
MPart part = (MPart)partService.findPart("com.itrane.myvideo.GalleryPart");
if (part!=null) {
if (part.getObject() instanceof GalleryView) {
GalleryView view = (GalleryView)part.getObject();
view.initGallery();
}
}
}
}
}

これで、ビデオディレクトリを変更すれば、変更されたビデオディレクトリのビデオ一覧でギャラリーの内容も更新されます。 最後に、GalleyView の initData メソッドを次のように修正して、起動時に、プリファレンスの設定内容を取得して、このディレクトリからビデオ一覧を取得するようにします:
privatevoid initData() {
String root = "C:/$mydoc/video/music";
Preferences pref = ConfigurationScope.INSTANCE.getNode("com.itrane.myvideo");
if (pref!=null) {
root = pref.get("root.dir", "C:/$mydoc/video/music");
}

fVideoHome = zVideoHomeFactory.createHome(
root,
new String[] {"mp4", "divx", "flv", "avi", "wmv"});
// ...
}
アプリケーションを起動して、メニューからウィンドウ > 設定を選択すれば、次の設定ダイアログが表示されます。

ディレクトリを変更してOKを押せば、設定が保存され、変更ディレクトリの内容にギャラリーが更新されます。 そして、次回起動時は保存されたディレクトリのビデオ一覧が表示されます。

Eclipse 4 アプリケーションの作成 (7) イベントブローカー

$
0
0
IEventBroker は低レベルのイベントメカニズムを提供します。 これを使って「Eclipse 4 アプリケーションの作成 (4)」の SwitchThemeHandler を書き換えます:
publicclass SwitchThemeHandler {
@Execute
publicvoid execute(
@Named("commandparameter.themeId") String themeId,
IThemeEngine engine,
IEventBroker eventBroker) {
if (eventBroker!=null && engine!=null) {
engine.setTheme(themeId,true);
eventBroker.post("themeid", themeId);
}
}
}
これで、以前のコードに比べてかなりすっきりしたと思います。依存性注入により、パラメータに IEventBroker と IThemeEngine のインスタンスがセットされます。IThemeEngine#setTheme によりテーマを変更した後、IEventBroker#post メソッドを使ってイベントを送ります。イベントバスへは、任意のオブジェクトを送ることができます。

さらに GalleryView に以下のメソッドを追加して、イベントを受け取れるようにします:
    @Inject
publicvoid receiveEvent(
@Optional @UIEventTopic("themeid") final String themeId) {
if (themeId!=null) {
setGroupTitleBackgroud();
fGallery.setItemCount(fGalleryGroups.size());
}
}
これにより、テーマ変更後に、直接 CSS でスタイル変更ができないギャラリーグループの背景色を変更できるようになります。

同じように、VideoView にも以下のメソッドを追加します:
    @Inject
publicvoid receiveEvent(
@Optional @UIEventTopic("themeid") final String themeId){
if (themeId!=null) {
fVideoViewer.setCanvasBackground();
}
}
ビデオビューアのキャンバスもスタイルが直接適用できないため、fVideoViewer.setCanvasBackground() により背景色を設定します。

「Eclipse 4 アプリケーションの作成 (6)」の OpenPreferenceHandlerではプリファレンスによりビデオフォルダの設定/変更を行いました。 その処理では、フォルダ変更時に GyalleryView の initGallery メソッドを呼んで変更を反映しましたが、これもイベントブローカーを使った方が簡単になります。

Eclipse 4 アプリケーションの作成 (8) ポップアップメニューの追加

$
0
0
ギャラリービューでグループやギャラリーアイテムを選択してポップアップメニューを表示させる例を示します。
Application.e4xmi を開いてください。まずコマンド要素(Id : playVideo)を追加します:

次に、ハンドラ要素(Command : playVideo) を追加します:

"Class URI" をクリックして、対応するハンドラクラス com.itrane.myvideo.handlers.PlayVideoHandler を作成します:
publicclass PlayVideoHandler {
@Execute
publicvoid execute() {
// TODO ビデオを再生
System.out.println("play video");
}

}
さらに、ギャラリーパートの Menus ノードにポップアップメニュー要素(Id: GalleryView.popupmenu)を追加します:

最後に、このポップアップメニューにメニュー項目(Command: playVideo) を追加します:

モデルに追加したポップアップメニューをギャラリービューで表示させるには、GalleryView を次のように修正します:
publicclass GalleryView {

//...

@Inject
private EMenuService zMenuService;

//...

privatevoid createGallery(Composite composite) {
//...

if (zMenuService!=null) {
zMenuService.registerContextMenu(fGallery, "GalleryView.popupmenu");
}
}
//...
}

注入されたメニューサービスを使って、Gallery コンポーネントに上述のポップアップメニューを登録します。"GalleryView.popupmenu" はポップアップメニューの Id です。

これで、ギャラリー項目を選択して、ポップアップメニューを表示できます。

Eclipse 4 アプリケーションの作成 (9) 既存モデルの拡張

$
0
0
今回は、別のプラグインで既存のアプリモデルを拡張する方法を説明します。前回はギャラリービューでポップアップメニューを表示する例を示しました。このポップアップメニューに新しいメニューを追加する例を説明します。拡張されるモデルの要素(この例ではポップアップメニュー)はユニークなIDを持っていることが必要です。

ステップ1:新規プラグインの作成
com.itrane.myvideo.search プラグインを作成してください。通常のプラグインですが、Activator クラスは不要です。
MANIFEST.MF の依存関係ページで以下のプラグインを追加してください:

ステップ2:モデルフラグメントの作成
新規プラグインに、モデルフラグメント(flagment.e4xmi) を作成します。

フラグメントは小さなアプリケーションモデルです。 アプリモデルに追加できる要素は何でも追加できます。

ステップ3:モデル要素の追加
前回ポップアップメニューを追加したときと同じように、コマンド要素、ハンドラ要素、メニュー項目要素を追加します。 ただし今回は flagment.e4xmiに追加します。

コマンドの追加:
flagment.e4xmi を開いて、Model Flagments ノードにモデルフラグメントを追加します。 追加されたフラグメントの要素ID(Element Id)ではメインのアプリケーションモデルのどの要素(例えばメインメニュー)が拡張されるのかを指定します。 "Find..." ボタンを押して、Application要素 の Id (com.itrane.myvideo.applicatio) を選択します(下図):

次のフィーチャー名(Featurename) には、追加要素の EMF 参照を指定します。 "Find..." を押して、commands を選択します(下図):

基本のアプリモデルには最初から Commands ノードがあり、前回はそれに Command を追加しました。 ここまでの処理はその Commands ノードを指定するものです。 そして、最後にこの追加フラグメント(commands)に子要素として、Command (Id: webSearch, Name: webSearchCommand) を追加します(下図):

ハンドラの追加:
 同様にして、ハンドラ要素を追加します。
 ・要素ID: com.itrane.myvideo.application
 ・フィーチャー名: handlers
 ・追加する子要素: Handler

対応するハンドラクラスも作成します。

メニュー項目の追加:
 前回追加したポップアップメニューにメニュー項目を追加します。
 ・要素ID:GalleryView.popupmenu
 ・フィーチャー名: children
 ・追加する子要素: HandledMenuItem
ステップ4:flagment.e4xmi の登録
com.itrane.myvideo.search プラグインの plugin.xml を開いて、flagment.e4xmi を登録します。
<extension
id="id1"
point="org.eclipse.e4.workbench.model">

<fragment
uri="fragment.e4xmi">

</fragment>
</extension>

ステップ5:製品構成に新規プラグインを追加
以上の1~4の作業が終わったら、新しく作成した com.itrane.myvideo.search プラグインを 製品構成(com.itrane.myvideo.product) の依存関係ページで追加します。それにともなって起動構成も修正してください。

実行して新しくメニューが追加されていることを確認します。


新しく拡張のためのプラグインを作成するには、それなりに手間が掛かりますが、一旦完成した汎用的な拡張プラグインを使って、他の基本モデルを拡張するのは簡単です。

OSX でのRCPアプリケーション開発

$
0
0
最近、Mac Book Air を使い始めました。 これまで Apple PC を使ったことはありませんが、どのインストールアプリも違和感なく使えて、非常に快適です。 タッチパッドの2本指スクロールや2本指タップも一旦慣れてしまうと、Windows ノートPCで使えないのがかえって不便に感じたりしています。 動作も持ち運びも軽く、バッテリは長持ち、とても気に入ってます(Windows ノートはパワー重視で買ったため、重さは2倍以上、バッテリ時間は3分の1なので)。

しかしアプリ開発のために、最もよく使うアプリは Eclipse です。 当然最も多い操作は編集作業ですが、キーボードの操作もタッチパッドによる操作も、今まで無意識にできていたことが意識しないとできないので、かなり作業効率が悪くなっています。

もうひとつの問題が互換性です。 Java で開発したアプリはマルチプラットフォームで動作するはずですが、今まで Windows 以外での使用を想定して開発したことがありませんでした。 そのため、DropBox上 のリポジトリからソースコードを Mac へインポートして、そのまま動作したときには今更ながら感激でした。

しかし、これはたまたまだったようで、それではと思い別のコードをインポートしてみると、少しずつ問題にぶつかっています。

Java バージョン
これは同じプラットフォームでも考慮する必要があります。 できるだけ最新の機能を使いたいのですが、ユーザー環境を考慮して、Java5, Java6 コードで開発するのが無難です。 OSX では公式バージョンは Java 6 のようです。 OSX版の OpenJDK 7 もリリースされていますが、私が試したところでは、7u6 のSWT_AWT ブリッジを使った処理はうまく動作しませんでした。 SWT_AWT ブリッジは、vlcj を使ったアプリ開発で必要ですが今のところ OpenJDK 7 では使用できません。 とにかくMac では、実績があり、ネット上に情報が多い古いバージョンを使用する方が安全のようです。

開発ツール、ライブラリ
開発には、Eclipse 以外にも様々なツールやライブラリを使用します。Mac 環境に不慣れなため、便利なツールやハード関連のライブラリで、Windows と同等のものを見つけるのは、楽しみでもありますがなかなか大変です。

プラットフォーム依存
ファイルシステムにアクセスする場合、Eclipse の定数やシステムプロパティを使って、互換性に考慮している場合は問題ありませんが、Windows しか使わない前提でついついプラットフォーム依存(絶対パスやエンコードなど)のコードを書いていたことに気付かされます。

プラグイン?DB?
EclipseLink の JPQLでもおかしなことがありました。 Windows では問題なく動作していた Select 文が Mac で実行すると1件も結果を返しません。条件を含まない場合は同じように全件返すのですが、条件を入れるとだめ。 少しだけネイティブクエリーも試しましたが結果は同じでした。 時間がなくて、MySQL でしか試していないので、他のデータベースで同じ結果になるかはまだ不明です。(※更新:クライアント側がUTF-8に設定されていませんでした。 慣れないOSXで設定ファイルをエディタで修正したくなくて、Workbench を使ったのが間違いだったようです。デフォルトを UTF-8にして欲しいものです。)


まだまだ開発は Windows 中心なのですが、これからは、Mac 環境も考慮しなくては・・。

クラウドからデータを取得する (1) Evernote OAuth 認証

$
0
0
最近はモバイル機器(特にタブレット)を使う機会が多くなりましたが、その場合データ保存にはほとんどクラウドサービス(主に Evernote, Google Drive, DropBoxなど) を利用しています。 特に Evernote は、簡単なメモ、参照したWEBページのコピー、写真や、音声などを簡単に保存できて非常に便利です。 RCP/RAPアプリケーションからこれらのデータにアクセスする基本的な手順を色々と試してみたので紹介したいと思います。 これらのクラウドサービスの多くはユーザーリソースへのアクセスに OAuth 認証を使用しています。 今回の投稿では、デスクトップアプリケーションで、 OAuth 認証のアクセストークンを取得するところまでを説明します。 実際にデータを取得する処理は次回の投稿で紹介します。

事前の準備
Evernote 連携アプリケーションを開発するには、APIキーの取得、SDKのダウンロード、サンドボックス環境(製品環境とは独立した実験用)のユーザー登録などが必要です。 詳しい内容は、Evernote Developersを参照してください。

Evernote OAuth 認証の手順
OAuth 認証のおおまかな流れは以下のとおりです:
  1. リクエストトークンの要求:
    アプリはサービスプロバイダから取得したコンシューマーキー、コンシューマーシークレットを使ってリクエストトークンを要求する。
  2. リソース所有者の承認:
    サービスプロバイダの認証ページでユーザーにアプリのアクセスを承認してもらう。
  3. アクセストークンの要求:
    2で承認された場合はアクセストークンを取得する。
  4. 取得したアクセストークンを使ってユーザーデータにアクセスする。
※詳しい説明は Evernote Developers の認証の説明を参照してください。



新規プロジェクトの作成
プロジェクト名: e4.oauth.example
E4テンプレートを使用して作成します(以前のポスト「Eclipse 4 アプリケーションの作成」を参照してください)。


(必須ライブラリのセットアップ)
プロジェクトルートに lib フォルダを作成してください。
GitHub の evernote-sdk-java/libから 次の2つの JAR ファイルをダウンロードして、プロジェクトの lib フォルダにコピーします:
  • evernote-api-1.22.jar
  • libthrift.jar
さらに、次の OAuth Java ライブラリを Maven リポジトリからダウンロードして lib フォルダにコピーします(必ず 1.3.1 をダウンロードしてください。1.3.0 ではうまく動作しませんでした):
  • scribe-1.3.1.jar
plugin.xml の "ランタイム" ページで上記の jar をクラスパスに追加します。


(依存プラグインの追加)
plugin.xml の "依存関係" ページで、以下のプラグインを追加して、Jetty サーバーを使用できるようにします。

  • org.eclipse.jetty.http
  • org.eclipse.jetty.server
  • org.eclipse.jetty.servlet
  • org.eclipse.jetty.util
  • org.eclipse.equinox.http.jetty
  • org.eclipse.equinox.http.registry
  • org.apache.commons.codec
  • org.apache.commons.logging
  • javax.servlet


(モデルの修正)
Application.e4xmi を開いて、アプリモデルを以下のように修正します。
Trimmed Window:bounds (0, 0, 750, 650)
パートの追加: 認証パート


サーブレットの作成
Jetty のスローガンの通り、アプリを Jetty サーバーにデプロイするのではなく、Jetty サーバーをアプリ内で起動して、OAuth 認証のためのサーブレットを実行します。

リクエストトークンを要求するためのサーブレット EvernoteOAuth を作成してください。
e4.oauth.example.servlet.EvernoteOAuth :
publicclass EvernoteOAuth extends HttpServlet {

privatestaticfinallong serialVersionUID = -164103688811676193L;
privatestaticfinal String CONSUMER_KEY = "KEY";
privatestaticfinal String CONSUMER_SECRET = "SECRET";
privatestaticfinal String CALLBACK_URL = "http://localhost:8080/callback";
privatestaticfinal String BASE_URL = "https://sandbox.evernote.com";

public EvernoteOAuth() {}
publicvoid doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//サンドボックス版または本番用の API の生成
Class providerClass = org.scribe.builder.api.EvernoteApi.Sandbox.class;
if (BASE_URL.equals("https://www.evernote.com")) {
providerClass = org.scribe.builder.api.EvernoteApi.class;
}

// OAuth 認証サービスの作成
Activator.service = new ServiceBuilder()
.provider(providerClass)
.apiKey(CONSUMER_KEY)
.apiSecret(CONSUMER_SECRET)
.callback(CALLBACK_URL)
.debug()
.build();
System.out.println("リクエストトークンを取得します...");
Token token = Activator.service.getRequestToken();
Activator.requestToken = token.getToken();
Activator.requestTokenSecret = token.getSecret();
String authorizationUrl = BASE_URL +
"/OAuth.action?oauth_token=" + Activator.requestToken;
response.sendRedirect(authorizationUrl);
}
}

Scribe ライブラリを使用することで、コードは非常に簡単になります。 上記サーブレットでは 上述した OAuth 認証手順の 1, 2 を行います。 手順をわかりやすくするために、例外時の処理は省略しています。 OAuth サービスには、コールバックURLを設定しています。こうすることで、2. のプロバイダの認証ページでリソース所有者の承認処理が終わると、コールバックURLで指定したサーブレットが実行されます。コードを簡単にするために、OAuth サービスや中間的に取得されるリクエストトークンなどはすべて Activator のstatic 変数に保存しています。
e4.oauth.example.Activator :
publicclass Activator implements BundleActivator {
//...
publicstatic OAuthService service;
publicstatic String accessToken;
publicstatic String requestToken;
publicstatic String requestTokenSecret;
//...
}

次はアクセストークンを要求するサーブレットを作成します。
e4.oauth.example.servlet.OAuthCallback:
publicclass OAuthCallback extends HttpServlet {

privatestaticfinallong serialVersionUID = -7486601429917362732L;

public OAuthCallback(){}

@Override
protectedvoid doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html; charset=UTF-8");
request.setCharacterEncoding("UTF-8");

String verifierCode = request.getParameter("oauth_verifier");
String tokenCode = request.getParameter("oauth_token");
Verifier verifier = new Verifier(verifierCode);
Token token = new Token(Activator.requestToken, Activator.requestTokenSecret);

System.out.println("アクセストークンとリクエストトークンと交換...");
Token aToken = Activator.service.getAccessToken(token, verifier);
Activator.accessToken = aToken.getToken();

response.getWriter().println("アクセストークンを取得しました! :<br />" +
Activator.accessToken);
}
}

サーバーの作成
上の2つのサーブレットを登録したサーバークラスを作成します。
e4.oauth.example.server.OAuthServer :
publicclass OAuthServer {

privatestatic Server server;

publicstaticvoid start() throws Exception {
server = new Server(8080);

ServletContextHandler context0 = new ServletContextHandler(ServletContextHandler.SESSIONS);
context0.setContextPath("/oauth");
context0.addServlet(new ServletHolder(new EvernoteOAuth()),"/*");

ServletContextHandler context1 = new ServletContextHandler(ServletContextHandler.SESSIONS);
context1.setContextPath("/callback");
context1.addServlet(new ServletHolder(new OAuthCallback()),"/*");

ContextHandlerCollection contexts = new ContextHandlerCollection();
contexts.setHandlers(new Handler[] { context0, context1 });
server.setHandler(contexts);

server.start();
}
}

"http://localhost:8080/oauth" で、1つ目のサーブレットの EvernoteOAuth が実行されます。 "http://localhost:8080/callback" でサーブレット OAuthCallback が呼び出されます。 実際には何らかのユーザーのアクション(ログインやデータ要求など)に応じてコマンドから EvernoteOAuth を呼び出すようにすべきです。 また取得したアクセストークンはユーザーに紐付けして、許可が取り消されるまで一定期間保存することも必要でしょう。 しかし、ここでは手順を分かりやすくするためにそれらの処理は省略しています。 アプリを起動すると、Activator の start( ) メソッド内で上記のサーバーを無条件に起動しています。
publicclass Activator implements BundleActivator {
//...
publicvoid start(BundleContext bundleContext) throws Exception {
Activator.context = bundleContext;

OAuthServer.start();
}
//...
}
そして、ブラウザビュー(後述)が表示されるときに1つ目のサーブレットを自動的に呼び出します。

ブラウザを持つビューの作成
ブラウザを持つだけの簡単なビューを作成してください。
e4.oauth.example.views.BrowserView :
publicclass BrowserView {

private Browser fBrowser;

public BrowserView() {
}

/**
* Create contents of the view part.
*/

@PostConstruct
publicvoid createControls(Composite parent) {
parent.setLayout(new FillLayout());
fBrowser = new Browser(parent, SWT.NONE);
fBrowser.setUrl("http://localhost:8080/oauth");
}
//....
}

アプリモデルを開いて、先に追加したブラウザパートとこのビューをリンクしてください。 ブラウザの  setURL("http://localhost:8080/oauth")メソッドにより、1つ目のサーブレットを呼び出します。

アプリケーションの実行
実行前に、製品構成ファイルを開いて、依存関係ページで、"Include optional dependencies ..." をチェックして、"Add Required Plug-ins" ボタンを押してください。
アプリケーションを実行すると、EvernoteOAuth サーブレットが実行されて、ブラウザ内にユーザーの認証ページが表示されます(下図)。

ここで、サンドボックス用に登録したユーザーIDとパスワードを入力すると、アプリケーション承認ページが表示されます(下図)。

許可を押すとコールバックURLにより、OAuthCallback サーブレットが実行されます。 ここでは取得したアクセストークンを表示しているだけです(下図)。


実際の処理では、取得したトークンをDBなどへ保存し、このトークンを使って必要な処理を行い、最後に別のビューへ移って結果等を表示することになるでしょう。

クラウドからデータを取得する (2) Evernote のクラウド API を使用する

$
0
0
前回の投稿では、Evernote の OAuth 認証で、アクセストークンを取得するところまでを紹介しました。 一旦トークンを取得できれば、クラウドAPIを使用して様々な処理を行うことができます。  https://github.com/evernote/evernote-sdk-java のサンプルをダウンロードして、APIの使用方法をすぐに試してみることができます。 前回作成したプロジェクト( e4.oauth.example ) の src フォルダに evernote-sdk-java/src 以下のパッケージをそのままコピーします。 次に、evernote-sdk-java/sample/client/EDAMDemo.javaを プロジェクトの e4.oauth.example パッケージにコピーしてください。
src/com 以下のパッケージと EDAMDemo.java をコピーした後の e4.oauth.example/src は次のようになります:



このデモクラスは、次のような処理を行います:
  • ノート一覧の取得
  • 新規ノートの作成
  • 条件にマッチするノートの検索
  • タグの更新
このデモクラスに、少しだけ修正を加えます。 EDAMDemo.java の main( ) メソッドを次のように変更します:
publicstaticvoid execute(String aToken) throws Exception {
authToken = aToken;

//...
}
execute( ) メソッドにはパラメータとして、前回処理で取得したアクセストークンが渡されます。 

次はこのアクセストークンを保持するために、元のソースコードの次の部分:
privatestaticfinal String authToken = "....";
を次のように変更します:
privatestatic String authToken;

createNoet( ) メソッドの画像ファイル名の指定も次のように変更します(新規ノートに加えたい適当な画像ファイルを指定してください)。
privatevoid createNote() throws Exception {
// ...

String fileName = "C:/$mydoc/picture/evernote_demo/enlogo.png";
// ...
}

前準備はこれだけです。

それでは、このデモクラスを実行するためのハンドラーを作成します。 まず、アプリモデルにコマンド( demoCommand )とハンドラを追加します(下図)。

そして、追加したハンドラにリンクする DemoHandler クラスを作成します。
e4.oauth.example.handlers.DemoHandler :
publicclass DemoHandler {
@Execute
publicvoid execute(@Named(IServiceConstants.ACTIVE_SHELL) Shell shell) throws Exception {
if (Activator.accessToken==null) {
MessageDialog.openError(shell, "EDAMDemo エラー",
"アクセストークンを取得してください");
return;
}

EDAMDemo.execute(Activator.accessToken);
}
}
Acitvator.accessToken に保持されたアクセストークンをパラメータとして、EDAMDemo クラスの execute( ) メソッドを実行しているだけです。
このハンドラを実行するために、アプリモデルのファイルメニューに「Evernote デモ」メニュー項目を追加して、Command 属性に、demoCommand 設定してください:

前回のシナリオではアプリの起動と同時に OAuth 認証サーブレットが実行されるため、まず認証ページで承認の処理を行います。 その処理を終えると取得されたアクセストークンが表示され、Activator.accessToken には有効なアクセストークンがセットされています。 そこで、"ファイル" メニューから "Evernote デモ" を実行します。

私のサンドボックス Evernote の初期状態は以下の状態でした (https://sandbox.evernote.com/):

"Evernote デモ" メニューの実行後の結果は次のようになります:
TestTag が作成され、1件のノート(タイトル: "EDAMDemo.java で作成したテストノート")が追加されています。APIのより詳しい使い方については、またいずれ報告します。

Eclipse 4, Eclipselink, Spring Data JPA (1)

$
0
0
以前紹介した「eclipselink を使う(1)~(8)」では、CRUD 操作や問い合わせ処理を自前で実装しました。 今回はそれらの処理をすべて Spring Data JPA に丸投げします。 Spring Data JPA を使う利点は、多くの開発者が使用しているポピュラーなフレームワークを利用することで、高品質のシステムが簡単に作成できることです。 ここでは Spring の次の2つのフレームワークを利用します。
  • Spring Data JPA (詳細はリファレンスガイドを参照してください)
    リレーショナルデータベースやNoSQLデータストアに対して統一したデータアクセスの機能を提供します。 
  • Spring DM (詳細はリファレンスガイドを参照してください)
    OSGi バンドル(Eclipse プラグイン)で Spring の機能を使用できるようにします。
    Spring DM は、OSGi バンドルの org.springframework.osgi.extenderを提供します。 このバンドルは、E4(RCP)アプリの各バンドルに対して、Spring活用バンドルを探し、アプリケーションコンテキストを作成します(バンドルの META-INF/spring フォルダ内に Spring 設定ファイルを作成した場合、エクステンダーはこのバンドルを Spring活用バンドルとして処理を行います)。 
アプリケーションの構成
このアプリケーションは次の4つのバンドルで構成されます:
  • e4.example.spring.jpa (Eclipse 4 アプリケーション)
    アプリケーションのエントリポイント、UIの構築
  • e4.example.spring.jpa.entity
    エンティティの定義
  • e4.example.spring.jpa.home
    Spring Data リポジトリ、データサービスインターフェース の定義
    eclipselink jpa の定義
  • e4.example.spring.jpa.home.impl
    データサービスの実装
さらに、ターゲットプラットフォームを設定するためのプロジェクトも作成します。
  • e4.example.spring.jpa.target (一般プロジェクト)
    ターゲットプラットフォームの定義

ターゲットプラットフォームの設定
このサンプルアプリケーションは、Spring DM, Spring Data JPA, Eclipselink などのたくさんの JAR に依存します。 これらの JAR をダウンロードするために、ターゲットプラットフォーム設定用プロジェクトを作成してください。
プロジェクト名  : e4.example.spring.jpa.target
プロジェクトタイプ:一般プロジェクト

作成したプロジェクトに、ダウンロード先のフォルダとして lib を作成してください。

そして、以下の内容の pom.xml を作成します:
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>
<groupId>fr.opensagres.samples</groupId>
<artifactId>fr.opensagres.samples.targetplatform</artifactId>
<packaging>pom</packaging>
<version>0.0.1-SNAPSHOT</version>

<build>
<plugins>
<plugin>
<!--すべての依存JARを /lib にコピーする -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.1</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>process-resources</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>lib</outputDirectory>
<overWriteReleases>true</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

<properties>
<spring-dm-version>1.2.1</spring-dm-version>
<slf4j-version>1.6.1</slf4j-version>
<org-aopalliance>1.0.0</org-aopalliance>
<spring-data-jpa>1.0.3.RELEASE</spring-data-jpa>
<jpa-version>1.1</jpa-version>
<eclipselink>2.4.0</eclipselink>
<commons-dbcp>1.2.2.osgi</commons-dbcp>
<database-mysql>5.1.19</database-mysql>
</properties>

<repositories>
<repository>
<id>spring-maven-milestone</id>
<name>Springframework Maven Repository</name>
<url>http://maven.springframework.org/milestone</url>
</repository>
<repository>
<id>com.springsource.repository.bundles.external</id>
<name>SpringSource Enterprise Bundle Repository - External Bundle Releases</name>
<url>http://repository.springsource.com/maven/bundles/external</url>
</repository>
<repository>
<id>EclipseLink Repo</id>
<url>http://download.eclipse.org/rt/eclipselink/maven.repo</url>
</repository>
</repositories>

<dependencies>
<!-- Spring DM -->
<dependency>
<groupId>org.springframework.osgi</groupId>
<artifactId>spring-osgi-extender</artifactId>
<version>${spring-dm-version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- API logger: Commons Logging required by Spring DM -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>com.springsource.slf4j.org.apache.commons.logging</artifactId>
<version>${slf4j-version}</version>
<scope>provided</scope>
</dependency>
<!-- Implementation logger: log4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>com.springsource.slf4j.log4j</artifactId>
<version>${slf4j-version}</version>
<scope>provided</scope>
</dependency>
<!-- OSGi AOP Alliance (need do that since we have exclude org.springframework) -->
<dependency>
<groupId>org.aopalliance</groupId>
<artifactId>com.springsource.org.aopalliance</artifactId>
<version>${org-aopalliance}</version>
</dependency>
<!-- Spring Data JPA -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>${spring-data-jpa}</version>
</dependency>
<!-- API JPA: annotation -->
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-jpa_2.0_spec</artifactId>
<version>${jpa-version}</version>
</dependency>
<!-- Implementation JPA: EclipseLink -->
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.jpa</artifactId>
<version>${eclipselink}</version>
</dependency>
<!-- Database Connection Pool -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>com.springsource.org.apache.commons.dbcp</artifactId>
<version>${commons-dbcp}</version>
</dependency>
<!-- Database Derby -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${database-mysql}</version>
</dependency>
</dependencies>
</project>

この pom.xml を右クリックして、ポップアップメニューから、"Run As" > "Maven build..." を選択します。 表示されたダイアログで Maven のゴールに "process-resources" を設定して実行してください。 これにより、必要な JARが指定したフォルダ(上の例では lib フォルダ)にダウンロードされます(下図)。  


次はこのプロジェクトに、ターゲット定義を作成してください。 ターゲット定義に ${eclipse_home} と、上記の lib フォルダを追加します(下図)。 そして、"Set As Target Platform" をクリックして、ターゲットプラットフォームを設定してください。



エンティティクラスの作成
新規プロジェクトを作成してください。
プロジェクト名 : e4.example.spring.jpa.entity
OSGi フレームワークを選択(下図):

Activator は生成しない(下図):

MANIFEST.MF を開いて次のインポートパッケージを追加してください:
Import-Package : javax.persistence;resolution:=optional

eclipselink を使う (1) エンティティの作成」では、簡単にするためにデータ処理関連の全てのクラスを com.itrane.mycontact.db プラグインに作成しました。 本来はデータ操作とは別にすべきものなので、今回は e4.example.spring.jpa.entity バンドルにはエンティティだけを作成します。 作成するエンティティはそのときと同じです。

  • AbstractPersistentEntity
  • Contact
    @Entity
    publicclass Contact extends AbstractPersistentEntity {
    privatestaticfinallong serialVersionUID = 1L;

    private String simei;
    private String yomi;
    private ContactType contactType;
    private String company;
    private String seibetu;
    private Date seinengappi;
    private String title;
    private String jobTitle;
    private String note;
    private String jpegString;
    private String username;
    private String googleid;
    private Map<String, Address> addresses = new HashMap<String, Address>();
    private Map<String, Phone> phones = new HashMap<String, Phone>();

    public Contact() {
    contactType = ContactType.NONE;
    seibetu = "";
    seinengappi = Calendar.getInstance().getTime();
    simei = "";
    yomi = "";
    company = "";
    title = "";
    jobTitle = "";
    note = "";
    username="";
    googleid="";
    jpegString="";
    }

    public Contact(Contact c) {
    contactType = c.getContactType();
    seibetu = c.getSeibetu();
    seinengappi = c.getSeinengappi();
    simei = c.getSimei();
    yomi = c.getYomi();
    company = c.getCompany();
    title = c.getTitle();
    jobTitle = c.getJobTitle();
    note = c.getNote();
    username =c.getUsername();
    googleid = c.getGoogleid();
    jpegString = c.getJpegString();
    }

    @OneToMany(cascade=CascadeType.ALL)
    @MapKeyClass(String.class)
    @MapKeyColumn(table="CONTACT_ADDRESS")
    @JoinTable(name="CONTACT_ADDRESS")
    public Map<String, Address> getAddresses() {
    return addresses;
    }

    publicvoid setAddresses(Map<String, Address> addresses) {
    this.addresses = addresses;
    }

    @OneToMany(cascade=CascadeType.ALL)
    @MapKeyClass(String.class)
    @MapKeyColumn(table="CONTACT_PHONE")
    @JoinTable(name="CONTACT_PHONE")
    public Map<String, Phone> getPhones() {
    return phones;
    }

    publicvoid setPhones(Map<String, Phone> phones) {
    this.phones = phones;
    }

    @Column(length=20)
    public String getSimei() {
    return simei;
    }
    publicvoid setSimei(String simei) {
    this.simei = simei;
    }
    @Column(length=30)
    public String getYomi() {
    return yomi;
    }
    publicvoid setYomi(String yomi) {
    this.yomi = yomi;
    }
    @Enumerated(EnumType.STRING)
    @Column(length=10)
    public ContactType getContactType() {
    return contactType;
    }
    publicvoid setContactType(ContactType type) {
    this.contactType = type;
    }
    @Column(length=100)
    public String getCompany() {
    return company;
    }
    publicvoid setCompany(String company) {
    this.company = company;
    }
    @Column(length=10)
    public String getSeibetu() {
    return seibetu;
    }
    publicvoid setSeibetu(String seibetu) {
    this.seibetu = seibetu;
    }
    @Basic
    @Temporal(TemporalType.DATE)
    public Date getSeinengappi() {
    return seinengappi;
    }
    publicvoid setSeinengappi(Date seinengappi) {
    this.seinengappi = seinengappi;
    }
    @Column(length=50)
    public String getTitle() {
    return title;
    }
    publicvoid setTitle(String title) {
    this.title = title;
    }
    @Column(length=50)
    public String getJobTitle() {
    return jobTitle;
    }
    publicvoid setJobTitle(String jobTitle) {
    this.jobTitle = jobTitle;
    }
    public String getNote() {
    return note;
    }
    publicvoid setNote(String note) {
    this.note = note;
    }
    @Lob
    public String getJpegString() {
    return jpegString;
    }
    publicvoid setJpegString(String jpegString) {
    this.jpegString = jpegString;
    }
    @Column(length=30)
    public String getUsername() {
    return username;
    }
    publicvoid setUsername(String username) {
    this.username = username;
    }
    @Column(length=80)
    public String getGoogleid() {
    returnthis.googleid;
    }
    publicvoid setGoogleid(String googleid) {
    this.googleid = googleid;
    }
    //.... equals(), toString() は省略
    }
  • Address
  • Phone

最後に MANIFEST.MF でエクスポートパッケージを指定します:
Export-Package : e4.example.spring.jpa.entity

次回は、e4.example.spring.jpa.home プラグインを作成して、Spring Data JPA を使う場合の中心的な話題である、 リポジトリの作成や、Spring 設定ファイルについて説明します。

Eclipse 4, Eclipselink, Spring Data JPA (2)

$
0
0
Eclipse 4, Eclipselink, Spring Data JPA (1)」では、サンプルアプリケーションのためのターゲットプラットフォームの定義とエンティティの作成までを行いました。 今回はこのテーマの中心である Spring Data リポジトリの作成、Spring 設定ファイルの作成などを取り上げます。

サービス定義バンドルの作成
e4.example.spring.jpa.entity の場合と同様にして、OSGi バンドルの e4.example.spring.jpa.home を作成してください。

MANIFEST.MF を修正して、以下に示す必須バンドルとインポートパッケージを追加してください:
Require-Bundle : org.eclipse.persistence.jpa; bundle-version="2.4.0",
org.eclipse.persistence.antlr; bundle-version ="3.2.0",
org.eclipse.persistence.asm; bundle-version="3.3.1"
Import-Package : e4.example.spring.jpa.entity,
com.mysql.jdbc,
javax.persistence,
javax.persistence.criteria,
javax.persistence.metamodel,
javax.persistence.spi,
org.aopalliance.aop,
org.apache.commons.dbcp,
org.springframework.aop,
org.springframework.aop.framework,
org.springframework.data.domain,
org.springframework.data.jpa.repository,
org.springframework.data.jpa.repository.support,
org.springframework.data.repository,
org.springframework.data.repository.core.support,
org.springframework.orm.jpa,
org.springframework.orm.jpa.vendor; version="3.0.5.RELEASE"

リポジトリの作成
プロジェクトに新規パッケージ e4.example.spring.jpa.repository を作成して、前回作成したそれぞれのエンティティに対応するリポジトリを作成します。
e4.example.spring.jpa.repository.ContactRepository :
publicinterface ContactRepository extends PagingAndSortingRepository<Contact, Long> {

//連絡先タイプによる検索
List<Contact> findByContactType(ContactType contactType);
Page<Contact> findByContactType(ContactType contactType, Pageable pageable);

//氏名によるあいまい検索
List<Contact> findBySimeiLike(String simei);
Page<Contact> findBySimeiLike(String simei, Pageable pageable);
}

e4.example.spring.jpa.repository.AddressRepository :
publicinterface AddressRepository extends PagingAndSortingRepository<Address, Long> {

}

e4.example.spring.jpa.repository.PhoneRepository :
publicinterface PhoneRepository extends PagingAndSortingRepository<Phone, Long> {

}

データ処理サービスの定義
新規パッケージ e4.example.spring.jpa.home を作成して、上記のリポジトリを利用する上位のサービスをそれぞれ定義します。

e4.example.spring.jpa.home.IContactHome :
publicinterface IContactHome {

List<Contact> findAll();
Page<Contact> findAll(Pageable pageable);

List<Contact> findByContactType(ContactType contactType);
Page<Contact> findByContactType(ContactType contactType, Pageable pageable);

List<Contact> findBySimeiLike(String simei);
Page<Contact> findBySimeiLike(String simei, Pageable pageable);

Contact saveContact(Contact contact);
}

e4.example.spring.jpa.home.IAddressHome :
publicinterface IAddressHome {
List<Address> findAll();
Page<Address> findAll(Pageable pageable);

Address saveAddress(Address address);
}

e4.example.spring.jpa.home.IPhoneHome :
publicinterface IPhoneHome {
List<Phone> findAll();
Page<Phone> findAll(Pageable pageable);

Phone savePhone(Phone phone);
}

Spring  設定ファイルの作成
META-INF フォルダ下に spring フォルダを作成して、以下の内容の spring-context.xml を作成してください。
META-INF/spring/spring-context.xml :
<?xml version="1.0" encoding= "UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"xmlns:util="http://www.springframework.org/schema/util"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"
>


<bean id= "jpaDialect"class="org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect" />

<bean id= "dataSource"class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName"value="com.mysql.jdbc.Driver" />
<property name="url"value="jdbc:mysql://localhost/spring" />
<property name="username"value="demo" />
<property name="password"value="demo" />
<property name="initialSize"value="5" />
<property name="maxActive"value="50" />
</bean>

<bean id= "entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

<property name="persistenceUnitName"value="userUnit" />
<property name="dataSource"ref="dataSource" />
<property name="jpaDialect"ref="jpaDialect" />
<property name="jpaProperties">
<util:properties location="classpath:META-INF/config/jpa.properties" />
</property>

<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
<property name="databasePlatform"value="org.eclipse.persistence.platform.database.MySQLPlatform" />
</bean>
</property>

</bean>

<!-- Transaction Manager -->
<bean id= "transactionManager"class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory">

<property name="jpaDialect"ref="jpaDialect" />
</bean>

<tx:annotation-driven transaction-manager="transactionManager" />
<jpa:repositories base-package="e4.example.spring.jpa.repository">
<jpa:repository id= "contactRepository" />
<jpa:repository id= "addressRepository" />
<jpa:repository id= "phoneRepository" />
</jpa:repositories>
</beans>

この設定ファイルでは、データーソース、エンティティマネージャファクトリ、トランザクションマネージャを spring ビーンとして作成しています。 さらに JPAリポジトリのベースパッケージを指定して、ContactRepository, AddressRepository, PhoneRepository のインスタンスを生成し、指定した id で登録します。

上の設定ファイル内で参照している jpa.properties ファイルも作成してください。
META-INF/config/jpa.properties :
eclipselink.ddl-generation=update-tables
eclipselink.ddl-generation.output-mode= both
eclipselink.target-database=org.eclipse.persistence.platform.database.MySQLPlatform
eclipselink.weaving=false

作成したビーンを公開するために、以下の内容の spring-osgi-context.xml も作成してください。
META-INF/spring/spring-osgi-context.xml :
<?xml version="1.0" encoding= "UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:osgi="http://www.springframework.org/schema/osgi"
xsi:schemaLocation="http://www.springframework.org/schema/osgi
http://www.springframework.org/schema/osgi/spring-osgi-1.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
>


<osgi:service ref="dataSource"interface= "javax.sql.DataSource" />

<osgi:service ref="contactRepository"interface="e4.example.spring.jpa.repository.ContactRepository" />
<osgi:service ref="addressRepository"interface="e4.example.spring.jpa.repository.AddressRepository" />
<osgi:service ref="phoneRepository"interface="e4.example.spring.jpa.repository.PhoneRepository" />
</beans>

また、META-INF フォルダに persistence.xml も作成します。
 META-INF/persistence.xml :
<?xml version="1.0"  encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="2.0"xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
>


<persistence-unit name="userUnit"transaction-type="RESOURCE_LOCAL">
<class>e4.example.spring.jpa.entity.Address</class>
<class>e4.example.spring.jpa.entity.Contact</class>
<class>e4.example.spring.jpa.entity.Phone</class>
</persistence-unit>
</persistence>

Spring DM エクステンダーは、META-INF/spring フォルダの設定ファイルにより、e4.example.spring.jpa.home バンドルを Spring 活用バンドルとして処理します。 そして、設定ファイルの内容から、エンティティマネージャファクトリ、Spring DATA リポジトリのインスタンスを生成し、 OSGi レジストリに登録します。

次回は、e4.example.spring.jpa.home.impl プラグインを作成して、データ処理サービスを実装します。


Eclipse 4, Eclipselink, Spring Data JPA (3)

$
0
0
Eclipse 4, Eclipselink, Spring Data JPA (2)」では、Spring Data リポジトリを作成し、それらを OSGi サービスとして公開しました。 今回はこれらのリポジトリを利用するデーター処理サービスを作成します。

サービス実装バンドルの作成
e4.example.spring.jpa.entity バンドルと同じように OSGi バンドルの e4.example.spring.jpa.home.impl を作成します。


(インポートパッケージの指定)
MANIFEST.MF を修正して、インポートパッケージを追加してください:
Import-Package : e4.example.spring.jpa.entity,
e4.example.spring.jpa.home,
e4.example.spring.jpa.repository,
org.springframework.data.domain,
org.springframework.data.repository

(データサービスの実装)
パッケージ e4.example.spring.jpa.home.impl を作成して、以下のクラスを作成してください。
e4.example.spring.jpa.home.impl.ContactHome :
publicclass ContactHome implements IContactHome {

private ContactRepository contactRepository;

public ContactHome() {}

publicvoid setContactRepository(ContactRepository contactRepository) {
this.contactRepository = contactRepository;
}

public List<Contact> findAll() {
return (List<Contact>) contactRepository.findAll();
}

public Page<Contact> findAll(Pageable pageable) {
return contactRepository.findAll(pageable);
}

public List<Contact> findByContactType(ContactType contactType) {
return contactRepository.findByContactType(contactType);
}

public Page<Contact> findByContactType(ContactType contactType,
Pageable pageable) {
return contactRepository.findByContactType(contactType, pageable);
}

public List<Contact> findBySimeiLike(String simei){
simei = getCriteriaLike(simei);
return contactRepository.findBySimeiLike(simei);
}

public Page<Contact> findBySimeiLike(String simei, Pageable pageable) {
simei = getCriteriaLike(simei);
return contactRepository.findBySimeiLike(simei, pageable);
}

private String getCriteriaLike(String criteria) {
return criteria == null || criteria.length() < 1 ? "%" : criteria + "%";
}

public Contact saveContact(Contact contact) {
return contactRepository.save(contact);
}
}

e4.example.spring.jpa.home.impl.AddressHome :
publicclass AddressHome implements IAddressHome {

private AddressRepository addressRepository;

publicvoid setAddressRepository(AddressRepository addressRepository) {
this.addressRepository = addressRepository;
}

public List<Address> findAll() {
return (List<Address>) addressRepository.findAll();
}

public Page<Address> findAll(Pageable pageable) {
return addressRepository.findAll(pageable);
}

private String getCriteriaLike(String criteria) {
return criteria == null || criteria.length() < 1 ? "%" : criteria + "%";
}

public Address saveAddress(Address address) {
return addressRepository.save(address);
}
}

e4.example.spring.jpa.home.impl.PhoneHome :
publicclass AddressHome implements IAddressHome {

private AddressRepository addressRepository;

publicvoid setAddressRepository(AddressRepository addressRepository) {
this.addressRepository = addressRepository;
}

public List<Address> findAll() {
return (List<Address>) addressRepository.findAll();
}

public Page<Address> findAll(Pageable pageable) {
return addressRepository.findAll(pageable);
}

private String getCriteriaLike(String criteria) {
return criteria == null || criteria.length() < 1 ? "%" : criteria + "%";
}

public Address saveAddress(Address address) {
return addressRepository.save(address);
}
}

(エクスポートパッケージの指定)
Export-Package : e4.example.spring.jpa.home.impl

設定ファイルの作成
META-INF/spring/spring-context.xml :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
>


<bean id="contactHome"class="e4.example.spring.jpa.home.impl.ContactHome">
<property name="contactRepository">
<ref bean="contactRepository" />
</property>
</bean>
<bean id="addressHome"class="e4.example.spring.jpa.home.impl.AddressHome">
<property name="addressRepository">
<ref bean="addressRepository" />
</property>
</bean>
<bean id="phoneHome"class="e4.example.spring.jpa.home.impl.PhoneHome">
<property name="phoneRepository">
<ref bean="phoneRepository" />
</property>
</bean>
</beans>
データサービスクラス ...Home を Spring ビーンとして作成します。 フレームワークにより、プロパティ ...Repository に参照リポジトリが設定されます。 これによりホームクラスは処理をリポジトリに委譲することができます。

META-INF/spring/spring-osgi-context.xml :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:osgi="http://www.springframework.org/schema/osgi"
xsi:schemaLocation="http://www.springframework.org/schema/osgi
http://www.springframework.org/schema/osgi/spring-osgi-1.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
>


<!-- OSGiレジストリからリポジトリを参照 -->
<osgi:reference id="contactRepository"interface="e4.example.spring.jpa.repository.ContactRepository" />
<osgi:reference id="addressRepository"interface="e4.example.spring.jpa.repository.AddressRepository" />
<osgi:reference id="phoneRepository"interface="e4.example.spring.jpa.repository.PhoneRepository" />

<!-- OSGiレジストリで Home インスタンスを公開 -->
<osgi:service ref="contactHome"interface="e4.example.spring.jpa.home.IContactHome" />
<osgi:service ref="addressHome"interface="e4.example.spring.jpa.home.IAddressHome" />
<osgi:service ref="phoneHome"interface="e4.example.spring.jpa.home.IPhoneHome" />
</beans>

各データサービスクラスを OSGi サービスとして公開します。 他のバンドルはこのサービスを使ってデータベースにアクセスすることができます。

次回ポストでは、Eclipse 4 アプリケーションからこのサービスを使用する方法を説明します。

Eclipse 4, Eclipselink, Spring Data JPA (4)

$
0
0
今回は、OSGi サービスとして公開した ContactHome を Eclipse 4 アプリから利用する方法ついて説明します。

Eclipse 4 アプリケーションの作成
新規プロジェクトを作成します。テンプレートとして Eclipse 4 アプリケーションを選択してください。
※E4アプリの作成に関しては、「Eclipse 4 アプリケーションの作成 (1) 」を参照してください。



(インポートパッケージの指定)
MANIFEST.MF を修正して、以下のインポートパッケージを追加してください:
Import-Package: e4.example.spring.jpa.entity,
e4.example.spring.jpa.home,
e4.example.spring.jpa.home.impl,
org.osgi.framework;version="1.3.0",
org.springframework.beans;version="3.0.5.RELEASE",
org.springframework.beans.factory;version="3.0.5.RELEASE",
org.springframework.beans.factory.xml;version="3.0.5.RELEASE",
org.springframework.context;version="3.0.5.RELEASE",
org.springframework.context.support;version="3.0.5.RELEASE",
org.springframework.core.io;version="3.0.5.RELEASE",
org.springframework.data.domain;version="1.1.0.RELEASE"

(必須プラグインの追加)
MANIFEST.MF の "Dependencies" ページで、E4テンプレートにより自動的に追加された必須プラグイン に加えて、以下の2つのプラグインを追加します:
 org.eclipse.e4.ui.di;bundle-version="0.10.1",
org.springframework.osgi.extender;bundle-version="1.2.1"

Eclipse 4, Eclipselink, Spring Data JPA (1)」で説明したように、Spring DM の org.springframework.osgi.extender バンドルは、Spring 活用バンドル(このサンプルアプリの場合、e4.example.spring.jpa.home と e4.example.spring.jpa.home.impl )の設定ファイルを読み込んで、Spring Data リポジトリの生成やデータ処理サービスの公開等を行います。

(製品構成の修正)
製品構成 e4.example.spring.jpa.product を開き、"Dependencies"ページで "Include optional dependencies ..." をチェックしてください。 "Add Required Plug-ins" ボタンを押して必要なプラグインを追加します。

上記のエクステンダーが、Spring 活用バンドルよりも先に起動されるようにするために、自動スタートとスタートレベルを設定する必要があります。 製品構成の "Configuration" ページで、スタートレベルを以下のように設定します:

ビューの作成
連絡先一覧を表示するためのビューを作成してください。
e4.example.spring.jpa.views.ContactListView :
publicclass ContactListView {

private IContactHome zContactHome;
private List<Contact> fContactList;
private Table fTable;
private TableViewer fViewer;

/**
* 連絡先コンテントプロバイダ.
*/

class ContactContentProvider implements IStructuredContentProvider,
PropertyChangeListener {

public ContactContentProvider() {
}
@Override
publicvoid dispose() {
}
@Override
publicvoid inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
@Override
publicvoid propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("propertyChange.input")) {
fViewer.refresh();
}
}
@Override
public Object[] getElements(Object inputElement) {
return fContactList.toArray();
}
}

public ContactListView() {
}

@Inject
publicvoidsetContactHome(@Optional IContactHome contactHome) {
this.zContactHome = contactHome;
if (zContactHome!=null) {
fContactList = zContactHome.findAll();
refreshViewer();
}
}

privatevoid refreshViewer() {
Display display = Display.getDefault();
display.asyncExec(new Runnable() {
@Override
publicvoid run() {
if (fViewer!=null && !fViewer.getControl().isDisposed()) {
fViewer.refresh();
}
}
});
}

@PostConstruct
publicvoid createControls(Composite parent) {
parent.setLayout(new GridLayout(4, false));

// --- テーブルビューア
fViewer = new TableViewer(parent, SWT.BORDER | SWT.MULTI |
SWT.FULL_SELECTION | SWT.V_SCROLL | SWT.H_SCROLL);
fViewer.setContentProvider(new ContactContentProvider());
ContactColumnLabelProvider labelProvider = new ContactColumnLabelProvider();
labelProvider.createColumns(fViewer);

fTable = fViewer.getTable();
fTable.setLinesVisible(true);
fTable.setHeaderVisible(true);
fTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 4, 1));

initContact();
fViewer.setInput(fContactList);
}

privatevoid initContact() {
if (zContactHome==null) {
fContactList = new ArrayList<Contact>();
} else {
fContactList = zContactHome.findAll();
}
}
//...
}

Eclipse 4 アプリケーションの作成 (2)」で紹介したように、E4 では依存性注入(DI)メカニズムにより、プラットフォームが提供するサービスや自分で作成したサービスを、オブジェクトに注入することができます。 同様に、@Inject を使って OSGi サービスを注入することができます。 上のビューでは、setContactHome( ) メソッドにより Spring フレームワークが生成した ContactHome インスタンスを zContactHome フィールドにセットします。  アプリ起動後のビュー生成時には、まだ zContactHome には何もセットされていません。 その場合、initContact( ) は空の ArrayList を fContactList に設定します。 そして、fContactList がビューアの入力として設定されるので、この時点ではビューアには何も表示されません。 Spring DM が各バンドルの設定ファイルを調べ、ContactHome インスタンスが生成されると、setContactHome( ) により、zContctHome に ContactHome インスタンスがセットされます。 zContactHome.findAll( ) により、データベースから連絡先一覧が取得され、ビューアに表示されます。

上のビューが使用するカラムラベルプロバイダーも作成します:
publicclass ContactColumnLabelProvider {

String[] titles = { "氏名", "よみ", "種別" };
int[] bounds = { 120, 150, 90 };

publicvoid createColumns(TableViewer viewer) {

int i = 0;
TableViewerColumn column = new TableViewerColumn(viewer, SWT.NONE);
setColumn(column, i, true);
column.setLabelProvider(new ColumnLabelProvider() {
public String getText(Object element) {
return ((Contact) element).getSimei();
}
@Override
public Image getImage(Object element) {
return null;
}
});

i++;
column = new TableViewerColumn(viewer, SWT.NONE);
setColumn(column, i, true);
column.setLabelProvider(new ColumnLabelProvider() {
public String getText(Object element) {
return ((Contact) element).getYomi();
}
});

i++;
column = new TableViewerColumn(viewer, SWT.NONE);
setColumn(column, i, true);
column.setLabelProvider(new ColumnLabelProvider() {
public String getText(Object element) {
return ((Contact) element).getContactType().toString();
}
});
}

/**
* 各カラムの設定を行う
*
* @param column カラム
* @param colNo カラムの番号
* @param moveable 移動可能の設定
*/

privatevoid setColumn(TableViewerColumn column, int colNo, boolean moveable) {
column.getColumn().setWidth(bounds[colNo]); // 列幅
column.getColumn().setText(titles[colNo]); // 列ヘッダ
column.getColumn().setMoveable(moveable); // 移動可能設定
}
}


(アプリケーションモデルの修正)
E4アプリモデルにパートを追加して、上記のビューとリンクします。
※ビューの作成、アプリモデルとのリンクについては「Eclipse 4 アプリケーションの作成 (2)」の説明を参照してください。

アプリケーションの起動
残念ながらこのままでは、アプリを起動してもビューには何も表示されません。 e4.example.spring.jpa.home と e4.example.spring.jpa.home.impl バンドルを自動スタートするように設定する必要があります。 製品構成を右クリックして、"Run As" > "Run Configurations..." で起動構成ダイアログをオープンしてください。 一度製品構成からアプリを起動しておくと、対応する起動構成 "e4.example.spring.jpa.product" が自動的に作成されるので、それを選択します。 そして、"Plug-ins" タブで、この2つのバンドルの "Auto-start" を "true" に設定します(下図):

 これで、サンプルアプリケーションを起動すると、空のビューが表示され、しばらくして連絡先一覧が表示されます。




Eclipse 4, Eclipselink, Spring Data JPA (5)

$
0
0
前回までの作業で、Eclipse 4 アプリから Spring Data JPA を使用できるようになりました。 今回は、いくつか簡単なテストを行います。
ContactHome を修正して、saveContacts( ) と deleteContacts( ) の2つのメソッドを追加します。 使用しないメソッドは削除しています。
e4.example.spring.jpa.home.impl.ContactHome :
publicclass ContactHome implements IContactHome {

private ContactRepository contactRepository;

public ContactHome() {}

publicvoid setContactRepository(ContactRepository contactRepository) {
this.contactRepository = contactRepository;
}

public List<Contact> findAll() {
return (List<Contact>) contactRepository.findAll();
}
public List<Contact> findByContactType(ContactType contactType) {
return contactRepository.findByContactType(contactType);
}
public List<Contact> findBySimeiLike(String simei){
simei = getCriteriaLike(simei);
return contactRepository.findBySimeiLike(simei);
}
private String getCriteriaLike(String criteria) {
return criteria == null || criteria.length() < 1 ? "%" : criteria + "%";
}
public Contact saveContact(Contact contact) {
return contactRepository.save(contact);
}
publicvoid saveContacts(List<Contact> contacts) {
contactRepository.save(contacts);
}
publicvoid deleteContacts(List<Contact> contacts) {
contactRepository.delete(contacts);
}
}

選択サービスに、テーブルビューアの選択行をセットできるように、ContactListView を次のように修正します:
    @PostConstruct
publicvoid createControls(Composite parent) {
// ...

// テーブルの選択行が変わった場合、選択サービスに選択行(複数可)をセットする
fViewer.addSelectionChangedListener(new ISelectionChangedListener() {
@SuppressWarnings("restriction")
@Override
publicvoid selectionChanged(SelectionChangedEvent event) {
IStructuredSelection selection = (IStructuredSelection) fViewer
.getSelection();
zSelectionService.setSelection(selection);
}
});
}


ハンドラーとテストメソッドの作成
ビュークラス(ContactListView) にテスト用のメソッド(後述)を追加します。 パッケージ e4.example.spring.jpa.handlers にそれらのテストメソッドを呼ぶハンドラークラスを作成します。 そしてそれらのハンドラーを実行するメニューをアプリモデルに追加して、ハンドラーとリンクさせます(下図)。 ここでは、DirectMenuItem 要素を使って直接ハンドラークラスとリンクさせています。

(テスト実行前の状態)



(新規追加テスト)
  1. 連絡先1件を追加するテスト。 id は自動生成します。
    e4.example.spring.jpa.handlers.AddTest01 :
    publicclass AddTest01 {
    @Execute
    publicvoid execute(EPartService pertService) {
    MPart part = (MPart) pertService
    .findPart("e4.example.spring.jpa.ContactListPart");
    if (part != null && part.getObject() instanceof ContactListView) {
    ContactListView view = (ContactListView) part.getObject();
    view.addTest01();
    }
    }
    }
    ContactListView#addTest01 :
    publicvoid addTest01() {
    try {
    zContactHome.saveContact(createContact(null, "追加1"
    , "ついか1", ContactType.KOKYAKU));
    } catch (Exception e) {
    System.out.println(e.getMessage());
    }
    refreshView();
    }
    連絡先の作成とビューアをリフレッシュするヘルパーメソッド:
    private Contact createContact(Long id,
    String simei, String yomi, ContactType type) {
    SimpleDateFormat sdf = new SimpleDateFormat("hhmmss");
    Contact c = new Contact();
    if (id!=null)
    c.setId(id);
    c.setSimei(simei + sdf.format(Calendar.getInstance().getTime()));
    c.setYomi(yomi);
    c.setContactType(type);
    return c;
    }
    privatevoid refreshView() {
    fContactList = zContactHome.findAll();
    fViewer.refresh();
    }
    結果: 期待通り1件追加されます。 さらに、メニュー「追加:1件」を押して、連続して追加することができます。
  2. 1度に複数の連絡先を追加するテスト。
    e4.example.spring.jpa.handlers.AddTest02 : AddTest01と同じ。 テストメソッド名がview.addTest02( ) に変わるだけ。

    1件目の id は自動生成するが、2件目には固定の id を設定します。そのため、1回目は成功するが、2回目以降は2件目で id 重複によるエラーになり、処理全体がロールバックされなければなりません。
    ContactListView#addTest02 :
    publicvoid addTest02() {
    try {
    List<Contact> contacts = new ArrayList<Contact>();
    contacts.add(createContact(null, "追加21"
    , "ついか21", ContactType.SYSFAMILY));
    contacts.add(createContact(1001L, "追加22"
    , "ついか22", ContactType.SYSFRIENDS));
    zContactHome.saveContacts(contacts);
    } catch (Exception e) {
    System.out.println(e.getMessage());
    }
    refreshView();
    }
    結果:
    1回目は2件が追加されますが、2回目は1件も追加されません。

(更新テスト)
  1. テーブルビューアで選択されてる行の「よみ」列を更新します。
    ※行が複数選択されている場合でも、1行のみを処理対象にしています。
    e4.example.spring.jpa.handlers.UpdTest01 :
    publicclass UpdTest01 {
    @Execute
    publicvoid execute(
    EPartService pertService,
    @Optional @Named(IServiceConstants.ACTIVE_SELECTION)
    IStructuredSelection selection) {
    if (selection == null)
    return;
    MPart part = (MPart) pertService
    .findPart("e4.example.spring.jpa.ContactListPart");
    if (part != null && part.getObject() instanceof ContactListView) {
    ContactListView view = (ContactListView) part.getObject();
    if (selection.getFirstElement() instanceof Contact) {
    Contact c = (Contact) selection.getFirstElement();
    view.updTest01(c);
    }
    }
    }
    }

    ContactListView#updTest01 :
    publicvoid updTest01(Contact c) {
    c.setYomi(c.getYomi()+"更新");
    try {
    zContactHome.saveContact(c);
    } catch (Exception e) {
    System.out.println(e.getMessage());
    }
    refreshView();
    }
    "しまだようこ"を選択して、テスト実行した結果:
  2. 選択行に勤務先住所を追加します。 関連エンティティに対する操作をテストします。
    e4.example.spring.jpa.handlers.UpdTest02 : UpdTest01 ほとんど同じ。

    ContactListView#updTest02 :
    publicvoid updTest02(Contact c) {
    Map<String, Address> addresses = c.getAddresses();
    if (addresses.get("勤務先")==null) {
    Address a = new Address();
    a.setYubin("1234567");
    a.setTodofuken("東京都");
    a.setSikuchoson("渋谷区");
    a.setAddressType("勤務先");
    addresses.put("勤務先", a);
    try {
    zContactHome.saveContact(c);
    } catch (Exception e) {
    System.out.println(e.getMessage());
    }
    }
    List<Contact> list= zContactHome.findAll();
    for (Contact contact: list) {
    System.out.println(contact.toString());
    }
    }
    結果: MySQL Workbench で直接確認します。 期待通り address テーブルに勤務先住所が追加され、関係テーブルの contact_address にもリンク行が追加されます。



(削除テスト)
  1. 選択行(複数) を削除します。
    e4.example.spring.jpa.handlers.DelTest01 :
    publicclass DelTest01 {
    @Execute
    publicvoid execute(
    EPartService pertService,
    @Optional @Named(IServiceConstants.ACTIVE_SELECTION)
    IStructuredSelection selection) {
    if (selection == null)
    return;
    MPart part = (MPart) pertService
    .findPart("e4.example.spring.jpa.ContactListPart");
    if (part != null && part.getObject() instanceof ContactListView) {
    ContactListView view = (ContactListView) part.getObject();
    List<Contact> contacts = new ArrayList<Contact>(selection.toList());
    view.delTest01(contacts);
    }
    }
    }
    ContactListView#delTest01 :
    publicvoid delTest01(List<Contact> contacts) {
    try {
    zContactHome.deleteContacts(contacts);
    } catch (Exception e) {
    System.out.println(e.getMessage());
    }
    refreshView();
    }
    結果: これまでのテストで追加した行を全て選択して「削除:複数」メニューを選択します。 スタート状態の行数に戻ります。



(選択テスト)
  1. Spring Data リポジトリの命名規則による検索メソッドをテストします。 findByContactType( ) は、メソッド名から連絡先タイプによるクエリーを実行します。
    e4.example.spring.jpa.handlers.SelTest01 : AddTest01 とほとんど同じ。
    ContactListView#selTest01 : zContactHome.findByContactType(ContactType.DOURYOU)を呼ぶだけです。

  2. findBySimeiLike( ) は、氏名によるあいまい検索です。
    e4.example.spring.jpa.handlers.SelTest02 :
    ContactListView#selTest02 : zContactHome.findBySimeiLike("岡")を呼ぶだけです。

  3. 全件検索です。 

上述のように、Spring Data リポジトリに用意されているメソッドだけで基本的な処理はほとんど可能です。 この場合、トランザクション管理は、リポジトリのメソッド単位です。 しかし、もっと複雑なビジネスロジックを実装するには、上位のデータサービスクラス(例えば、ContactHome) にそのためのメソッドを作成して、そのメソッドでトランザクションを管理する必要があります。 そのために、Spring Data JPA は @Transactional アノテーションをサポートしますが、Spring DM はデフォルトでは、このサポートを有効にしません。 次回は、Spring DM で @Transactional を有効にする方法を説明します。

Eclipse 4, Eclipselink, Spring Data JPA (6)

$
0
0
ビジネスアプリケーションで求められる高レベルのデータ品質を維持するには、適切なトランザクションの管理が必要です。 「Eclipse 4, Eclipselink, Spring Data JPA (5)」でも説明したように、Spring フレームワークは @Transactional アノテーションをサポートしますが、メソッドに @Transactional を追加しただけでは、期待通りに機能しません。Spring DM はデフォルトで @Transactional アノテーションのサポートを有効にしないからです。 このサポートを有効にするには、spring 設定ファイルに次のような記述を追加する必要があります:
<bean id="transactionManager" .. >(...)</bean>
<tx:annotation-driven transaction-manager="transactionManager" />

e4.example.spring.jpa.home バンドルではこの記述により、すでに @Transactional アノテーションサポートは有効です。 e4.example.spring.jpa.home.impl で、@Transactional を有効にするには、..home バンドルでトランザクションマネージャ・ビーンを OSGi レジストリに登録し、..home.impl バンドルでそれを参照する必要があります。


  1. e4.example.spring.jpa.home バンドルの修正
    MANIFEST.MF を開いて、次のインポートパッケージを追加
     org.springframework.transaction,
    org.springframework.transaction.annotation
    META-INF/spring/spring-osgi-context.xml でトランザクションマネージャを公開
    次の記述を追加する
      ..
    <osgi:service ref="transactionManager"
    interface="org.springframework.transaction.PlatformTransactionManager"/>

    ..
  2. e4.example.spring.jpa.home.impl バンドルの修正
    MANIFEST.MF を開いて、次のインポートパッケージを追加
     org.aopalliance.aop,
    org.springframework.aop,
    org.springframework.aop.framework,
    org.springframework.transaction,
    org.springframework.transaction.annotation
    META-INF/spring/spring-osgi-context.xml でトランザクションマネージャを参照
      ..
    <osgi:reference id="transactionManager"
    interface="org.springframework.transaction.PlatformTransactionManager"/>

    ..
    META-INF/spring/spring-context.xml でアノテーションサポートを有効にする
    <?xml version="1.0" encoding="UTF-8"?>
    <beans
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"
    >


    <tx:annotation-driven transaction-manager="transactionManager" />
    ...
これで、ContactHome にビジネスロジックメソッドを作成して @Transactional アノテーションを付加すれば、Spring AOP により、メソッドにトランザクション管理コードが追加されます。


Eclipse 4 アプリケーションの作成 (10) ツールバーにコントロールを追加する

$
0
0
Eclipse 4 アプリケーションでツールバーに普通のボタンを追加する場合、アプリケーションモデルで "Direct Tool Item" を追加して、対応するハンドラクラスとリンクします。 しかし、以下の画面のようにプルダウンボタンやラベルなどを追加する場合は、 "ToolControl" を追加して、対応するUIクラスを作成する必要があります。


アプリケーションモデルの設定
次のようにアプリケーションモデルの "Toolbar" に必要な "Direct Tool ITem"や"ToolControl"を追加して、"Class URI" 属性を使って対応するクラスとリンクします。

対応するクラスの作成
(プルダウンボタンの例)
publicclass PreferenceToolItemControl {

publicstaticfinal String ID = "preferenceitem.toolcontrol";

@Inject
IEventBroker zBroker;

private Menu fPreferenceMenu;
private ToolItem tltmDropdownItem;

@PostConstruct
publicvoid createControls(final Composite parent) {
GridLayout gl_parent = new GridLayout(1, false);
gl_parent.verticalSpacing = 0;
gl_parent.marginWidth = 0;
gl_parent.marginHeight = 0;
gl_parent.horizontalSpacing = 0;
parent.setLayout(gl_parent);

final ToolBar toolBar = new ToolBar(parent, SWT.FLAT | SWT.RIGHT);

tltmDropdownItem = new ToolItem(toolBar, SWT.DROP_DOWN);
tltmDropdownItem.setText("");
tltmDropdownItem.setImage(ResourceManager.getPluginImage("e4video", "icons/cog.png"));

tltmDropdownItem.addSelectionListener(new SelectionAdapter() {
@Override
publicvoid widgetSelected(SelectionEvent e) {
if (e.detail == SWT.ARROW) {
Rectangle rect = tltmDropdownItem.getBounds();
Point pt = new Point(rect.x, rect.y + rect.height);
pt = toolBar.toDisplay(pt);
getPreferencePopup(parent.getShell()).setLocation(pt.x, pt.y);
getPreferencePopup(parent.getShell()).setVisible(true);
} else {
// ボタンが押された場合。 設定ダイアログを表示する
// ...
}
}
});
}

private Menu getPreferencePopup(final Shell shell) {
if (fPreferenceMenu==null) {
fPreferenceMenu = new Menu(shell, SWT.POP_UP);
MenuItem itemNext = new MenuItem(fPreferenceMenu, SWT.PUSH);
itemNext.setText("同期");
itemNext.addSelectionListener(new SelectionAdapter() {
@Override
publicvoid widgetSelected(SelectionEvent e) {
DataSyncHandler.execute(shell);
}
});
}
return fPreferenceMenu;
}
}
上記コードで、@PostConstruct 注釈付きのメソッドは、フレームワークにより、インスタンスが生成された後で呼ばれます。 このメソッドで、渡された parent コンポジットに、org.eclipse.swt.widgets.ToolBar を追加して、このツールバーに SWT.DROP_DOWN スタイルの ToolItem を1つ追加します。 1つの "ToolControl" に対して、複数のコントロール(例えばラベルとプログレスバー)を割り当てた方がよい場合もあります。 このような場合でも、WindowBuilder Editor を使えば、SWTコントロールの追加やレイアウト設定を簡単に行うことができます(下図)。


UIクラスを作成したら、アプリケーションモデルで追加した "ToolControl" とリンクしてください。

(ラベルの例)
同じようにして、ラベルコントロールも作成します。
publicclass VideoTitleControl {

publicstaticfinal String ID = "videotitle.toolcontrol";

private Label fTitleLabel;

@PostConstruct
publicvoid createControls(Composite parent) {
parent.setLayout(new GridLayout(1, false));

fTitleLabel = new Label(parent, SWT.NONE);
GridData gd_fTitleLabel = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1);
gd_fTitleLabel.widthHint = 500;
gd_fTitleLabel.minimumWidth = 200;
fTitleLabel.setLayoutData(gd_fTitleLabel);
fTitleLabel.setText("");

}

publicvoid setTitle(final String title) {
Display.getDefault().asyncExec(new Runnable() {
@Override
publicvoid run() {
fTitleLabel.setText(title);
}
});
}

publicvoid setForeground(Color fg) {
fTitleLabel.setForeground(fg);
}
}

ビューからツールアイテムやツールコントロールを参照する
アプリケーションの状態に応じてボタンを非表示にしたりラベルにテキストを設定するには、ビューからツールバーのツールアイテムやツールコントロールを参照する必要があります。 これは、EModelService#find() メソッドを使用して行うことができます。 ここでは検索のルート要素として MWindow を指定しています。 EModelService , MWindow は、依存性注入により取得します。
(例:ツールアイテムの取得)
private MToolItem getToolItem(String id) {
MToolItem toolItem = (MToolItem) zModelService.find(id, zWindow);
return toolItem;
}
(例:ラベルコントロールの取得)
private VideoTitleControl getVideoTitleControl() {
MToolControl toolControl = (MToolControl) zModelService.find(
VideoTitleControl.ID, zWindow);
if (toolControl!=null) {
return (VideoTitleControl)toolControl.getObject();
} else {
return null;
}
}

(例:タグによる取得)
ツールアイテムに限らずアプリモデルの特定の要素を取得して、取得要素に対してある処理を行いたいことがあります。 例えばサンプルアプリケーションの場合、windows で実行する場合は、終了ボタンと全画面ボタンをツールバーに表示し、osx の場合は表示しないようにしています(osx では os が全画面切替機能や全画面時の終了メニューを提供するからです)。それ以外にもギャラリーを非表示にした場合、ギャラリーの展開や、縮小のようなギャラリービュー関連のボタンも非表示にします。 これらの場合、それぞれのツールアイテムを id で個別に取得することもできますが、タグを使うと簡単かつ柔軟に特定要素を取り出すことができます。まず、アプリモデルを修正して、ツールバーの終了ボタンと全画面ボタンにタグとして "osx" を追加します。 そして以下のコードにより、タグが "osx" のツールアイテムを取得します。 この例では、サーチルートはウィンドウ、要素id には null(任意の id)を指定しています。
        List<String> tags = new ArrayList<String>();
tags.add("osx");
List<MToolItem> items = zModelService.findElements(
zWindow, null, MToolItem.class, tags);

このシリーズ( Eclipse 4 アプリケーションの作成)の 以前のポストも参考にしてください。

 クラウドからデータを取得する (3) Google OAuth 認証

$
0
0
以前のポスト「Evernote OAtuth 認証」からかなり間が空きましたが、そのとき作成したプロジェクトの e4.oauth.example を修正して、Google クラウドサービスの OAuth 認証処理を追加します。すでにプロジェクトは作成しているので、新しいサーブレットを追加するだけです。

新規サーブレットの追加
e4.oauth.example.servlet パッケージに GoogleOAuth クラスを作成します。
e4.oauth.example.servlet.GoogleOAuth :
publicclass GoogleOAuth extends HttpServlet {

privatestaticfinallong serialVersionUID = 1L;

privatestaticfinal String CONSUMER_KEY = "anonymous";
privatestaticfinal String CONSUMER_SECRET = "anonymous";
privatestaticfinal String CALLBACK_URL = "http://localhost:8080/callback";
privatestaticfinal String AUTHORIZE_URL
= "https://www.google.com/accounts/OAuthAuthorizeToken?oauth_token=";
privatestaticfinal String SCOPE = "https://www.google.com/m8/feeds";

public GoogleOAuth() {}

publicvoid doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

System.out.println("QueryString="+request.getQueryString());

// OAuth 認証サービスの作成
Activator.service = new ServiceBuilder()
.provider(GoogleApi.class)
.apiKey(CONSUMER_KEY)
.apiSecret(CONSUMER_SECRET)
.scope(SCOPE)
.callback(CALLBACK_URL)
.build();

System.out.println("=== Google Contacts OAuth Workflow ===");
System.out.println();

// リクエストトークンの取得
System.out.println("リクエストトークンを取得します...");
Token token = Activator.service.getRequestToken();
Activator.requestToken = token.getToken();
Activator.requestTokenSecret = token.getSecret();
System.out.println("リクエストトークンを取得! :" + Activator.requestToken);
System.out.println("リクエストシークレット :" + Activator.requestTokenSecret);
String authorizationUrl = AUTHORIZE_URL + Activator.requestToken;
System.out.println("認証 URL=" + authorizationUrl);
response.sendRedirect(authorizationUrl);
}
}

後で、グーグル連絡先データを取得するため、 SCOPE は "https://www.google.com/m8/feeds" としています。

サーバーの修正
e4.oauth.example.server.OAuthServer を次のように修正します(参照:元のサーバークラス):
publicclass OAuthServer {

privatestatic Server server;

publicstaticvoid start() throws Exception {
server = new Server(8080);

ServletContextHandler context0 = new ServletContextHandler(
ServletContextHandler.SESSIONS);
context0.setContextPath("/evernote_oauth");
context0.addServlet(new ServletHolder(new EvernoteOAuth()), "/*");

ServletContextHandler context1 = new ServletContextHandler(
ServletContextHandler.SESSIONS);
context1.setContextPath("/callback");
context1.addServlet(new ServletHolder(new OAuthCallback()), "/*");

ServletContextHandler context2 = new ServletContextHandler(
ServletContextHandler.SESSIONS);
context2.setContextPath("/google_oauth");
context2.addServlet(new ServletHolder(new GoogleOAuth()), "/*");

ContextHandlerCollection contexts = new ContextHandlerCollection();
contexts.setHandlers(new Handler[] { context0, context1, context2 });
server.setHandler(contexts);

server.start();
}
}

ServletContextHandler の context2 を追加して、上で作成した GoogleOAuth サーブレットを追加します。

ビュークラスの修正
起動時に自動的に行っていたサーブレットの呼び出しをハンドラ等から行えるように修正します(参照:元のビュークラス):
publicclass BrowserView {

private Browser fBrowser;
private String fUrl;

public BrowserView() {
}

/**
* Create contents of the view part.
*/

@PostConstruct
publicvoid createControls(Composite parent) {
parent.setLayout(new FillLayout());
fBrowser = new Browser(parent, SWT.NONE);
}

publicvoid setUrl(String url) {
fUrl = url;
fBrowser.setUrl(url);
}
//...
}

Activator クラスの修正
グーグルのアクセストークンとシークレットを保存できるように、Activator も次のように修正します:
publicclass Activator implements BundleActivator {

publicstatic final String PLUGIN_ID = "e4.oauth.example";

privatestatic BundleContext context;

publicstatic OAuthService service;
publicstaticString serviceName;

publicstaticString googleAccessToken;
publicstaticString evernoteAccessToken;

publicstaticString accessToken;
publicstaticString accessSecret;
publicstaticString requestToken;
publicstaticString requestTokenSecret;
//--

ハンドラークラスの作成
次のようなハンドラークラスを作成してください。
e4.oauth.example.handlers.GoogleDemoHandler :
publicclass GoogleDemoHandler {

privatestaticfinal String RESOURCE_URL = "https://www.google.com/m8/feeds/contacts/default/full";
@Execute
publicvoid execute(@Named(IServiceConstants.ACTIVE_SHELL) Shell shell,
EPartService partService)
throws Exception {
if (Activator.googleAccessToken==null) {
MPart part = (MPart)partService.findPart("e4.oauth.example.BrowserPart");
if (part!=null) {
if (part.getObject() instanceof BrowserView) {
BrowserView view = (BrowserView)part.getObject();
Activator.serviceName = "google";
view.setUrl("http://localhost:8080/google_oauth");
}
}
} else {
//... すでに認証済みの場合の処理
}
}
}

起動直後にこのハンドラーを実行すると、Activator.googleAccessToken はまだ null なので、認証処理が実行され、認証後は Evernote  の場合と同様にコールバック処理が呼ばれます。コールバック内では Activator.serviceName を調べ、値が "google" の場合は取得したアクセストークンとシークレットを Activator.googleAccessToken と Activator.accessSecret に保存します(実際のアプリでは、ユーザーIDとともにデータベース等に保存すべきです)。 再度「Google デモ」メニューが選択された場合は、データの取得などの処理を行います。

アプリモデルで、コマンド、ハンドラ、メニュー項目(Googleデモ) を追加して上記のハンドラークラスとリンクしてください。


アプリケーションの実行 
アプリケーションを起動して、ファイルメニューから上記の「Googleデモ」を選択すると、Google にログインしていない場合は、次の画面が表示されます。


ログイン後の遷移は、Google アカウントのセキュリティ設定の状態によって変わります。 2段階認証を設定している場合(下図)、画面1が表示されます。そうでない場合は、直接画面2が表示されます。



画面1:
2段階認証を設定している場合は、携帯電話に番号が通知されます。 通知された番号を上の画面で入力して「確認」ボタンを押すと、画面2に変わります。

画面2:
「アクセス許可」を押すと、取得したアクセストークンが表示されます。 
Evernote の場合と同じようにして、アクセストークンが取得できます。 そして、コールバックでは次のハンドラー(GoogleDemoHandler) の実行に備えて、アクセストークンとシークレットを保存します。 次回はこのアクセストークンとシークレットを使ってグーグル連絡先データを取得する例を説明します。
 

クラウドからデータを取得する (4) Google 連絡先データの取得

$
0
0
前回のポストでは Google アカウントへのアクセス要求を行い、アクセストークンとシークレットを取得するところまで説明しました。 一旦アクセストークンを取得すれば、様々な方法で Google サービスにアクセスできます。 まずは、連絡先データを xml として取得する例を説明します。 xml の解析には JDOMを使用します。

JDOM のダウンロードと設定
jdom.org から最新の JDOM  をダウンロードして、以下の jar ファイルをプロジェクトの lib フォルダにコピーして、plugin.xml  > Runtime タブ > Classpath に追加してください:

  • jdom-2.0.2-contrib.jar
  • jdom-2.0.2.jar
  • jaxen-1.1.3.jar
  • xercesimpl.jar
  • xml-apis.jar

ハンドラーの修正
GoogleDemoHandler を次のように修正してください:
publicclass GoogleDemoHandler {

privatestaticfinal String RESOURCE_URL = "https://www.google.com/m8/feeds/contacts/default/full";

@Execute
publicvoid execute(
EPartService partService) throws Exception {
if (Activator.googleAccessToken==null) {
MPart part = (MPart)partService.findPart(BrowserView.PART_ID);
if (part!=null) {
if (part.getObject() instanceof BrowserView) {
BrowserView view = (BrowserView)part.getObject();
Activator.serviceName = "google";
view.setUrl("http://localhost:8080/google_oauth");
}
}
} else {
Token token = new Token(Activator.googleAccessToken, Activator.accessSecret);
OAuthRequest req = new OAuthRequest(Verb.GET, RESOURCE_URL);
Activator.service.signRequest(token, req);
req.addHeader("GData-Version", "3.0");
Response res = req.send();
try {
SAXBuilder builder = new SAXBuilder();
Document document = (Document) builder.build(res.getStream());
Element rootNode = document.getRootElement();
List list = rootNode.getChildren();
for (int i = 0; i < list.size(); i++) {
Element node = (Element) list.get(i);
List<Element> children = node.getChildren();
for (Element child: children) {
String tag = child.getName();
String txt = child.getTextTrim();
String val = child.getValue();
List<Content> contents = child.getContent();
List<Attribute> attrs = child.getAttributes();
if (tag.equals("id")) {
print("id", txt);
} elseif (tag.equals("content")) {
print("種別", txt);
} elseif (tag.equals("gender")) {
for (Attribute a: attrs) {
if (a.getName().equals("value")) {
print("性別", a.getValue());
}
}
} elseif (tag.equals("birthday")) {
for (Attribute a: attrs) {
if (a.getName().equals("when")) {
print("誕生日", a.getValue());
}
}
} elseif (tag.equals("organization")) {
for (Content content: contents) {
if (content.toString().contains("orgName")) {
print("会社名", content.getValue());
} elseif (content.toString().contains("orgTitle")) {
print("部署", content.getValue());
}
}
} elseif (tag.equals("name")) {
for (Content content: contents) {
if (content.toString().contains("fullName")) {
print("氏名", content.getValue());
} elseif (content.toString().contains("givenName")) {
print("名", content.getValue());
} elseif (content.toString().contains("familyName")) {
print("姓", content.getValue());
}
}
} elseif (tag.equals("phoneNumber")) {
String telno = txt;
Attribute attr = child.getAttribute("rel");
if (attr!=null) {
if (attr.getValue().endsWith("work")) {
print("勤務先電話", txt);
} elseif (attr.getValue().endsWith("home")) {
print("自宅電話", txt);
} elseif (attr.getValue().endsWith("mobile")) {
print("携帯電話", txt);
}
}
} elseif (tag.equals("structuredPostalAddress")) {
String addr = "";
for (Content content: contents) {
if (content.toString().contains("formattedAddress")) {
addr = content.getValue();
}
}
Attribute attr = child.getAttribute("rel");
if (attr!=null) {
if (attr.getValue().endsWith("work")) {
print("勤務先住所", addr.replace("\n", ", "));
} elseif (attr.getValue().endsWith("home")) {
print("自宅住所", addr.replace("\n", ", "));
}
}
} elseif (tag.equals("userDefinedField")) {
Attribute key = child.getAttribute("key");
Attribute contactid = child.getAttribute("value");
if (key!=null && key.equals("CONTACT_ID") && contactid !=null) {
print("contactid", contactid.getValue());
}
} elseif (tag.equals("link")) {
Attribute attr = child.getAttribute("rel");
if (attr!=null) {
if (attr.getValue().endsWith("photo")) {
print("photo-href", child.getAttributeValue("href"));
print("photo-etag", child.getAttributeValue("etag"));
}
}
} elseif (tag.equals("email")) {
Attribute attr = child.getAttribute("rel");
if (attr!=null) {
if (attr.getValue().endsWith("work")) {
print("勤務先メール", child.getAttribute("address").getValue());
} elseif (attr.getValue().endsWith("home")) {
print("自宅メール", child.getAttribute("address").getValue());
}
}
} elseif (tag.equals("website")) {
Attribute attr = child.getAttribute("rel");
if (attr!=null) {
if (attr.getValue().equals("home-page")) {
print("WEB", child.getAttribute("href").getValue());
}
}
}
}
System.out.println("------");
}
} catch (IOException io) {
System.out.println(io.getMessage());
} catch (JDOMException jdomex) {
System.out.println(jdomex.getMessage());
}
}
}

privatevoid print(String tag, String txt) {
System.out.println(tag + " : " + txt);
}
}

取得した連絡先データの例
上の例では取得した xml を解析して、タグごとに主な値をプリントしているだけです。
id : http://www.google.com/m8/feeds/contacts/<USERのID>/base/35fa916a8e91b952
種別 : 種別(同僚)
photo-href : https://www.google.com/m8/feeds/photos/media/<USERのID>/35fa916a8e91b952
photo-etag : null
氏名 : 荒川一男
名 : 一男
姓 : 荒川
性別 : male
誕生日 : 2012-03-26
会社名 : 株式会社 Itrane
部署 : システム開発部
勤務先電話 : 03055552222
勤務先住所 : 2160000, 神奈川県, 川崎市宮前区
------
id : http://www.google.com/m8/feeds/contacts/<USERのID>/base/373c12fa0d05fb1c
種別 : 種別(顧客)
photo-href : https://www.google.com/m8/feeds/photos/media/<USERのID>/373c12fa0d05fb1c
photo-etag : null
氏名 : 森真美
名 : 真美
姓 : 森
性別 : female
誕生日 : 2012-03-26
会社名 : E社
部署 : 営業部
勤務先電話 : 03080024159
携帯電話 : 09050015489
勤務先住所 : 1110041, 東京都, 台東区, 元浅草
...

gdata-contacts api を使用する
gdata-contacts api を使用すればもっと簡単に連絡先データにアクセスすることができます。 それには、 gdata-samples.java-1.47.1.zipをダウンロードして、それに含まれる以下の jar を使用します。
  • gdata-cleint-1.0.jar
  • gdata-contacts-3.0.jar
  • gdata-core-1.0.jar
  • google-collect-1.0-rc1.jar
  • google-oauth-client-1.8.0.jar
上記の jarをクラスパスに追加して、GoogleDemoHandler の execute メソッドを次のように変更します:
@Execute
publicvoid execute(EPartService partService) throws Exception {
if (Activator.googleAccessToken==null) {
MPart part = (MPart)partService.findPart("e4.oauth.example.BrowserPart");
if (part!=null) {
if (part.getObject() instanceof BrowserView) {
BrowserView view = (BrowserView)part.getObject();
Activator.serviceName = "google";
view.setUrl("http://localhost:8080/google_oauth");
}
}
} else {
GoogleService service = new ContactsService("GoogleContactService");
OAuthSigner signer = new OAuthHmacSha1Signer();
GoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();
oauthParameters.setOAuthConsumerKey("anonymous");
oauthParameters.setOAuthConsumerSecret("anonymous");
oauthParameters.setScope("https://www.google.com/m8/feeds");
oauthParameters.setOAuthToken(Activator.googleAccessToken);
oauthParameters.setOAuthTokenSecret(Activator.accessSecret);
try {
service.setOAuthCredentials(oauthParameters, signer);
} catch (OAuthException e) {
//エラー処理
}
//ContactService を使った処理
}
}

service.setOAuthCredentials(oauthParameters, signer) で、作成した ContactsService に認証情報をセットして、このサービスが提供する様々な API を使用することができます。
Viewing all 71 articles
Browse latest View live