More flexible service locator interface.

https://sourceforge.net/tracker/?func=detail&aid=2700937&group_id=176962&atid=879334
This commit is contained in:
joergviola 2010-03-08 20:27:33 +00:00
parent e11cc21c52
commit f73caa3e98
16 changed files with 326 additions and 55 deletions

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
<extension-point id="org.adempiere.base.ResourceFinder" name="org.adempiere.base.ResourceFinder" schema="schema/ResourceFinder.exsd"/>
<extension-point id="org.adempiere.base.IResourceFinder" name="ResourceFinder" schema="schema/ResourceFinder.exsd"/>
<extension-point id="org.adempiere.base.Callout" name="Callout" schema="schema/org.adempiere.base.Callout.exsd"/>
</plugin>

View File

@ -0,0 +1,119 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Schema file written by PDE -->
<schema targetNamespace="org.adempiere.base" xmlns="http://www.w3.org/2001/XMLSchema">
<annotation>
<appinfo>
<meta.schema plugin="org.adempiere.base" id="org.adempiere.base.Callout" name="Callout"/>
</appinfo>
<documentation>
This is the ADempiere Call extension point.
Callouts provided by plugins are not entered into the application dictionary.
Instead, you specify Table and Column in the extension declaration.
</documentation>
</annotation>
<element name="extension">
<annotation>
<appinfo>
<meta.element />
</appinfo>
</annotation>
<complexType>
<choice>
<element ref="client" minOccurs="1" maxOccurs="unbounded"/>
</choice>
<attribute name="point" type="string" use="required">
<annotation>
<documentation>
</documentation>
</annotation>
</attribute>
<attribute name="id" type="string">
<annotation>
<documentation>
</documentation>
</annotation>
</attribute>
<attribute name="name" type="string">
<annotation>
<documentation>
</documentation>
<appinfo>
<meta.attribute translatable="true"/>
</appinfo>
</annotation>
</attribute>
</complexType>
</element>
<element name="client">
<complexType>
<attribute name="class" type="string" use="required">
<annotation>
<documentation>
</documentation>
<appinfo>
<meta.attribute kind="java" basedOn=":org.compiere.model.Callout"/>
</appinfo>
</annotation>
</attribute>
<attribute name="tableName" type="string" use="required">
<annotation>
<documentation>
</documentation>
</annotation>
</attribute>
<attribute name="columnName" type="string" use="required">
<annotation>
<documentation>
</documentation>
</annotation>
</attribute>
</complexType>
</element>
<annotation>
<appinfo>
<meta.section type="since"/>
</appinfo>
<documentation>
[Enter the first release in which this extension point appears.]
</documentation>
</annotation>
<annotation>
<appinfo>
<meta.section type="examples"/>
</appinfo>
<documentation>
[Enter extension point usage example here.]
</documentation>
</annotation>
<annotation>
<appinfo>
<meta.section type="apiinfo"/>
</appinfo>
<documentation>
[Enter API information here.]
</documentation>
</annotation>
<annotation>
<appinfo>
<meta.section type="implementation"/>
</appinfo>
<documentation>
[Enter information about supplied implementation of this extension point.]
</documentation>
</annotation>
</schema>

View File

@ -0,0 +1,41 @@
package org.adempiere.base;
import java.net.URL;
import java.util.List;
import org.adempiere.base.equinox.ExtensionList;
import org.compiere.model.Callout;
/**
* This is a facade class for the Service Locator.
* It provides simple access to all core services.
*
* @author viola
*/
public class Core {
public static IResourceFinder getResourceFinder() {
return new IResourceFinder() {
public URL getResource(String name) {
List<IResourceFinder> f = Service.list(IResourceFinder.class);
for (IResourceFinder finder : f) {
URL url = finder.getResource(name);
if (url!=null)
return url;
}
return null;
}
};
}
public static Callout getCallout(String tableName, String columnName) {
ServiceQuery query = new ServiceQuery();
query.put("tableName", tableName);
query.put("columnName", columnName);
return Service.locate(Callout.class, query);
}
}

View File

@ -18,7 +18,7 @@ package org.adempiere.base;
import java.net.URL;
public interface IResourceFinder {
public interface IResourceFinder extends IService {
URL getResource(String name);

View File

@ -0,0 +1,5 @@
package org.adempiere.base;
public interface IService {
}

View File

@ -16,16 +16,24 @@
*****************************************************************************/
package org.adempiere.base;
import java.util.List;
import org.compiere.model.Callout;
/**
* A service locator looks up services.
* This is the central authority for adempiere service definition,
* because each service defined has to be looked up via this interface.
*
* A service in adempiere is an interface extended from the tagging interface IService.
*
* @author viola
*
*/
public interface IServiceLocator {
IResourceFinder getResourceFinder();
<T extends IService> T locate(Class<T> type);
<T extends IService> T locate(Class<T> type, ServiceQuery query);
<T extends IService> List<T> list(Class<T> type);
<T extends IService> List<T> list(Class<T> type, ServiceQuery query);
}

View File

@ -16,26 +16,46 @@
*****************************************************************************/
package org.adempiere.base;
import java.util.List;
import org.adempiere.base.equinox.EquinoxServiceLocator;
/**
* This is a very simple factory for service locators
*
* @author viola
*
*
*/
public class Service {
private static IServiceLocator theLocator;
public static IServiceLocator locator() {
if (theLocator==null) {
if (theLocator == null) {
synchronized (Service.class) {
if (theLocator==null) {
if (theLocator == null) {
theLocator = new EquinoxServiceLocator();
System.out
.println("Started service locator: " + theLocator);
}
}
}
return theLocator;
}
public static <T extends IService> T locate(Class<T> type) {
return locator().locate(type);
}
public static <T extends IService> T locate(Class<T> type, ServiceQuery query) {
return locator().locate(type, query);
}
public static <T extends IService> List<T> list(Class<T> type) {
return locator().list(type);
}
public static <T extends IService> List<T> list(Class<T> type, ServiceQuery query) {
return locator().list(type, query);
}
}

View File

@ -0,0 +1,9 @@
package org.adempiere.base;
import java.util.HashMap;
public class ServiceQuery extends HashMap<String, String> {
private static final long serialVersionUID = -3624488575106821781L;
}

View File

@ -1,35 +0,0 @@
/******************************************************************************
* Product: Adempiere ERP & CRM Smart Business Solution *
* Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved. *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program is distributed in the hope *
* that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
* For the text or an alternative of this public license, you may reach us *
* ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA *
* or via info@compiere.org or http://www.compiere.org/license.html *
*****************************************************************************/
package org.adempiere.base.equinox;
import java.net.URL;
import org.adempiere.base.IResourceFinder;
public class EquinoxResourceFinder implements IResourceFinder {
public URL getResource(String name) {
ExtensionList<IResourceFinder> list = new ExtensionList<IResourceFinder>(IResourceFinder.class, "org.adempiere.base.ResourceFinder");
for (IResourceFinder rf : list) {
URL url = rf.getResource(name);
if (url!=null)
return url;
}
return null;
}
}

View File

@ -16,14 +16,41 @@
*****************************************************************************/
package org.adempiere.base.equinox;
import org.adempiere.base.IResourceFinder;
import java.util.List;
import org.adempiere.base.IService;
import org.adempiere.base.IServiceLocator;
import org.adempiere.base.ServiceQuery;
/**
* This is the Equinox implementation of the ADempiere Service Locator.
* It delegates work to the ExtensionList that lookups up services as extensions.
* The ids of extension points have to correspond to the interface names of the services.
*
* @author viola
*
*/
public class EquinoxServiceLocator implements IServiceLocator {
public IResourceFinder getResourceFinder() {
return new EquinoxResourceFinder();
public <T extends IService> List<T> list(Class<T> type) {
ExtensionList<T> list = new ExtensionList<T>(type, type.getName());
return list.asList();
}
public <T extends IService> List<T> list(Class<T> type, ServiceQuery query) {
ExtensionList<T> list = new ExtensionList<T>(type, type.getName(), query);
return list.asList();
}
public <T extends IService> T locate(Class<T> type) {
ExtensionList<T> list = new ExtensionList<T>(type, type.getName());
return list.first();
}
public <T extends IService> T locate(Class<T> type, ServiceQuery query) {
ExtensionList<T> list = new ExtensionList<T>(type, type.getName(), query);
return list.first();
}
}

View File

@ -16,12 +16,28 @@
*****************************************************************************/
package org.adempiere.base.equinox;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import org.adempiere.base.ServiceQuery;
import org.compiere.model.Callout;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.Platform;
/**
* This List looks up services as extensions in equinox.
* The extension point must be the class name of the service interface.
* The query attributes are checked against the attributes
* of the extension configuration element.
*
* In order to minimize equinox lookups, a filtering iterator is used.
* @author viola
*
* @param <T> The service this list holds implementations of.
*/
public class ExtensionList<T> implements Iterable<T>{
public class ExtensionIterator<T> implements Iterator<T> {
@ -29,11 +45,31 @@ public class ExtensionList<T> implements Iterable<T>{
private int index = 0;
public boolean hasNext() {
iterateUntilAccepted();
return index<elements.length;
}
private void iterateUntilAccepted() {
while (index<elements.length) {
if (accept(elements[index]))
break;
index++;
}
}
private boolean accept(IConfigurationElement element) {
for (String name : filters.keySet()) {
String expected = filters.get(name);
String actual = element.getAttribute(name);
if (!expected.equals(actual))
return false;
}
return true;
}
@SuppressWarnings("unchecked")
public T next() {
iterateUntilAccepted();
IConfigurationElement e = elements[index++];
try {
return (T) e.createExecutableExtension("class");
@ -49,6 +85,7 @@ public class ExtensionList<T> implements Iterable<T>{
}
private IConfigurationElement[] elements;
private HashMap<String, String> filters = new HashMap<String, String>();
public ExtensionList(Class<T> clazz, String id) {
try {
@ -58,8 +95,34 @@ public class ExtensionList<T> implements Iterable<T>{
}
}
public ExtensionList(Class<T> type, String name, ServiceQuery query) {
this(type, name);
for (String key : query.keySet()) {
addFilter(key, query.get(key));
}
}
public Iterator<T> iterator() {
return new ExtensionIterator<T>();
}
public void addFilter(String name, String value) {
filters.put(name, value);
}
public T first() {
Iterator<T> i = iterator();
if (!i.hasNext())
return null;
return i.next();
}
public List<T> asList() {
List<T> result = new ArrayList<T>();
for (T t : this) {
result.add(t);
}
return result;
}
}

View File

@ -22,6 +22,7 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
@ -30,6 +31,8 @@ import javax.jnlp.ServiceManager;
import javax.jnlp.UnavailableServiceException;
import javax.swing.ImageIcon;
import org.adempiere.base.Core;
import org.adempiere.base.IResourceFinder;
import org.adempiere.base.Service;
import org.compiere.db.CConnection;
import org.compiere.model.MClient;
@ -337,7 +340,7 @@ public final class Adempiere
if (s_imageLogo == null)
{
Toolkit tk = Toolkit.getDefaultToolkit();
URL url = Service.locator().getResourceFinder().getResource(s_file100x30);
URL url = Core.getResourceFinder().getResource(s_file100x30);
// URL url = org.compiere.Adempiere.class.getResource(s_file100x30);
// System.out.println(url);
if (url == null)
@ -583,7 +586,7 @@ public final class Adempiere
} // startupEnvironment
public static URL getResource(String name) {
return Service.locator().getResourceFinder().getResource(name);
return Core.getResourceFinder().getResource(name);
}

View File

@ -18,6 +18,8 @@ package org.compiere.model;
import java.util.Properties;
import org.adempiere.base.IService;
/**
* Callout Interface for Callout.
* Used in MTab and ImpFormatRow
@ -25,7 +27,7 @@ import java.util.Properties;
* @author Jorg Janke
* @version $Id: Callout.java,v 1.2 2006/07/30 00:51:05 jjanke Exp $
*/
public interface Callout
public interface Callout extends IService
{
/**
* Start Callout.

View File

@ -37,6 +37,8 @@ import java.util.logging.Level;
import javax.script.ScriptEngine;
import javax.swing.event.EventListenerList;
import org.adempiere.base.Core;
import org.adempiere.base.Service;
import org.compiere.util.CLogMgt;
import org.compiere.util.CLogger;
import org.compiere.util.DB;
@ -2682,6 +2684,13 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
*/
public String processCallout (GridField field)
{
Object value = field.getValue();
Object oldValue = field.getOldValue();
Callout co = Core.getCallout(getTableName(), field.getColumnName());
if (co!=null) {
return co.start(m_vo.ctx, "", m_vo.WindowNo, this, field, value, oldValue);
}
String callout = field.getCallout();
if (callout.length() == 0)
return "";
@ -2689,8 +2698,6 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
if (isProcessed() && !field.isAlwaysUpdateable()) // only active records
return ""; // "DocProcessed";
Object value = field.getValue();
Object oldValue = field.getOldValue();
log.fine(field.getColumnName() + "=" + value
+ " (" + callout + ") - old=" + oldValue);

View File

@ -46,6 +46,7 @@ import javax.swing.JFrame;
import javax.swing.RepaintManager;
import javax.swing.SwingUtilities;
import org.adempiere.base.Core;
import org.adempiere.base.IResourceFinder;
import org.adempiere.base.Service;
import org.compiere.Adempiere;
@ -1485,7 +1486,7 @@ public final class Env
*/
public static Image getImage (String fileNameInImageDir)
{
IResourceFinder rf = Service.locator().getResourceFinder();
IResourceFinder rf = Core.getResourceFinder();
URL url = rf.getResource("images/" + fileNameInImageDir);
// URL url = Adempiere.class.getResource("images/" + fileNameInImageDir);
@ -1506,7 +1507,7 @@ public final class Env
*/
public static ImageIcon getImageIcon (String fileNameInImageDir)
{
IResourceFinder rf = Service.locator().getResourceFinder();
IResourceFinder rf = Core.getResourceFinder();
URL url = rf.getResource("images/" + fileNameInImageDir);
// URL url = Adempiere.class.getResource("images/" + fileNameInImageDir);
if (url == null)
@ -1528,7 +1529,7 @@ public final class Env
*/
public static ImageIcon getImageIcon2 (String fileName)
{
IResourceFinder rf = Service.locator().getResourceFinder();
IResourceFinder rf = Core.getResourceFinder();
URL url = rf.getResource("images/" + fileName+".gif");
// URL url = Adempiere.class.getResource("images/" + fileName+".gif");
if (url == null)

View File

@ -4,7 +4,7 @@
<extension
id="RF"
name="RF"
point="org.adempiere.base.ResourceFinder">
point="org.adempiere.base.IResourceFinder">
<client
class="org.compiere.util.ResourceFinder">
</client>