プラグイン対応アプリケーションの作成
では、アプリケーション側のクラスを作成しましょう。ここでは、メインクラスとなるSamplePluginAppには計算のための処理は用意せず、サンプルとしてプラグインクラスを1つ標準で用意しておき、それをデフォルトで組み込んでおく形にしておきました。
package jp.tuyano.codezine; import java.awt.*; import java.awt.event.*; import java.io.File; import java.net.*; import java.util.*; import java.util.jar.*; import javax.swing.*; public class SamplePluginApp extends JFrame { private static final long serialVersionUID = 1L; private JTextField result; private JComboBox pluginsCombo; private JPanel panel; private ArrayList <SamplePluginAppPlugin>plugins; private SamplePluginAppPlugin selectedPlugin; private JPanel selectedPluginPanel; public static void main(String[] args) { new SamplePluginApp().setVisible(true); } // コンストラクタ。基本GUIの作成 public SamplePluginApp(){ result = new JTextField(); this.add(result,BorderLayout.NORTH); panel = new JPanel(); panel.setLayout(new BorderLayout()); pluginsCombo = this.getPluginComboBox(); pluginsCombo.setBounds(new Rectangle(25,25,100,20)); pluginsCombo.addActionListener(new pluginComboAction()); panel.add(pluginsCombo,BorderLayout.NORTH); panel.add(selectedPlugin.getPanel(),BorderLayout.CENTER); this.add(panel,BorderLayout.CENTER); JButton button = new JButton("calc"); button.addActionListener(new buttonAction()); this.add(button,BorderLayout.SOUTH); this.setSize(new Dimension(200,200)); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } // JComboBoxの作成 public JComboBox getPluginComboBox(){ JComboBox combo = new JComboBox(); plugins = getPlugins(); // デフォルトプラグインの準備 selectedPlugin = new DefaultPlugin(); selectedPluginPanel = selectedPlugin.getPanel(); combo.addItem(selectedPlugin); for(int i = 0;i < plugins.size();i++){ SamplePluginAppPlugin plugin = plugins.get(i); combo.addItem(plugin); } return combo; } // JComboBoxのActionListener class pluginComboAction implements ActionListener { public void actionPerformed(ActionEvent ev){ panel.removeAll(); panel.add(pluginsCombo,BorderLayout.NORTH); selectedPlugin = (SamplePluginAppPlugin) pluginsCombo.getSelectedItem(); selectedPluginPanel = selectedPlugin.getPanel(); panel.add(selectedPluginPanel); panel.updateUI(); } } // JButtonのActionListener class buttonAction implements ActionListener { public void actionPerformed(ActionEvent ev){ result.setText(selectedPlugin.getResult()); } } // プラグインクラスのインスタンスをArrayListにまとめて返す public ArrayList<SamplePluginAppPlugin> getPlugins() { ArrayList <SamplePluginAppPlugin>plugins = new ArrayList<SamplePluginAppPlugin>(); String cpath = System.getProperty("user.dir") + File.separator + "plugins"; try { File f = new File(cpath); String[] files = f.list(); for (int i = 0; i < files.length; i++) { if (files[i].endsWith(".jar")) { File file = new File(cpath + File.separator + files[i]); JarFile jar = new JarFile(file); Manifest mf = jar.getManifest(); Attributes att = mf.getMainAttributes(); String cname = att.getValue("Plugin-Class"); URL url = file.getCanonicalFile().toURI().toURL(); URLClassLoader loader = new URLClassLoader( new URL[] { url }); Class cobj = loader.loadClass(cname); Class[] ifnames = cobj.getInterfaces(); for (int j = 0; j < ifnames.length; j++) { if (ifnames[j] == SamplePluginAppPlugin.class) { System.out.println("load..... " + cname); SamplePluginAppPlugin plugin = (SamplePluginAppPlugin)cobj.newInstance(); plugins.add(plugin); break; } } } } } catch (Exception ex) { ex.printStackTrace(); } return plugins; } } // サンプルプラグインのクラス class DefaultPlugin implements SamplePluginAppPlugin { private JTextField input; private JPanel currentPanel; public String toString(){ return "Default(tax)"; } public String getResult() { String inputData = input.getText(); if (inputData == null) inputData = "0"; int n; try { n = Integer.parseInt(inputData); } catch (NumberFormatException e) { e.printStackTrace(); n = 0; } double res = ((int)(n * 1.05 * 100)) / 100; return Double.toString(res); } public void setInputData(String str) { input.setText(str); } public String getInputData(){ return input.getText(); } public JPanel getPanel() { currentPanel = new JPanel(); currentPanel.setLayout(null); input = new JTextField(); input.setBounds(new Rectangle(25,25,50,20)); currentPanel.add(input); return currentPanel; } }

プラグイン読み込みの処理
SamplePluginAppでは、getPluginsメソッドでプラグインをロードしArrayListにまとめる処理をしています。処理の流れを整理していきましょう。――まずは、アプリケーションのディレクトリ内にある「plugins」フォルダのパスを取得します。
String cpath = System.getProperty("user.dir") + File.separator + "plugins";
アプリケーションも実行可能Jarとしてまとめる前提で考えています。この場合、System.getProperty("user.dir")でアプリケーションがおかれているディレクトリのパスが得られます。これにFile.separatorを使って"plugins"を付け足すことで「plugins」フォルダのパスが得られます。
File f = new File(cpath);
String[] files = f.list();
「plugins」フォルダ内の全ファイルを取得します。これはFileインスタンスのlist()で得ることができます。こうして取得されたファイルの中から、forによる繰り返しを使って、この中からJarファイルだけをピックアップしていきます。
for (int i = 0; i < files.length; i++) { if (files[i].endsWith(".jar")) {
ファイル名の末尾が".jar"で終わるものだけを処理していきます。まずFileインスタンスを作成し、それを元にJarFileインスタンスを作成します。
File file = new File(cpath + File.separator + files[i]); JarFile jar = new JarFile(file);
続いて、JarFileからManifestを取得します。これは名前の通り、マニフェストファイルを示すクラスです。そこからメインアトリビュート(属性)を取得し、その中にある"Plugin-Class"の値を取得します。
Manifest mf = jar.getManifest();
Attributes att = mf.getMainAttributes();
String cname = att.getValue("Plugin-Class");
こうして得られたプラグインクラスをJarファイルから読み込みます。これには、JarファイルをURLとして取得し、そこからURLClassLoaderを作成します。
URL url = file.getCannicalFile().toURI().toURL(); URLClassLoader loader = new URLClassLoader( new URL[] { url });
そしてこのURLClassLoaderのloadClass()を使い、プラグインクラスのClassを取得し、そこからインターフェイスをすべて取り出します。
Class cobj = loader.loadClass(cname); Class[] ifnames = cobj.getInterfaces();
これでプラグイン・クラスにimplementsされている全インターフェイスのClassが得られました。後は、forを使い、この中にSamplePluginAppPluginが含まれていないかチェックすればいいわけです。
for (int j = 0; j < ifnames.length; j++) { if (ifnames[j] == SamplePluginAppPlugin.class) {
もし含まれていた場合には、newInstance()メソッドでSamplePluginAppPluginインスタンスを作成し、これをArrayListに追加します。
System.out.println("load..... " + cname);
SamplePluginAppPlugin plugin =
(SamplePluginAppPlugin)cobj.newInstance();
plugins.add(plugin);
これで、JarからSamplePluginAppPluginをimplementsしてあるクラスのインスタンスを作成しArrayListにまとめる、という処理ができました。Jarファイルの作成、マニフェストの読み込み、属性の値の取得、インターフェイスの取得、クラスのインスタンスの作成――これらの流れが一通り理解できれば、Jarファイルからプラグインのクラスを読み込むことはほぼできるようになります。
