/* Copyright 2011 Jose Maria Arranz Santamaria Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package jepl.impl; import jepl.impl.query.JEPLResultSetDAOListenerDefaultImpl; import java.sql.Connection; import java.sql.SQLException; import java.util.Collections; import java.util.HashMap; import java.util.Map; import javax.sql.DataSource; import jepl.JEPLBoot; import jepl.JEPLConnection; import jepl.JEPLDAL; import jepl.JEPLDAO; import jepl.JEPLDataSource; import jepl.JEPLException; import jepl.JEPLListener; import jepl.JEPLResultSetDAOListenerDefault; import jepl.JEPLResultSetDAOBeanMapper; import jepl.JEPLResultSetDAOListener; import jepl.JEPLUpdateDAOBeanMapper; import jepl.JEPLUpdateDAOListener; import jepl.JEPLUpdateDAOListenerDefault; import jepl.impl.query.JEPLBeanPropertyDescriptorImpl; import jepl.impl.query.JEPLUpdateColumnPropertyInfoList; import jepl.impl.query.JEPLUpdateDAOListenerDefaultImpl; /** * A la hora de heredar entre los sistemas NonJTA y JTA hay que conseguir que * JEPLayer funcionen sin dependencias JTA (clases java.transaction) * pues mientras el hilo de ejecución no pase por las clases JTA internas * no se intentarán cargar dependencias JTA * * @author jmarranz */ public abstract class JEPLDataSourceImpl implements JEPLDataSource { protected final JEPLBootImpl boot; protected final DataSource ds; protected final ThreadLocal<JEPLConnectionImpl> connectionByThread = new ThreadLocal<JEPLConnectionImpl>(); protected final JEPLListenerListImpl listenerList = new JEPLListenerListImpl(); protected final JEPLUserDataMultiThreadImpl userData = new JEPLUserDataMultiThreadImpl(); protected volatile boolean preparedStatementCached = true; protected volatile boolean inUse = false; // La verdad es que el volatile sobra pues es para detectar errores en tiempo de desarollo protected volatile boolean isC3PO = false; protected volatile Map<String,JEPLUpdateColumnPropertyInfoList> updateBeanInfoMap = Collections.synchronizedMap(new HashMap<String,JEPLUpdateColumnPropertyInfoList>()); public boolean generatedColumnMetadataIsSupported = true; // por defecto consideramos soportado // getJavaVersion() >= 1.7; public JEPLDataSourceImpl(JEPLBootImpl boot,DataSource ds) { this.boot = boot; this.ds = ds; } @Override public String[] getUserDataNames() { return userData.getUserDataNames(); } @Override public boolean containsName(String name) { return userData.containsName(name); } @Override public Object getUserData(String name) { return userData.getUserData(name); } @Override public <T> T getUserData(String name, Class<T> returnType) { return userData.getUserData(name, returnType); } @Override public Object setUserData(String name, Object value) { return userData.setUserData(name, value); } @Override public Object removeUserData(String name) { return userData.removeUserData(name); } @Override public <T> T removeUserData(String name, Class<T> returnType) { return userData.removeUserData(name, returnType); } public boolean isInUse() { return inUse; } public boolean isC3PO() { return isC3PO; } public void setIsC3PO(boolean isC3PO) { this.isC3PO = isC3PO; } public JEPLUpdateColumnPropertyInfoList getJEPLUpdateColumnPropertyInfoList(JEPLConnectionImpl jcon,String tableNameLowerCase,Map<String,JEPLBeanPropertyDescriptorImpl> propertyMap) throws SQLException { // Método multihilo JEPLUpdateColumnPropertyInfoList updateBeanInfo = updateBeanInfoMap.get(tableNameLowerCase); if (updateBeanInfo == null) { // No pasa nada porque haya una carrera de dos hilos, al final ganará el último y el contenio del objeto JEPLUpdateColumnPropertyInfoList será el mismo updateBeanInfo = new JEPLUpdateColumnPropertyInfoList(jcon,tableNameLowerCase,propertyMap); updateBeanInfoMap.put(tableNameLowerCase,updateBeanInfo); } return updateBeanInfo; } @Override public JEPLBoot getJEPLBoot() { return boot; } public JEPLBootImpl getJEPLBootImpl() { return boot; } @Override public DataSource getDataSource() { return ds; } @Override public boolean isPreparedStatementCached() { return preparedStatementCached; } @Override public void setPreparedStatementCached(boolean value) { checkIsInUse(); this.preparedStatementCached = value; } public JEPLListenerListImpl getJEPLListenerList() { return listenerList; } @Override public void addJEPLListener(JEPLListener listener) { if (listener instanceof JEPLResultSetDAOListener || listener instanceof JEPLUpdateDAOListener) throw new JEPLException("You cannot register a DAO listener in this level"); // Porque sólo se permite uno de cada tipo de listener y clases-modelo hay varias listenerList.addJEPLListener(listener); } @Override public void removeJEPLListener(JEPLListener listener) { listenerList.removeJEPLListener(listener); } @Override public JEPLConnection getCurrentJEPLConnection() { return getCurrentJEPLConnectionImpl(); // Puede ser null } public JEPLConnectionImpl getCurrentJEPLConnectionImpl() { return connectionByThread.get(); // Puede ser null } protected void checkIsInUse() { if (inUse) throw new JEPLException("DataSource is already in use"); } public JEPLConnectionImpl getJEPLConnectionFromPool() throws SQLException { Connection con = ds.getConnection(); return createJEPLConnection(con); // Este constructor no dará nunca error } public void returnJEPLConnectionToPool(JEPLConnectionImpl jcon) throws SQLException { jcon.closePreparedStatements(); Connection con = jcon.getConnection(); con.close(); // Devolver al pool } @Override public <T> JEPLResultSetDAOListenerDefault<T> createJEPLResultSetDAOListenerDefault(Class<T> clasz,JEPLResultSetDAOBeanMapper<T> mapper) { try { return new JEPLResultSetDAOListenerDefaultImpl<T>(clasz,mapper); } catch(Exception ex) { throw new JEPLException(ex); } } @Override public <T> JEPLResultSetDAOListenerDefault<T> createJEPLResultSetDAOListenerDefault(Class<T> clasz) { return createJEPLResultSetDAOListenerDefault(clasz,null); } @Override public <T> JEPLUpdateDAOListenerDefault<T> createJEPLUpdateDAOListenerDefault(Class<T> clasz,JEPLUpdateDAOBeanMapper<T> mapper) { try { return new JEPLUpdateDAOListenerDefaultImpl<T>(clasz,mapper); } catch(Exception ex) { throw new JEPLException(ex); } } @Override public <T> JEPLUpdateDAOListenerDefault<T> createJEPLUpdateDAOListenerDefault(Class<T> clasz) { return createJEPLUpdateDAOListenerDefault(clasz,null); } public <T> JEPLConnectionImpl pushJEPLTask(JEPLTaskExecContextInConnectionImpl<T> task) throws SQLException { this.inUse = true; listenerList.setInUse(); getJEPLBootImpl().setInUse(); JEPLConnectionImpl jcon = connectionByThread.get(); if (jcon == null) { jcon = getJEPLConnectionFromPool(); // Si ocurre una excepción será aquí no devolviendo la conexión connectionByThread.set(jcon); } jcon.pushJEPLTaskExecContex(task); task.setJEPLConnection(jcon); return jcon; } public <T> void popJEPLTask(JEPLTaskExecContextInConnectionImpl<T> task) throws SQLException { task.setJEPLConnection(null); JEPLConnectionImpl jcon = connectionByThread.get(); // jcon NO puede ser nulo if (jcon.popJEPLTaskExecContex() != task) throw new JEPLException("INTERNAL ERROR"); if (jcon.isEmptyOfJEPLTasks()) { // La vida del JEPLConnectionImpl coincide con la vida del Connection fuera del pool connectionByThread.set(null); returnJEPLConnectionToPool(jcon); // Devolver al pool } } @Override public JEPLDAL createJEPLDAL() { return new JEPLDALDefaultImpl(this); } @Override public <T> JEPLDAO<T> createJEPLDAO(Class<T> type) { return new JEPLDAOImpl<T>(this); } /* private static double getJavaVersion() { String version = System.getProperty("java.version"); // Ej. de 1.7.0_75 se obtiene 1.7 if ("0".equals(version)) return 1.7; // Es Android pero le damos una oportunidadde soportarlo http://developer.android.com/reference/java/lang/System.html int pos = version.indexOf('.'); pos = version.indexOf('.', pos+1); version = version.substring(0, pos); return Double.parseDouble(version); } */ public abstract <T> T execInternal(JEPLTaskOneExecWithConnectionImpl<T> task,JEPLDALImpl dal,JEPLListenerListImpl paramListener) throws Exception; public abstract JEPLConnectionImpl createJEPLConnection(Connection con) throws SQLException; }