vendredi 8 janvier 2016

How to identify non thread-safe code in a multi-threaded environment?

I have designed and implemented a simple webstore based on traditional MVC Model 1 architecture using pure JSP and JavaBeans (Yes, I still use that legacy technology in my pet projects ;)).

I am using DAO design pattern to implement my persistence layer for a webstore. But I am not sure if I have implemented the classes correctly in my DAO layer. I am specifically concerned about the QueryExecutor.java and DataPopulator.java classes (mentioned below). All the methods in both these classes are defined as static which makes me think if this is the correct approach in multithreaded environment. Hence, I have following questions regarding the static methods.

  1. Will there be synchronization issues when multiple users are trying to do a checkout with different products? If answer to the above question is yes, then how can I actually reproduce this synchronization issue?
  2. Are there any testing/tracing tools available which will actually show that a specific piece of code will/might create synchronization issues in a multithreaded environment? Can I see that a User1 was trying to access Product-101 but was displayed Product-202 because of non thread-safe code?
  3. Assuming there are synchronization issues; Should these methods be made non-static and classes instantitable so that we can create an instance using new operator OR Should a synchronized block be placed around the non thread-safe code?

Please guide.

MasterDao.java

public interface MasterDao {
    Product getProduct(int productId) throws SQLException;
}

BaseDao.java

public abstract class BaseDao {
    protected DataSource dataSource;
    public BaseDao(DataSource dataSource) {
        this.dataSource = dataSource;
    }
}

MasterDaoImpl.java

public class MasterDaoImpl extends BaseDao implements MasterDao {

    private static final Logger LOG = Logger.getLogger(MasterDaoImpl.class);

    public MasterDaoImpl(DataSource dataSource) {
        super(dataSource);
    }

    @Override
    public Product getProduct(int productId) throws SQLException {
        Product product = null;
        String sql = "select * from products where product_id= " + productId;
        //STATIC METHOD CALL HERE, COULD THIS POSE A SYNCHRONIZATION ISSUE ??????
        List<Product> products = QueryExecutor.executeProductsQuery(dataSource.getConnection(), sql);
        if (!GenericUtils.isListEmpty(products)) {
            product = products.get(0);
        }
        return product;
    }

}

QueryExecutor.java

public final class QueryExecutor {

    private static final Logger LOG = Logger.getLogger(QueryExecutor.class);

    //SO CANNOT NEW AN INSTANCE
    private QueryExecutor() {
    }

    static List<Product> executeProductsQuery(Connection cn, String sql) {
        Statement stmt = null;
        ResultSet rs = null;
        List<Product> al = new ArrayList<>();

        LOG.debug(sql);

        try {
            stmt = cn.createStatement();
            rs = stmt.executeQuery(sql);
            while (rs != null && rs.next()) {
                //STATIC METHOD CALL HERE, COULD THIS POSE A SYNCHRONIZATION ISSUE ???????
                Product p = DataPopulator.populateProduct(rs); 
                al.add(p);
            }
            LOG.debug("al.size() = " + al.size());
            return al;
        } catch (Exception ex) {
            LOG.error("Exception while executing products query....", ex);
            return null;
        } finally {
            try {
                if (rs != null) {
                    rs.close();
                }
                if (stmt != null) {
                    stmt.close();
                }
                if (cn != null) {
                    cn.close();
                }
            } catch (Exception ex) {
                LOG.error("Exception while closing DB resources rs, stmt or cn.......", ex);
            }
        }
    }

}

DataPopulator.java

public class DataPopulator {

    private static final Logger LOG = Logger.getLogger(DataPopulator.class);

    //SO CANNOT NEW AN INSTANCE
    private DataPopulator() {
    }

    //STATIC METHOD DEFINED HERE, COULD THIS POSE A SYNCHRONIZATION ISSUE FOR THE CALLING METHODS ???????
    public static Product populateProduct(ResultSet rs) throws SQLException {
        String productId = GenericUtils.nullToEmptyString(rs.getString("PRODUCT_ID"));
        String name = GenericUtils.nullToEmptyString(rs.getString("NAME"));
        String image = GenericUtils.nullToEmptyString(rs.getString("IMAGE"));
        String listPrice = GenericUtils.nullToEmptyString(rs.getString("LIST_PRICE"));

        Product product = new Product(new Integer(productId), name, image, new BigDecimal(listPrice));
        LOG.debug("product = " + product);

        return product;
    }

}

Aucun commentaire:

Enregistrer un commentaire