/** * This class implements methods to access PIM data in a data store. * @author Diorgenes Felipe Grzesiuk * @copyright Copyright 2007-2008 Prognus * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 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 Foobar; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package br.com.prognus.psync.items.dao; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Timestamp; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import javax.naming.NamingException; import javax.sql.DataSource; import br.com.prognus.psync.exception.PIMDBAccessException; import br.com.prognus.psync.util.Def; import com.funambol.common.pim.common.Property; import com.funambol.framework.logging.FunambolLogger; import com.funambol.framework.logging.FunambolLoggerFactory; import com.funambol.framework.tools.DBTools; import com.funambol.framework.tools.DataSourceTools; import com.novell.ldap.LDAPAttribute; import com.novell.ldap.LDAPAttributeSet; import com.novell.ldap.LDAPConnection; import com.novell.ldap.LDAPEntry; import com.novell.ldap.LDAPException; import com.novell.ldap.LDAPSearchResults; import com.novell.ldap.util.Base64; public abstract class PIMEntityDAO { // ------------------------------------------------------------ Private data protected static final FunambolLogger log = FunambolLoggerFactory .getLogger(Def.LOGGER_NAME); protected String jndiDataSourceName; protected DataSource dataSource = null; // -------------------------------------------------------------- Properties protected String userId; public String getUserId() { return userId; } // ------------------------------------------------------------ Constructors /** * Creates a new instance of PIMEntityDAO, ready to be linked to a given * DataSource. * * @param jndiDataSourceName * the jndiname of the datasource to use * @param userId * corresponding to the "userid" field of the fun_pim_contact * table * @throws IllegalArgumentException * if there are errors looking up the dataSource with the given * jndiDataSourceName */ public PIMEntityDAO(String jndiDataSourceName, String userId) { Connection con = null; PreparedStatement ps = null; try { this.dataSource = DataSourceTools .lookupDataSource(jndiDataSourceName); } catch (NamingException ex) { throw new IllegalArgumentException("Error looking up datasource: " + jndiDataSourceName, ex); } try { con = getDataSource().getConnection(); PreparedStatement ldap_server = con .prepareStatement("select config_value from phpgw_config WHERE config_name = 'ldap_host' AND config_app = 'phpgwapi'"); ResultSet rs1 = ldap_server.executeQuery(); PreparedStatement ldap_dc = con .prepareStatement("select config_value from phpgw_config WHERE config_name = 'ldap_context' AND config_app = 'phpgwapi'"); ResultSet rs2 = ldap_dc.executeQuery(); rs1.next(); rs2.next(); String server = rs1.getString(1); String dc = rs2.getString(1); this.jndiDataSourceName = jndiDataSourceName; this.userId = LdapUID(server, dc, userId); } catch (Exception e) { throw new IllegalArgumentException("Error UID ldap", e); } finally { DBTools.close(con, ps, null); } } // -----------------------------------------------------------Public methods protected String LdapUID(String server, String dc, String user) throws Exception { LDAPConnection ldap = new LDAPConnection(); String searchAttrs[] = { "uidNumber" }; String id_owner = null; try { ldap.connect(server, 389); LDAPSearchResults searchResults = ldap.search(dc, LDAPConnection.SCOPE_SUB, "(uid=" + user + ")", searchAttrs, false); while (searchResults.hasMore()) { LDAPEntry nextEntry = null; try { nextEntry = searchResults.next(); } catch (LDAPException e) { if (e.getResultCode() == LDAPException.LDAP_TIMEOUT || e.getResultCode() == LDAPException.CONNECT_ERROR) break; else continue; } LDAPAttributeSet attributeSet = nextEntry.getAttributeSet(); Iterator allAttributes = attributeSet.iterator(); while (allAttributes.hasNext()) { LDAPAttribute attribute = (LDAPAttribute) allAttributes .next(); String attributeName = attribute.getName(); Enumeration allValues = attribute.getStringValues(); if (allValues != null) { while (allValues.hasMoreElements()) { String Value = (String) allValues.nextElement(); if (Base64.isLDIFSafe(Value)) { } else { Value = Base64.encode(Value.getBytes()); } if (attributeName.equalsIgnoreCase("uidNumber")) { id_owner = Value; break; } } } } } ldap.disconnect(); if (id_owner == null) { throw new LDAPException(); } } catch (LDAPException e) { log.info("Error while connecting and binding to LDAP: " + e.toString()); } return id_owner; } /** * Looks up the data source, making some guess attempts based on * jndiDataSourceName. * * @throws Exception */ protected DataSource getDataSource() throws Exception { return dataSource; } public abstract List getAllItems() throws PIMDBAccessException; public abstract void removeItem(String uid) throws PIMDBAccessException; public abstract void removeAllItems() throws PIMDBAccessException; public abstract char getItemState(String uid, Timestamp since) throws PIMDBAccessException; /** * Retrieves the UID list of the new items belonging to the user filtered * according to the given time interval. * * @param since * the earliest allowed last-update Timestamp * @param to * the latest allowed last-update Timestamp * @throws PIMDBAccessException * @return a List of UIDs (as String objects) */ public List getNewItems(Timestamp since, Timestamp to) throws PIMDBAccessException { log.info("\n\n==>DAO start getNewItems"); return getItemsHavingStatus(since, to, 'N'); } /** * Retrieves the UID list of the deleted items belonging to the user * filtered according to the given time interval. * * @param since * the earliest allowed last-update Timestamp * @param to * the latest allowed last-update Timestamp * @throws PIMDBAccessException * @return a List of UIDs (as String objects) */ public List getDeletedItems(Timestamp since, Timestamp to) throws PIMDBAccessException { log.info("\n\n==>DAO start getDeletedItems"); return getItemsHavingStatus(since, to, 'D'); } /** * Retrieves the UID list of the updated items belonging to the user * filtered according to the given time interval. * * @param since * the earliest allowed last-update Timestamp * @param to * the latest allowed last-update Timestamp * @throws PIMDBAccessException * @return a List of UIDs (as String objects) */ public List getUpdatedItems(Timestamp since, Timestamp to) throws PIMDBAccessException { log.info("\n\n==>DAO start getUpdatedItems"); return getItemsHavingStatus(since, to, 'U'); } // ---------------------------------------------------------- Private // methods /** * Retrieves the UID list of the items belonging to the user filtered * according to the given time interval and status. * * @param since * the earliest allowed last-update Timestamp * @param to * the latest allowed last-update Timestamp * @param status * 'D' for deleted items, 'N' for new items, 'U' for updated * items * @throws PIMDBAccessException * @return a List of UIDs (as String objects) */ protected abstract List getItemsHavingStatus(Timestamp since, Timestamp to, char status) throws PIMDBAccessException; /** * Checks (safely) whether the property is unset. * * @param property * may be null * @return false only if the property value is a non-null, but possibly * empty (""), string * * @see PIMEntityDAO#stringFrom(Property) */ protected static boolean isNullProperty(Property property) { return (property == null || property.getPropertyValueAsString() == null); } /** * Checks (safely) whether the property is unset or set to an empty string. * * @param property * may be null * @return false only if the property value is a non-null non-empty string * * @see PIMEntityDAO#stringFrom(Property, boolean) */ protected static boolean isEmptyProperty(Property property) { if (property == null) { return true; } String string = property.getPropertyValueAsString(); if (string == null || string.length() == 0) { return true; } return false; } /** * Extract a string from a property in a safe way. An empty string ("") is * considered as an acceptable value for the property: in such a case, an * empty String object will be returned. * * @param property * may be null * @return if existing, the property value will be returned as a String * object */ protected static String stringFrom(Property property) { if (property == null) { return null; } return property.getPropertyValueAsString(); } /** * Extract a string from a property in a safe way. This method is not * currently used, but it could be useful in the future for determining the * behaviour of the connector when dealing with empty properties. A field * whose value is extracted with stringFrom(..., true) will not be updated * in case its value is set to ""; a field whose value is extracted with * stringFrom(..., false) will be considered as explicitly kept blank if its * value is "". This means that single field deletions can be made tunable. * * @param property * may be null * @param emptyImpliesNull * if true, an empty string ("") will be treated as if it were * null; otherwise, in such a case an empty String object will be * returned * * @return if existing (and not empty if emptyImpliesNull is true), the * property value will be returned as a String object */ protected static String stringFrom(Property property, boolean emptyImpliesNull) { if (property == null) { return null; } String string = property.getPropertyValueAsString(); if (string == null || !emptyImpliesNull) { return string; } if (string.length() == 0) { return null; } return string; } /** * Gets a substring in a safe way. * * @param string * may be null or longer than maxLength * @param maxLength * @return null if the string is null, a truncated substring of string * otherwise */ protected static String truncate(String string, int maxLength) { if (string == null || string.length() <= maxLength) { return string; } return string.substring(0, maxLength); } }