/** * Helios, OpenSource Monitoring * Brought to you by the Helios Development Group * * Copyright 2007, Helios Development Group and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * */ package org.helios.collector.jdbc.binding.provider; import org.apache.log4j.Logger; import org.helios.collector.jdbc.binding.binder.Binder; import org.helios.collector.jdbc.binding.binder.IBinder; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Collections; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; /** * <p>Title: AbstractBindVariableProvider</p> * <p>Description: Base abstract class for bind variable provider implementations.</p> * <p>Company: Helios Development Group</p> * @author Whitehead (whitehead.nicholas@gmail.com) * @version $LastChangedRevision$ * $HeadURL$ * $Id$ */ public abstract class AbstractBindVariableProvider implements IBindVariableProvider, ApplicationContextAware { /** The value to be bound to the statement */ protected AtomicReference<Object> bindValue = new AtomicReference<Object>(); /** the unique bind token key for this provider class */ protected final String tokenKey = getMyTokenKey(); /** The instance bind token */ protected String bindToken = null; /** The custom configuration string */ protected String config = null; /** The provider's binder */ protected IBinder binder = null; /** The spring application context */ protected ApplicationContext appContext = null; /** A set of provider listeners */ protected Set<IBindVariableProviderListener> listeners = new CopyOnWriteArraySet<IBindVariableProviderListener>(); /** The forceNoBind flag */ protected AtomicBoolean forceNoBind = new AtomicBoolean(false); /** Instance logger */ protected Logger log = Logger.getLogger(getClass()); /** * Package protected constructor to avoid direct construction of providers. */ AbstractBindVariableProvider() { if(log.isDebugEnabled()) log.debug("Constructing Provider [" + getClass().getName() + "]" ); try { Binder binderAnn = this.getClass().getAnnotation(Binder.class); if(binderAnn!=null) { String name = binderAnn.name(); if(this.getClass().equals(BindVariableProviderFactory.getInstance().getBinderClass(name))) { this.binder = (IBinder)this; } else { String config = "".equals(binderAnn.config()) ? null : binderAnn.config(); this.binder = BindVariableProviderFactory.getInstance().getBinder(name + config); } } } catch (Exception e) { log.warn("Failed to initialize binder from annotation", e); } if(log.isDebugEnabled()) log.debug("Constructed Provider [" + getClass().getName() + "]" ); } /** * Retrieves the forceNoBind state. If true, the binder will issue a replace instead of a bind. * @return the forceNoBind state. */ public boolean isForceNoBind() { return forceNoBind.get(); } /** * Sets the forceNoBind boolean. If true, the binder will issue a replace instead of a bind. * @param forceNoBind If true, the binder will issue a replace instead of a bind. */ public void setForceNoBind(boolean forceNoBind) { this.forceNoBind.set(forceNoBind); } /** * Introspects the token key from the annotation. * @return the token key. */ protected String getMyTokenKey() { try { BindVariableProvider bvp = this.getClass().getAnnotation(BindVariableProvider.class); return bvp==null ? null : bvp.tokenKey(); } catch (Exception e) { return null; } } /** * Executes a bind against a prepared statement. * @param ps The prepared statement to invoke the bind against. * @param bindSequence The bind sequence. * @return The final bind sequence executed by this bind call. Normally, this would be the same as <code>bindSequence</code>. * @throws SQLException */ public int bind(PreparedStatement ps, int bindSequence) throws SQLException { return this.getIBinder().bind(ps, bindSequence, getValue()); } /** * Executes a token substitution against a sql statement for the passed bind token, replacing the token with the provider supplied literal. * Intended to support binding-like operations when using drivers that do not support <code>PreparedStatements</code> and/or bind variables. * @param sql The tokenized sql statement * @param bindToken The bind token to replace with a literal binding. * @return The substituted sql statement. * @throws SQLException */ public CharSequence bind(CharSequence sql, String bindToken) throws SQLException { return this.getIBinder().bind(sql, bindToken, getValue()); } /** * Returns the instance specific bind token. * @return the bind token. * @see org.helios.collectors.jdbc.binding.provider.IBindVariableProvider#getBindToken() */ public String getBindToken() { return bindToken; } /** * Sets the bind token that uniquely identifies an instance of this provider. * @param the bind token for this provider. */ public void setBindToken(String bindToken) { this.bindToken = bindToken; } /** * The bind token key that uniquely the key of this provider's class. * @return the bind token key for this provider. */ public String getBindTokenKey() { return tokenKey; } /** * Returns the instance's binder. * @return the binder. * @see org.helios.collectors.jdbc.binding.provider.IBindVariableProvider#getIBinder() */ public IBinder getIBinder() { return binder; } /** * Overrides a provider's binder * @param binder the overriding binder instance. */ public void setBinder(IBinder binder) { this.binder = binder; } /** * Returns the provider's current value. * @return the provider's value. * @see org.helios.collectors.jdbc.binding.provider.IBindVariableProvider#getValue() */ public Object getValue() { return bindValue.get(); } /** * Concrete implementations should implement this to configure themselves. * @param config The configuration string extracted from the bind token * @throws BindProviderConfigurationException */ public void configureProvider(String config) throws BindProviderConfigurationException{}; /** * Set's the provider's bind value. * @param value the provider's new bind value. * @see org.helios.collectors.jdbc.binding.provider.IBindVariableProvider#setValue(java.lang.Object) */ public void setValue(Object value) { Object oldValue = bindValue.get(); bindValue.set(value); if(oldValue==null && value==null) return; if((oldValue==null && value!=null) || (value==null && oldValue!=null) ) { BindVariableProviderFactory.getInstance().fireProviderValueChanged(Collections.unmodifiableSet(listeners), this, oldValue, value); } else if(value!=null && !value.equals(oldValue)) { BindVariableProviderFactory.getInstance().fireProviderValueChanged(Collections.unmodifiableSet(listeners), this, oldValue, value); } else if(oldValue!=null && !oldValue.equals(value)) { BindVariableProviderFactory.getInstance().fireProviderValueChanged(Collections.unmodifiableSet(listeners), this, oldValue, value); } } /** * Registers a new IBindVariableProviderListener * @param listener the listener to register. */ public void registerListener(IBindVariableProviderListener listener) { if(listener!=null) { listeners.add(listener); } } /** * Unregisters a IBindVariableProviderListener * @param listener the listener to unregister. */ public void removeListener(IBindVariableProviderListener listener) { if(listener!=null) { listeners.remove(listener); } } /** * Sets the spring application context * @param appContext the spring application context * @throws BeansException * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) */ public void setApplicationContext(ApplicationContext appContext) throws BeansException { this.appContext = appContext; } }