/*
* @(#)SyncFactory.java 1.13 04/07/17
* @(#)SyncFactory.java 1.11 04/06/25
*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package javax.sql.rowset.spi;
import java.util.Map;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Vector;
import java.util.Properties;
import java.util.Collection;
import java.util.StringTokenizer;
import java.util.logging.*;
import java.util.*;
import java.sql.*;
import javax.sql.*;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.FileNotFoundException;
import javax.naming.*;
/**
* The Service Provider Interface (SPI) mechanism that generates <code>SyncProvider</code>
* instances to be used by disconnected <code>RowSet</code> objects.
* The <code>SyncProvider</code> instances in turn provide the
* <code>javax.sql.RowSetReader</code> object the <code>RowSet</code> object
* needs to populate itself with data and the
* <code>javax.sql.RowSetWriter</code> object it needs to
* propagate changes to its
* data back to the underlying data source.
* <P>
* Because the methods in the <code>SyncFactory</code> class are all static,
* there is only one <code>SyncFactory</code> object
* per Java VM at any one time. This ensures that there is a single source from which a
* <code>RowSet</code> implementation can obtain its <code>SyncProvider</code>
* implementation.
* <p>
* <h3>1.0 Overview</h3>
* The <code>SyncFactory</code> class provides an internal registry of available
* synchronization provider implementations (<code>SyncProvider</code> objects).
* This registry may be queried to determine which
* synchronization providers are available.
* The following line of code gets an enumeration of the providers currently registered.
* <PRE>
* java.util.Enumeration e = SyncFactory.getRegisteredProviders();
* </PRE>
* All standard <code>RowSet</code> implementations must provide at least two providers:
* <UL>
* <LI>an optimistic provider for use with a <code>CachedRowSet</code> implementation
* or an implementation derived from it
* <LI>an XML provider, which is used for reading and writing XML, such as with
* <code>WebRowSet</code> objects
* </UL>
* Note that the JDBC RowSet Implementations include the <code>SyncProvider</code>
* implemtations <code>RIOptimisticProvider</code> and <code>RIXmlProvider</code>,
* which satisfy this requirement.
* <P>
* The <code>SyncFactory</code> class provides accessor methods to assist
* applications in determining which synchronization providers are currently
* registered with the <code>SyncFactory</code>.
* <p>
* Other methods let <code>RowSet</code> persistence providers be
* registered or de-registered with the factory mechanism. This
* allows additional synchronization provider implementations to be made
* available to <code>RowSet</code> objects at run time.
* <p>
* Applications can apply a degree of filtering to determine the level of
* synchronization that a <code>SyncProvider</code> implementation offers.
* The following criteria determine whether a provider is
* made available to a <code>RowSet</code> object:
* <ol>
* <li>If a particular provider is specified by a <code>RowSet</code> object, and
* the <code>SyncFactory</code> does not contain a reference to this provider,
* a <code>SyncFactoryException</code> is thrown stating that the synchronization
* provider could not be found.
* <p>
* <li>If a <code>RowSet</code> implementation is instantiated with a specified
* provider and the specified provider has been properly registered, the
* requested provider is supplied. Otherwise a <code>SyncFactoryException</code>
* is thrown.
* <p>
* <li>If a <code>RowSet</code> object does not specify a
* <code>SyncProvider</code> implementation and no additional
* <code>SyncProvider</code> implementations are available, the reference
* implementation providers are supplied.
* </ol>
* <h3>2.0 Registering <code>SyncProvider</code> Implementations</h3>
* <p>
* Both vendors and developers can register <code>SyncProvider</code>
* implementations using one of the following mechanisms.
* <ul>
* <LI><B>Using the command line</B><BR>
* The name of the provider is supplied on the command line, which will add
* the provider to the system properties.
* For example:
* <PRE>
* -Drowset.provider.classname=com.fred.providers.HighAvailabilityProvider
* </PRE>
* <li><b>Using the Standard Properties File</b><BR>
* The reference implementation is targeted
* to ship with J2SE 1.5, which will include an additional resource file
* that may be edited by hand. Here is an example of the properties file
* included in the reference implementation:
* <PRE>
* #Default JDBC RowSet sync providers listing
* #
*
* # Optimistic synchronization provider
* rowset.provider.classname.0=com.sun.rowset.providers.RIOptimisticProvider
* rowset.provider.vendor.0=Sun Microsystems Inc
* rowset.provider.version.0=1.0
*
* # XML Provider using standard XML schema
* rowset.provider.classname.1=com.sun.rowset.providers.RIXMLProvider
* rowset.provider.vendor.1=Sun Microsystems Inc.
* rowset.provider.version.1=1.0
* </PRE>
* The <code>SyncFactory</code> checks this file and registers the
* <code>SyncProvider</code> implementations that it contains. A
* developer or vendor can add other implementations to this file.
* For example, here is a possible addition:
* <PRE>
* rowset.provider.classname.2=com.fred.providers.HighAvailabilityProvider
* rowset.provider.vendor.2=Fred, Inc.
* rowset.provider.version.2=1.0
* </PRE>
* <p>
* <li><b>Using a JNDI Context</b><BR>
* Available providers can be registered on a JNDI
* context, and the <code>SyncFactory</code> will attempt to load
* <code>SyncProvider</code> implementations from that JNDI context.
* For example, the following code fragment registers a provider implementation
* on a JNDI context. This is something a deployer would normally do. In this
* example, <code>MyProvider</code> is being registered on a CosNaming
* namespace, which is the namespace used by J2EE resources.
* <PRE>
* import javax.naming.*;
*
* Hashtable svrEnv = new Hashtable();
* srvEnv.put(Context.INITIAL_CONTEXT_FACTORY, "CosNaming");
*
* Context ctx = new InitialContext(svrEnv);
* com.fred.providers.MyProvider = new MyProvider();
* ctx.rebind("providers/MyProvider", syncProvider);
* </PRE>
* </ul>
* Next, an application will register the JNDI context with the
* <code>SyncFactory</code> instance. This allows the <code>SyncFactory</code>
* to browse within the JNDI context looking for <code>SyncProvider</code>
* implementations.
* <PRE>
* Hashtable appEnv = new Hashtable();
* appEnv.put(Context.INITIAL_CONTEXT_FACTORY, "CosNaming");
* appEnv.put(Context.PROVIDER_URL, "iiop://hostname/providers");
* Context ctx = new InitialContext(appEnv);
*
* SyncFactory.registerJNDIContext(ctx);
* </PRE>
* If a <code>RowSet</code> object attempts to obtain a <code>MyProvider</code>
* object, the <code>SyncFactory</code> will try to locate it. First it searches
* for it in the system properties, then it looks in the resource files, and
* finally it checks the JNDI context that has been set. The <code>SyncFactory</code>
* instance verifies that the requested provider is a valid extension of the
* <code>SyncProvider</code> abstract class and then gives it to the
* <code>RowSet</code> object. In the following code fragment, a new
* <code>CachedRowSet</code> object is created and initialized with
* <i>env</i>, which contains the binding to <code>MyProvider</code>.
* <PRE>
* Hashtable env = new Hashtable();
* env.put(SyncFactory.ROWSET_SYNC_PROVIDER, "com.fred.providers.MyProvider");
* CachedRowSet crs = new com.sun.rowset.CachedRowSetImpl(env);
* </PRE>
* Further details on these mechanisms are available in the
* <code>javax.sql.rowset.spi</code> package specification.
*
* @author Jonathan Bruce
* @see javax.sql.rowset.spi.SyncProvider
* @see javax.sql.rowset.spi.SyncFactoryException
*/
public class SyncFactory {
/*
* The variable that represents the singleton instance
* of the <code>SyncFactory</code> class.
*/
private static SyncFactory syncFactory = null;
/**
* Creates a new <code>SyncFactory</code> object, which is the singleton
* instance.
* Having a private constructor guarantees that no more than
* one <code>SyncProvider</code> object can exist at a time.
*/
private SyncFactory() {};
/**
* The standard property-id for a synchronization provider implementation
* name.
*/
public static String ROWSET_SYNC_PROVIDER =
"rowset.provider.classname";
/**
* The standard property-id for a synchronization provider implementation
* vendor name.
*/
public static String ROWSET_SYNC_VENDOR =
"rowset.provider.vendor";
/**
* The standard property-id for a synchronization provider implementation
* version tag.
*/
public static String ROWSET_SYNC_PROVIDER_VERSION =
"rowset.provider.version";
/**
* The standard resource file name.
*/
private static String ROWSET_PROPERTIES = "rowset.properties";
/**
* The RI Optimistic Provider.
*/
private static String default_provider =
"com.sun.rowset.providers.RIOptimisticProvider";
/**
* The initial JNDI context where <code>SyncProvider</code> implementations can
* be stored and from which they can be invoked.
*/
private static Context ic;
/**
* The <code>Logger</code> object to be used by the <code>SyncFactory</code>.
*/
private static Logger rsLogger;
/**
*
*/
private static Level rsLevel;
/**
* The registry of available <code>SyncProvider</code> implementations.
* See section 2.0 of the class comment for <code>SyncFactory</code> for an
* explanation of how a provider can be added to this registry.
*/
private static Hashtable implementations;
/**
* Internal sync object used to maintain the SPI as a singleton
*/
private static Object logSync = new Object();
/**
* Internal PrintWriter field for logging facility
*/
private static java.io.PrintWriter logWriter = null;
/**
* Adds the the given synchronization provider to the factory register. Guidelines
* are provided in the <code>SyncProvider</code> specification for the
* required naming conventions for <code>SyncProvider</code>
* implementations.
* <p>
* Synchronization providers bound to a JNDI context can be
* registered by binding a SyncProvider instance to a JNDI namespace.
* <ul>
* <pre>
* SyncProvider p = new MySyncProvider();
* InitialContext ic = new InitialContext();
* ic.bind ("jdbc/rowset/MySyncProvider", p);
* </pre>
* </ul>
* Furthermore, an initial JNDI context should be set with the
* <code>SyncFactory</code> using the <code>setJNDIContext</code> method.
* The <code>SyncFactory</code> leverages this context to search for
* available <code>SyncProvider</code> objects bound to the JNDI
* context and its child nodes.
*
* @param providerID A <code>String</code> object with the unique ID of the
* synchronization provider being registered
* @throws SyncFactoryException if an attempt is made to supply an empty
* or null provider name
* @see #setJNDIContext
*/
public static synchronized void registerProvider(String providerID)
throws SyncFactoryException {
ProviderImpl impl = new ProviderImpl();
impl.setClassname(providerID);
initMapIfNecessary();
implementations.put(providerID, impl);
}
/**
* Returns the <code>SyncFactory</code> singleton.
*
* @return the <code>SyncFactory</code> instance
*/
public static SyncFactory getSyncFactory(){
// This method uses the Singleton Design Pattern
// with Double-Checked Locking Pattern for
// 1. Creating single instance of the SyncFactory
// 2. Make the class thread safe, so that at one time
// only one thread enters the synchronized block
// to instantiate.
// if syncFactory object is already there
// don't go into synchronized block and return
// that object.
// else go into synchronized block
if(syncFactory == null){
synchronized(SyncFactory.class) {
if(syncFactory == null){
syncFactory = new SyncFactory();
} //end if
} //end synchronized block
} //end if
return syncFactory;
}
/**
* Removes the designated currently registered synchronization provider from the
* Factory SPI register.
*
* @param providerID The unique-id of the synchronization provider
* @throws SyncFactoryException If an attempt is made to
* unregister a SyncProvider implementation that was not registered.
*/
public static synchronized void unregisterProvider(String providerID)
throws SyncFactoryException {
initMapIfNecessary();
if (implementations.containsKey(providerID)) {
implementations.remove(providerID);
}
}
private static String colon = ":";
private static String strFileSep = "/";
private static synchronized void initMapIfNecessary() throws SyncFactoryException {
// Local implementation class names and keys from Properties
// file, translate names into Class objects using Class.forName
// and store mappings
Properties properties = new Properties();
if (implementations == null) {
implementations = new Hashtable();
try {
// check if user is supplying his Synchronisation Provider
// Implementation if not use Sun's implementation.
// properties.load(new FileInputStream(ROWSET_PROPERTIES));
// The rowset.properties needs to be in jdk/jre/lib when
// integrated with jdk.
// else it should be picked from -D option from command line.
// -Drowset.properties will add to standard properties. Similar
// keys will over-write
/*
* Dependent on application
*/
String strRowsetProperties = System.getProperty("rowset.properties");
if ( strRowsetProperties != null) {
// Load user's implementation of SyncProvider
// here. -Drowset.properties=/abc/def/pqr.txt
ROWSET_PROPERTIES = strRowsetProperties;
properties.load(new FileInputStream(ROWSET_PROPERTIES));
parseProperties(properties);
}
/*
* Always available
*/
ROWSET_PROPERTIES = "javax" + strFileSep + "sql" +
strFileSep + "rowset" + strFileSep +
"rowset.properties";
// properties.load(
// ClassLoader.getSystemResourceAsStream(ROWSET_PROPERTIES));
ClassLoader cl = Thread.currentThread().getContextClassLoader();
properties.load(cl.getResourceAsStream(ROWSET_PROPERTIES));
parseProperties(properties);
// removed else, has properties should sum together
} catch (FileNotFoundException e) {
throw new SyncFactoryException("Cannot locate properties file: " + e);
} catch (IOException e) {
throw new SyncFactoryException("IOException: " + e);
}
/*
* Now deal with -Drowset.provider.classname
* load additional properties from -D command line
*/
properties.clear();
String providerImpls = System.getProperty(ROWSET_SYNC_PROVIDER);
if (providerImpls != null) {
int i = 0;
if (providerImpls.indexOf(colon) > 0) {
StringTokenizer tokenizer = new StringTokenizer(providerImpls, colon);
while (tokenizer.hasMoreElements()) {
properties.put(ROWSET_SYNC_PROVIDER + "." + i, tokenizer.nextToken());
i++;
}
} else {
properties.put(ROWSET_SYNC_PROVIDER, providerImpls);
}
parseProperties(properties);
}
}
}
/**
* The internal boolean switch that indicates whether a JNDI
* context has been established or not.
*/
private static boolean jndiCtxEstablished = false;
/**
* The internal debug switch.
*/
private static boolean debug = false;
/**
* Internal registry count for the number of providers contained in the
* registry.
*/
private static int providerImplIndex = 0;
/**
* Internal handler for all standard property parsing. Parses standard
* ROWSET properties and stores lazy references into the the internal registry.
*/
private static void parseProperties(Properties p) {
ProviderImpl impl = null;
String key = null;
String[] propertyNames = null;
for (Enumeration e = p.propertyNames(); e.hasMoreElements() ;) {
String str = (String)e.nextElement();
int w = str.length();
if (str.startsWith(SyncFactory.ROWSET_SYNC_PROVIDER)) {
impl = new ProviderImpl();
impl.setIndex(providerImplIndex++);
if (w == (SyncFactory.ROWSET_SYNC_PROVIDER).length()) {
// no property index has been set.
propertyNames = getPropertyNames(false);
} else {
// property index has been set.
propertyNames = getPropertyNames(true, str.substring(w-1));
}
key = p.getProperty(propertyNames[0]);
impl.setClassname(key);
impl.setVendor(p.getProperty(propertyNames[1]));
impl.setVersion(p.getProperty(propertyNames[2]));
implementations.put(key, impl);
}
}
}
/**
* Used by the parseProperties methods to disassemble each property tuple.
*/
private static String[] getPropertyNames(boolean append) {
return getPropertyNames(append, null);
}
/**
* Disassembles each property and its associated value. Also handles
* overloaded property names that contain indexes.
*/
private static String[] getPropertyNames(boolean append,
String propertyIndex) {
String dot = ".";
String[] propertyNames =
new String[] {SyncFactory.ROWSET_SYNC_PROVIDER,
SyncFactory.ROWSET_SYNC_VENDOR,
SyncFactory.ROWSET_SYNC_PROVIDER_VERSION};
if (append) {
for (int i = 0; i < propertyNames.length; i++) {
propertyNames[i] = propertyNames[i] +
dot +
propertyIndex;
}
return propertyNames;
} else {
return propertyNames;
}
}
/**
* Internal debug method that outputs the registry contents.
*/
private static void showImpl(ProviderImpl impl) {
System.out.println("Provider implementation:");
System.out.println("Classname: " + impl.getClassname());
System.out.println("Vendor: " + impl.getVendor());
System.out.println("Version: " + impl.getVersion());
System.out.println("Impl index: " + impl.getIndex());
}
/**
* Returns the <code>SyncProvider</code> instance identified by <i>providerID</i>.
*
* @param providerID the unique identifier of the provider
* @return a <code>SyncProvider</code> implementation
* @throws SyncFactoryException If the SyncProvider cannot be found or
* some error was encountered when trying to invoke this provider.
*/
public static SyncProvider getInstance(String providerID)
throws SyncFactoryException {
initMapIfNecessary(); // populate HashTable
initJNDIContext(); // check JNDI context for any additional bindings
ProviderImpl impl = (ProviderImpl)implementations.get(providerID);
if (impl == null) {
// Requested SyncProvider is unavailable. Return default provider.
return new com.sun.rowset.providers.RIOptimisticProvider();
}
// Attempt to invoke classname from registered SyncProvider list
Class c = null;
try {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
/**
* The SyncProvider implementation of the user will be in
* the classpath. We need to find the ClassLoader which loads
* this SyncFactory and try to laod the SyncProvider class from
* there.
**/
c = Class.forName(providerID, true, cl);
if (c != null) {
return (SyncProvider)c.newInstance();
} else {
return new com.sun.rowset.providers.RIOptimisticProvider();
}
} catch (IllegalAccessException e) {
throw new SyncFactoryException("IllegalAccessException: " + e.getMessage());
} catch (InstantiationException e) {
throw new SyncFactoryException("InstantiationException: " + e.getMessage());
} catch (ClassNotFoundException e) {
throw new SyncFactoryException("ClassNotFoundException: " + e.getMessage());
}
}
/**
* Returns an Enumeration of currently registered synchronization
* providers. A <code>RowSet</code> implementation may use any provider in
* the enumeration as its <code>SyncProvider</code> object.
* <p>
* At a minimum, the reference synchronization provider allowing
* RowSet content data to be stored using a JDBC driver should be
* possible.
*
* @return Enumeration A enumeration of available synchronization
* providers that are registered with this Factory
*/
public static Enumeration<SyncProvider> getRegisteredProviders()
throws SyncFactoryException {
initMapIfNecessary();
// return a collection of classnames
// of type SyncProvider
return implementations.elements();
}
/**
* Sets the logging object to be used by the <code>SyncProvider</code>
* implementation provided by the <code>SyncFactory</code>. All
* <code>SyncProvider</code> implementations can log their events to
* this object and the application can retrieve a handle to this
* object using the <code>getLogger</code> method.
*
* @param logger A Logger object instance
*/
public static void setLogger(Logger logger) {
rsLogger = logger;
}
/**
* Sets the logging object that is used by <code>SyncProvider</code>
* implementations provided by the <code>SyncFactory</code> SPI. All
* <code>SyncProvider</code> implementations can log their events
* to this object and the application can retrieve a handle to this
* object using the <code>getLogger</code> method.
*
* @param logger a Logger object instance
* @param level a Level object instance indicating the degree of logging
* required
*/
public static void setLogger(Logger logger, Level level) {
// singleton
rsLogger = logger;
rsLogger.setLevel(level);
}
/**
* Returns the logging object for applications to retrieve
* synchronization events posted by SyncProvider implementations.
*
* @throws SyncFactoryException if no logging object has been set.
*/
public static Logger getLogger() throws SyncFactoryException {
// only one logger per session
if(rsLogger == null){
throw new SyncFactoryException("(SyncFactory) : No logger has been set");
}
return rsLogger;
}
/**
* Sets the initial JNDI context from which SyncProvider implementations
* can be retrieved from a JNDI namespace
*
* @param ctx a valid JNDI context
* @throws SyncFactoryException if the supplied JNDI context is null
*/
public static void setJNDIContext(javax.naming.Context ctx)
throws SyncFactoryException {
if (ctx == null) {
throw new SyncFactoryException("Invalid JNDI context supplied");
}
ic = ctx;
jndiCtxEstablished = true;
}
/**
* Controls JNDI context intialization.
*
* @throws SyncFactoryException if an error occurs parsing the JNDI context
*/
private static void initJNDIContext() throws SyncFactoryException {
if (jndiCtxEstablished && (ic != null) && (lazyJNDICtxRefresh == false)) {
try {
parseProperties(parseJNDIContext());
lazyJNDICtxRefresh = true; // touch JNDI namespace once.
} catch (NamingException e) {
e.printStackTrace();
throw new SyncFactoryException("SPI: NamingException: " + e.getExplanation());
} catch (Exception e) {
e.printStackTrace();
throw new SyncFactoryException("SPI: Exception: " + e.getMessage());
}
}
}
/**
* Internal switch indicating whether the JNDI namespace should be re-read.
*/
private static boolean lazyJNDICtxRefresh = false;
/**
* Parses the set JNDI Context and passes bindings to the enumerateBindings
* method when complete.
*/
private static Properties parseJNDIContext() throws NamingException {
NamingEnumeration bindings = ic.listBindings("");
Properties properties = new Properties();
// Hunt one level below context for available SyncProvider objects
enumerateBindings(bindings, properties);
return properties;
}
/**
* Scans each binding on JNDI context and determines if any binding is an
* instance of SyncProvider, if so, add this to the registry and continue to
* scan the current context using a re-entrant call to this method until all
* bindings have been enumerated.
*/
private static void enumerateBindings(NamingEnumeration bindings,
Properties properties) throws NamingException {
boolean syncProviderObj = false; // move to parameters ?
try {
Binding bd = null;
Object elementObj = null;
String element = null;
while (bindings.hasMore()) {
bd = (Binding)bindings.next();
element = bd.getName();
elementObj = bd.getObject();
if (!(ic.lookup(element) instanceof Context)) {
// skip directories/sub-contexts
if (ic.lookup(element) instanceof SyncProvider) {
syncProviderObj = true;
}
}
if (syncProviderObj) {
SyncProvider sync = (SyncProvider)elementObj;
properties.put(SyncFactory.ROWSET_SYNC_PROVIDER,
sync.getProviderID());
syncProviderObj = false; // reset
}
}
} catch (javax.naming.NotContextException e) {
bindings.next();
// Re-entrant call into method
enumerateBindings(bindings, properties);
}
}
}
/**
* Internal class that defines the lazy reference construct for each registered
* SyncProvider implementation.
*/
class ProviderImpl extends SyncProvider {
private String className = null;
private String vendorName = null;
private String ver = null;
private int index;
public void setClassname(String classname) {
className = classname;
}
public String getClassname() {
return className;
}
public void setVendor(String vendor) {
vendorName = vendor;
}
public String getVendor() {
return vendorName;
}
public void setVersion(String providerVer) {
ver = providerVer;
}
public String getVersion() {
return ver;
}
public void setIndex(int i) {
index = i;
}
public int getIndex() {
return index;
}
public int getDataSourceLock() throws SyncProviderException {
int dsLock = 0;
try
{
dsLock = SyncFactory.getInstance(className).getDataSourceLock();
} catch(SyncFactoryException sfEx) {
throw new SyncProviderException(sfEx.getMessage());
}
return dsLock;
}
public int getProviderGrade() {
int grade = 0;
try
{
grade = SyncFactory.getInstance(className).getProviderGrade();
} catch(SyncFactoryException sfEx) {
//
}
return grade;
}
public String getProviderID() {
return className;
}
/*
public javax.sql.RowSetInternal getRowSetInternal() {
try
{
return SyncFactory.getInstance(className).getRowSetInternal();
} catch(SyncFactoryException sfEx) {
//
}
}
*/
public javax.sql.RowSetReader getRowSetReader() {
RowSetReader rsReader = null;;
try
{
rsReader = SyncFactory.getInstance(className).getRowSetReader();
} catch(SyncFactoryException sfEx) {
//
}
return rsReader;
}
public javax.sql.RowSetWriter getRowSetWriter() {
RowSetWriter rsWriter = null;
try
{
rsWriter = SyncFactory.getInstance(className).getRowSetWriter();
} catch(SyncFactoryException sfEx) {
//
}
return rsWriter;
}
public void setDataSourceLock(int param)
throws SyncProviderException {
try
{
SyncFactory.getInstance(className).setDataSourceLock(param);
} catch(SyncFactoryException sfEx) {
throw new SyncProviderException(sfEx.getMessage());
}
}
public int supportsUpdatableView() {
int view = 0;
try
{
view = SyncFactory.getInstance(className).supportsUpdatableView();
} catch(SyncFactoryException sfEx) {
//
}
return view;
}
}