joninx
joninx

Reputation: 1780

Correctly architecting Jersey WS + Hibernate to concurrency

I've developed a web service using Jersey 2 and Hibernate 5. I'll explain the architecture of Hibernate.

I use Dao pattern: only a unique DAO, despite the fact that I've a few classes. I haven't created any interface the Dao should implement. I assume th previous is not right at all in terms of acrchitecture. This is my Dao:

public class PtDao{

    /*
     * Atributos
     */
    private static PtDao INSTANCE = null;

    private Session currentSession;
    private Transaction currentTransaction;
    private SessionFactory factory;

    /*
     * Métodos
     */
    private PtDao() throws HibernateException, ConfigurationException{
        factory = getSessionFactory();
    }

    public static PtDao getInstance() throws HibernateException, ConfigurationException{
        if(INSTANCE == null){
            INSTANCE = new PtDao();
        }

        return INSTANCE;
    }

    public Session openCurrentSession() throws HibernateException {
        currentSession = factory.openSession();
        return currentSession;
    }

    public void closeCurrentSession() {
        currentSession.close();
        currentSession = null;
    }

    private SessionFactory getSessionFactory() throws HibernateException, ConfigurationException{

        if(getFactory() == null){
            Configuration cfg = new Configuration();

            cfg.setProperty("hibernate.dialect", "org.hibernate.spatial.dialect.postgis.PostgisDialect");
            cfg.setProperty("hibernate.connection.driver_class", "org.postgresql.Driver");

            PropertiesConfiguration config = PropertiesConfiguration.getInstance();

            String strUrl = "jdbc:postgresql://" + config.getDb_host() + ":" + config.getDb_port() + "/" + config.getDb_database();

            cfg.setProperty("hibernate.connection.url", strUrl);
            cfg.setProperty("hibernate.connection.username", config.getDb_user());
            cfg.setProperty("hibernate.connection.password", config.getDb_passwd());

            cfg.setProperty("hibernate.hbm2ddl.auto", "update");
            cfg.setProperty("hibernate.show_sql", "false");
            cfg.setProperty("hibernate.format_sql", "false");
            cfg.setProperty("hibernate.generate_statistics", "false");

            // C3p0 connection pool
            cfg.setProperty("connection.provider_class", "org.hibernate.connection.C3P0ConnectionProvider");
            cfg.setProperty("c3p0.min_size", "7");
            cfg.setProperty("c3p0.max_size", "10000");
            cfg.setProperty("c3p0.timeout", "1000");
            cfg.setProperty("c3p0.idle_test_period", "2000");
            cfg.setProperty("c3p0.preferredTestQuery", "select 1;");

            cfg.addAnnotatedClass(Tarifa.class);
            cfg.addAnnotatedClass(Provincia.class);
            cfg.addAnnotatedClass(Comarca.class);
            cfg.addAnnotatedClass(Municipio.class);
            cfg.addAnnotatedClass(Salto.class);

            StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder()
                    .applySettings(cfg.getProperties());
            setFactory(cfg.buildSessionFactory(builder.build()));

        }

        return getFactory();

    }

    public Session getCurrentSession() {
        return currentSession;
    }

    public void setCurrentSession(Session currentSession) {
        this.currentSession = currentSession;
    }

    public Transaction getCurrentTransaction() {
        return currentTransaction;
    }

    public void setCurrentTransaction(Transaction currentTransaction) {
        this.currentTransaction = currentTransaction;
    }

    public Transaction beginTransaction(){
        this.currentTransaction = currentSession.beginTransaction();
        return this.getCurrentTransaction();
    }

    public void commitTransaction(){
        if(currentTransaction != null)
            currentTransaction.commit();
    }

    public void rollbackTransaction(){
        if(currentTransaction != null)
            currentTransaction.rollback();
    }

    public SessionFactory getFactory() {
        return factory;
    }

    public void setFactory(SessionFactory factory) {
        this.factory = factory;
    }

    public void finish() throws HibernateException {
        Session session = getCurrentSession();
        if(session != null)
            session.close();
        if(factory != null)
            factory.close();
    }


    @SuppressWarnings("unchecked")
    public List<Municipio> getMunicipios (){
        Query q = currentSession.createQuery("from Municipio");
        return q.list();
    }

    public Municipio getMunicipioByCoor(Point coor) throws NoExisteMunicipioException{
        Criteria crit = currentSession.createCriteria(Municipio.class);
        crit.add(SpatialRestrictions.contains("geom", coor));
        @SuppressWarnings("unchecked")
        List<Municipio> l = crit.list();
        if (l.isEmpty())
            throw new NoExisteMunicipioException(coor);
        return l.get(0);
    }

    public Comarca getComarcaById(Integer pIdComarca) throws NoExisteComarcaException{
        Criteria crit = currentSession.createCriteria(Comarca.class);
        crit.add(Restrictions.eq("id", pIdComarca));
        crit.setMaxResults(1);
        @SuppressWarnings("rawtypes")
        List comarcas = crit.list();
        if(comarcas.isEmpty())
            throw new NoExisteComarcaException(pIdComarca);
        return (Comarca) comarcas.get(0);
    }

    public Boolean existenSaltosComarcas(){
        Criteria crit = currentSession.createCriteria(Salto.class);
        @SuppressWarnings("rawtypes")
        List saltos = crit.list();
        Boolean tiene = false;
        if(saltos != null){
            if(!saltos.isEmpty()){
                tiene = true;
            }
        }
        return tiene;
    }

    public void crearSalto(Comarca pComarcaOrigen, Comarca pComarcaDestino, int pNumeroSaltos) {
        Salto elSalto = new Salto(pComarcaOrigen, pComarcaDestino, pNumeroSaltos);
        currentSession.save(elSalto);
    }

    public Integer getCantidadSaltos(Municipio municipioInicio, Municipio municipioFin) throws SinViajesEntreZonasException {
        System.out.println("getCantidadSaltos()");
        Integer saltos = null;
        Criteria crit = currentSession.createCriteria(Salto.class);
        crit.add(
            Restrictions.and(
                Restrictions.eq("origen", municipioInicio.getZona_mugi()), 
                Restrictions.eq("destino", municipioFin.getZona_mugi())
            )
        );
        crit.setMaxResults(1);
        @SuppressWarnings("rawtypes")
        List res = crit.list();
        if(res != null){
            if(!res.isEmpty()){
                saltos = ((Salto) res.get(0)).getNumeroSaltos();
            }else{
                throw new SinViajesEntreZonasException(municipioInicio.getZona_mugi(), municipioFin.getZona_mugi());
            }
        }else{
            throw new SinViajesEntreZonasException(municipioInicio.getZona_mugi(), municipioFin.getZona_mugi());
        }
        return saltos;
    }

    public Boolean existenTarifas() {
        Boolean existen = false;

        Criteria crit = currentSession.createCriteria(Tarifa.class);
        crit.setProjection(Projections.rowCount());
        Long cant = (Long) crit.uniqueResult();
        if(cant > 0){
            existen = true;
        }

        return existen;
    }

    public void crearTarifa(Tarifa pTarifa) {
        currentSession.saveOrUpdate(pTarifa);
    }

    public Tarifa getTarifa(TipoTarifa pTipoTarifa) throws NoExisteTarifaException {
        Criteria crit = currentSession.createCriteria(Tarifa.class);
        crit.add(Restrictions.idEq(pTipoTarifa));

        Tarifa t = (Tarifa) crit.uniqueResult();

        if(t == null){
            throw new NoExisteTarifaException(pTipoTarifa);
        }else{
            return t;
        }

    }

}

I've got a service layer, which performs the actions started in the web service endpoint:

public class PtDaoService {

    private PtDao dao;

    public PtDaoService() throws HibernateException, ConfigurationException{
        dao = PtDao.getInstance();
    }

    public void finish() throws HibernateException {
        dao.finish();
    }

    public void imprimirZonas(){
        try {
            dao.openCurrentSession();
            List<Municipio> municipios = dao.getMunicipios();
            for(Municipio m:municipios){
                System.out.println(m.toString());
            }
            dao.closeCurrentSession();
        } catch (HibernateException e) {
            System.err.println("Error HibernateException: " + e.getMessage());
            e.printStackTrace();
        }
    }

    public Municipio getMunicipioByCoor(Point coor) throws NoExisteMunicipioException{
        try {
            dao.openCurrentSession();
            Municipio municipio = dao.getMunicipioByCoor(coor);         
            dao.closeCurrentSession();
            return municipio;
        } catch (HibernateException e) {
            System.err.println("Error HibernateException: " + e.getMessage());
            e.printStackTrace();
            return null;
        }
    }

    public void inicializarTarifasSiNoExisten(){
        boolean construyendo = false;
        try {
            dao.openCurrentSession();
            if(!dao.existenTarifas()){
                construyendo = true;
                dao.beginTransaction();
                Tarifa ocasional = new Tarifa(
                        TipoTarifa.Ocasional, 
                        "Sin condiciones", 
                        Money.of(CurrencyUnit.EUR, 1.70), 
                        Money.of(CurrencyUnit.EUR, 2.45), 
                        Money.of(CurrencyUnit.EUR, 4.65), 
                        Money.of(CurrencyUnit.EUR, 6.85), 
                        Money.of(CurrencyUnit.EUR, 8.90), 
                        Money.of(CurrencyUnit.EUR, 12.00));
                dao.crearTarifa(ocasional);

                Tarifa tramo1 = new Tarifa(
                        TipoTarifa.Tramo1, 
                        "Descuento del 45%", 
                        Money.of(CurrencyUnit.EUR, 0.93), 
                        Money.of(CurrencyUnit.EUR, 1.35), 
                        Money.of(CurrencyUnit.EUR, 2.56), 
                        Money.of(CurrencyUnit.EUR, 3.77), 
                        Money.of(CurrencyUnit.EUR, 4.90), 
                        Money.of(CurrencyUnit.EUR, 6.60));
                dao.crearTarifa(tramo1);
                dao.commitTransaction();
            }
            dao.closeCurrentSession();
        } catch (HibernateException e) {
            if(construyendo)
                dao.rollbackTransaction();
            System.err.println("Error HibernateException: " + e.getMessage());
            e.printStackTrace();
        }
    }

    public void inicializarSaltosComarcasSiNoExisten(){
        //a method
    }

    public Integer getCantidadSaltos(Double pOriginLatitude, Double pOriginLongitude, Double pDestinationLatitude,
            Double pDestinationLongitude) throws SinViajesEntreZonasException, NoExisteMunicipioException, ErrorEnGetCantidadSaltosException {

        System.out.println("getCantidadSaltos() [ pOriginLatitude=" + pOriginLatitude + ", pOriginLongitude=" + pOriginLongitude + ", pDestinationLatitude=" + pDestinationLatitude + ", pDestinationLongitude=" + pDestinationLongitude + " ]");

        Point inicio = GeometriesFactory.createPoint(pOriginLatitude, pOriginLongitude);
        Point fin = GeometriesFactory.createPoint(pDestinationLatitude, pDestinationLongitude);

        try {
            dao.openCurrentSession();
            Municipio municipioInicio = dao.getMunicipioByCoor(inicio);         
            Municipio municipioFin = dao.getMunicipioByCoor(fin);

            Integer saltos = dao.getCantidadSaltos(municipioInicio, municipioFin);

            dao.closeCurrentSession();

            return saltos;
        } catch (HibernateException e) {
            System.err.println("Error HibernateException: " + e.getMessage());
            e.printStackTrace();
            throw new ErrorEnGetCantidadSaltosException();
        }

    }

    public Money getTarifaDeSaltos(Boolean pTieneTarjeta, Integer pSaltos) throws NoExisteTarifaException, ErrorEnGetTarifaException {
        TipoTarifa tipoTarifa;
        if(pTieneTarjeta){
            tipoTarifa = TipoTarifa.Tramo1;
        }else{
            tipoTarifa = TipoTarifa.Ocasional;
        }
        Tarifa laTarifa = null;
        try {
            dao.openCurrentSession();
            laTarifa = dao.getTarifa(tipoTarifa);
            dao.closeCurrentSession();
            return laTarifa.getTarifaParaLosSaltos(pSaltos);
        } catch (HibernateException e) {
            System.err.println("Error HibernateException: " + e.getMessage());
            e.printStackTrace();
            throw new ErrorEnGetTarifaException(tipoTarifa);
        }

    }

}

This is a piece of code of my endpoint:

@Path("FareManager")
public class TarifasService {

private PtDaoService dao;
    ...
    @ValidateOnExecution
    @GET
    @Consumes(MediaType.TEXT_PLAIN)
    @Produces(MediaType.APPLICATION_JSON)
    @Path("{operator}/fare")
    public Response getTarifa(
        @NotNull @PathParam("operator") String pOperator,
        @NotNull @QueryParam("start_lat") Double pOriginLatitude,
        @NotNull @QueryParam("start_lon") Double pOriginLongitude,
        @NotNull @QueryParam("end_lat") Double pDestinationLatitude,
        @NotNull @QueryParam("end_lon") Double pDestinationLongitude,
        @QueryParam("user_id") String pUserIdentification,
        @QueryParam("provider") String pTransportProvider,
        @QueryParam("card") Boolean pCard
    ){

        String json = "";
        try {

            if(pOperator==null || pOriginLatitude==null || pOriginLongitude==null || pDestinationLatitude==null || pDestinationLongitude==null || pCard==null || pTransportProvider==null){
            String[] params = {"operator", "start_lat", "start_lon", "end_lat", "end_lon", "card", "provider"};
                throw new ParametrosInsuficientesException(params);
            }

            Integer saltos = dao.getCantidadSaltos(pOriginLatitude, pOriginLongitude, pDestinationLatitude, pDestinationLongitude);

            Money tarifa = dao.getTarifaDeSaltos(pCard, saltos);
            BigDecimal precio = tarifa.getAmount();
            Currency moneda = tarifa.getCurrencyUnit().toCurrency();

            FareResponse response = new FareResponse(pTransportProvider, precio, moneda);
            json = mWriter.writeValueAsString(response);
            logHelper.logGetTarifa(pOriginLatitude, pOriginLongitude, pDestinationLatitude, pDestinationLongitude, precio, moneda, saltos, pCard, pOperator, pUserIdentification, pTransportProvider);
            return Response.ok(json, MediaType.APPLICATION_JSON).build();
        } catch (Exception e) {
            System.err.println("Error: " + e.getLocalizedMessage());
            e.printStackTrace();
            ExceptionResponse errResp = new ExceptionResponse(e);
            try {
                json = mWriter.writeValueAsString(errResp);
                return Response.serverError().tag("Error").entity(json).build();
            } catch (JsonProcessingException e1) {
                System.err.println("Error: " + e.getLocalizedMessage());
                e.printStackTrace();
                return Response.serverError().tag("Error").build();
            }
        }
    }

The thing is that the web service should permit several requests to the same method concurrently. If I execute a method several times at once (in the browser, pressing several times f5 for instance), I get exception traces, and seem to be random.

Is there something wrong you can see in my code?

Thanks in advance

Edit This is the exception:

14:05:00.135 [http-apr-9090-exec-6] ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper - This statement has been closed.
    Error HibernateException: could not extract ResultSet
    org.hibernate.exception.GenericJDBCException: could not extract ResultSet
        at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:47)
        at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:111)
        at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:97)
        at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:79)
        at org.hibernate.loader.Loader.getResultSet(Loader.java:2115)
        at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1898)
        at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1874)
        at org.hibernate.loader.Loader.doQuery(Loader.java:919)
        at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:336)
        at org.hibernate.loader.Loader.doList(Loader.java:2610)
        at org.hibernate.loader.Loader.doList(Loader.java:2593)
        at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2422)
        at org.hibernate.loader.Loader.list(Loader.java:2417)
        at org.hibernate.loader.criteria.CriteriaLoader.list(CriteriaLoader.java:109)
        at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1787)
        at org.hibernate.internal.CriteriaImpl.list(CriteriaImpl.java:363)
        at com.ingartek.dao.PtDao.getMunicipioByCoor(PtDao.java:192)
        at com.ingartek.dao.PtDaoService.getCantidadSaltos(PtDaoService.java:326)
        at com.ingartek.ws.tarifas.TarifasService.getTarifa(TarifasService.java:195)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory$1.invoke(ResourceMethodInvocationHandlerFactory.java:81)
        at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:144)
        at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:161)
        at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:160)
        at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:99)
        at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:389)
        at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:347)
        at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:102)
        at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:326)
        at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
        at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
        at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
        at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
        at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
        at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)
        at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:305)
        at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154)
        at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:473)
        at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:427)
        at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:388)
        at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:341)
        at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:228)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
        at org.apache.logging.log4j.web.Log4jServletFilter.doFilter(Log4jServletFilter.java:71)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
        at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:522)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1095)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:672)
        at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.doRun(AprEndpoint.java:2500)
        at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:2489)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Unknown Source)
    Caused by: org.postgresql.util.PSQLException: This statement has been closed.
        at org.postgresql.jdbc.PgStatement.checkClosed(PgStatement.java:893)
        at org.postgresql.jdbc.PgStatement.getMaxRows(PgStatement.java:479)
        at org.postgresql.jdbc.PgStatement.createResultSet(PgStatement.java:181)
        at org.postgresql.jdbc.PgStatement$StatementResultHandler.handleResultRows(PgStatement.java:231)
        at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1947)
        at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:200)
        at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:424)
        at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:161)
        at org.postgresql.jdbc.PgPreparedStatement.executeQuery(PgPreparedStatement.java:114)
        at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:70)
        ... 65 more
    Error: Error a la hora de consultar la cantidad de saltos.
    com.ingartek.exception.ErrorEnGetCantidadSaltosException: Error a la hora de consultar la cantidad de saltos.
        at com.ingartek.dao.PtDaoService.getCantidadSaltos(PtDaoService.java:336)
        at com.ingartek.ws.tarifas.TarifasService.getTarifa(TarifasService.java:195)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory$1.invoke(ResourceMethodInvocationHandlerFactory.java:81)
        at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:144)
        at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:161)
        at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:160)
        at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:99)
        at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:389)
        at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:347)
        at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:102)
        at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:326)
        at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
        at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
        at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
        at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
        at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
        at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)
        at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:305)
        at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154)
        at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:473)
        at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:427)
        at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:388)
        at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:341)
        at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:228)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
        at org.apache.logging.log4j.web.Log4jServletFilter.doFilter(Log4jServletFilter.java:71)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
        at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:522)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1095)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:672)
        at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.doRun(AprEndpoint.java:2500)
        at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:2489)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Unknown Source)

Upvotes: 0

Views: 295

Answers (1)

Thierry
Thierry

Reputation: 5440

I see 3 issues :

  1. Your transaction management is wrong
  2. Your dao is not thread safe (this is this point that trigger the stacktrace you see).
  3. You hide exceptions at service level

About 1, you should commit tx / close hibernate session in finally blocks.

About 2, you must not use fields of singleton to store current tx nor current session. Well not exactly true, but stateful objects should be wrapped in ThreadLocal fields of stateless objects. This is relatively complex to manage, and you should use library for that (like spring-framework tx services).

About 3, if service is hiding exception, you never know in the controller if an error happened or not, and if you should continue processing or not...

Upvotes: 1

Related Questions