Announcement Announcement Module
Collapse
No announcement yet.
JDBCTemplate premature close connection Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • JDBCTemplate premature close connection

    Hi guys,

    I'm trying to integrate Spring ACL in my application. I use C3P0 for connection pooling, and Spring ACL uses JDBCTemplate, which requires a data-source, so I pass my ComboPooledDataSource. The problem is that to load the ACL rules the basic lookup strategy calls JDBCTemplate, which loads the ACL rules but then closes the connection! And I use this connection to continue the execution of the method, but because it is closed C3P0 throws "You cannot operate on a closed connection!" exception, so Spring tries to rollback the transaction and then finally a null-pointer is raised, because the connection that we try to rollback is already closed.
    To debug this you have to use the latest C3P0 (version 0.9.5-pre3-SNAPSHOT from here https://oss.sonatype.org/content/rep...pre3-SNAPSHOT/) which contains a new data-source class called

    Code:
    com.mchange.v2.c3p0.debug.AfterCloseLoggingComboPooledDataSource
    and the stack trace that I get is:

    Code:
    2013-05-22 08:28:31,159 [http-bio-8080-exec-4] DEBUG com.mchange.v2.c3p0.debug.AfterCloseLoggingConnectionWrapper - Original close() call stack trace:
    java.sql.SQLWarning: DEBUG STACK TRACE -- [email protected]() first-call stack trace.
    	at com.mchange.v2.c3p0.debug.AfterCloseLoggingConnectionWrapper$AfterCloseLoggingInvocationHandler.invoke(AfterCloseLoggingConnectionWrapper.java:77)
    	at $Proxy30.close(Unknown Source)
    	at org.springframework.jdbc.support.JdbcUtils.closeConnection(JdbcUtils.java:67)
    	at org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle.releaseConnection(HibernateJpaDialect.java:169)
    	at org.springframework.jdbc.datasource.ConnectionHolder.released(ConnectionHolder.java:193)
    	at org.springframework.jdbc.datasource.DataSourceUtils.doReleaseConnection(DataSourceUtils.java:323)
    	at org.springframework.jdbc.datasource.DataSourceUtils.releaseConnection(DataSourceUtils.java:294)
    	at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:610)
    	at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:637)
    	at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:666)
    	at org.springframework.security.acls.jdbc.BasicLookupStrategy.lookupObjectIdentities(BasicLookupStrategy.java:341)
    	at org.springframework.security.acls.jdbc.BasicLookupStrategy.readAclsById(BasicLookupStrategy.java:303)
    	at org.springframework.security.acls.jdbc.JdbcAclService.readAclsById(JdbcAclService.java:109)
    	at org.springframework.security.acls.jdbc.JdbcAclService.readAclById(JdbcAclService.java:94)
    	at org.springframework.security.acls.AclPermissionEvaluator.checkPermission(AclPermissionEvaluator.java:81)
    	at org.springframework.security.acls.AclPermissionEvaluator.hasPermission(AclPermissionEvaluator.java:59)
    	at org.springframework.security.access.expression.SecurityExpressionRoot.hasPermission(SecurityExpressionRoot.java:129)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:601)
    	at org.springframework.expression.spel.support.ReflectiveMethodExecutor.execute(ReflectiveMethodExecutor.java:69)
    	at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:172)
    	at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:134)
    	at org.springframework.expression.spel.ast.OpOr.getBooleanValue(OpOr.java:50)
    	at org.springframework.expression.spel.ast.OpOr.getValueInternal(OpOr.java:41)
    	at org.springframework.expression.spel.ast.OpOr.getValueInternal(OpOr.java:33)
    	at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:134)
    	at org.springframework.expression.spel.ast.OpOr.getBooleanValue(OpOr.java:50)
    	at org.springframework.expression.spel.ast.OpOr.getValueInternal(OpOr.java:41)
    	at org.springframework.expression.spel.ast.OpOr.getValueInternal(OpOr.java:33)
    	at org.springframework.expression.spel.ast.SpelNodeImpl.getTypedValue(SpelNodeImpl.java:102)
    	at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:98)
    	at org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:11)
    	at org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice.before(ExpressionBasedPreInvocationAdvice.java:41)
    	at org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter.vote(PreInvocationAuthorizationAdviceVoter.java:54)
    	at org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter.vote(PreInvocationAuthorizationAdviceVoter.java:24)
    	at org.springframework.security.access.vote.UnanimousBased.decide(UnanimousBased.java:77)
    	at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:206)
    	at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:60)
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
    	at $Proxy38.test(Unknown Source)
    	at com.facades.TestFacadeImpl.test(TestFacadeImpl.java:24)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:601)
    	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
    	at $Proxy40.test(Unknown Source)
    	at com.controller.TestController.home(TestController.java:33)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:601)
    From here:

    Code:
    2013-05-22 08:28:31,159 [http-bio-8080-exec-4] DEBUG com.mchange.v2.c3p0.debug.AfterCloseLoggingConnectionWrapper - Original close() call stack trace:
    java.sql.SQLWarning: DEBUG STACK TRACE -- [email protected]() first-call stack trace.
    	at com.mchange.v2.c3p0.debug.AfterCloseLoggingConnectionWrapper$AfterCloseLoggingInvocationHandler.invoke(AfterCloseLoggingConnectionWrapper.java:77)
    	at $Proxy30.close(Unknown Source)
    	at org.springframework.jdbc.support.JdbcUtils.closeConnection(JdbcUtils.java:67)
    	at org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle.releaseConnection(HibernateJpaDialect.java:169)
    	at org.springframework.jdbc.datasource.ConnectionHolder.released(ConnectionHolder.java:193)
    	at org.springframework.jdbc.datasource.DataSourceUtils.doReleaseConnection(DataSourceUtils.java:323)
    	at org.springframework.jdbc.datasource.DataSourceUtils.releaseConnection(DataSourceUtils.java:294)
    	at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:610)
    	at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:637)
    	at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:666)
    	at org.springframework.security.acls.jdbc.BasicLookupStrategy.lookupObjectIdentities(BasicLookupStrategy.java:341)
    and here(JDBCTemplate line 610):

    Code:
    		finally {
    			if (psc instanceof ParameterDisposer) {
    				((ParameterDisposer) psc).cleanupParameters();
    			}
    			JdbcUtils.closeStatement(ps);
    			DataSourceUtils.releaseConnection(con, getDataSource());
    		}
    I can clearly see that the JDBCTemplate is always closing the connection. Does this mean that I can't use JDBCTemplate with C3P0? Also, does this mean that I can't use ACL in my project?

    If you want to debug I have a zip file that I can give you.

    Thanks a lot for your time.

  • #2
    Hi
    Code:
    DataSourceUtils.releaseConnection(con, getDataSource());
    doesn't mean it really calls Connection.close(). If you look at the source code, you'll see:
    Code:
    		if (dataSource != null) {
    			ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
    			if (conHolder != null && connectionEquals(conHolder, con)) {
    				// It's the transactional Connection: Don't close it.
    				conHolder.released();
    				return;
    			}
    		}
    which really doesn't close the connection. The real close() is called by PlatformTransactionManager in use.

    However - there should be transaction in use - from your stack trace:
    Code:
    	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
    So - what is your transaction config? - what are your annotations at com.facades.TestFacadeImpl.test? Do you have XML transaction config?

    I can't see any particular SQL errors - you can try setting breakpoint in org.springframework.jdbc.core.JdbcTemplate.execute (PreparedStatementCreator, PreparedStatementCallback<T>) and see whether there's an exception being thrown.

    regards
    Grzegorz Grzybek

    Comment


    • #3
      Grzegorz,

      thank you so much for your post. I was stuck with this issue for a long time, and it's nice to have some progress finally. I use spring transactions configured via annotations. Apart from this I have no other xml transaction config:

      Code:
      	<tx:annotation-driven transaction-manager="transactionManager" mode="proxy"/>
      and my data-source:
      Code:
      	
      <bean id="dataSource" class="com.mchange.v2.c3p0.debug.AfterCloseLoggingComboPooledDataSource" destroy-method="close">
              <property name="driverClass" value="${jdbc.driverClassName}" />
              <property name="jdbcUrl" value="${jdbc.url}" />
              <property name="user" value="${jdbc.username}" />
              <property name="password" value="${jdbc.password}" />
              <property name="minPoolSize" value="5" />
              <property name="maxPoolSize" value="20" />
              <property name="acquireIncrement" value="1" />
              <property name="idleConnectionTestPeriod" value="100" />
              <property name="maxStatements" value="0" />
              <property name="checkoutTimeout" value="60000" />
          </bean>
      and the test facade:

      Code:
      @Component(value = "testFacade")
      public class TestFacadeImpl implements TestFacade {
      
          @Resource(name = "testService")
          private TestService testService;
          
          @Override
          @Transactional
          public void test() {
              
              TestModel test = new TestModel();
              test.setId(1l);
              testService.test(test);
          }
          
      }
      I will try to debug and look for exceptions. Again thanks a lot for your answer, and if you have any other suggestions I am willing to try them.

      Cheers, Petar.

      Comment


      • #4
        And how does your "transactionManager" bean look like?

        Comment


        • #5
          Hi Grzegorz,

          I forgot to paste that. Here it is:

          Code:
              <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
                  <property name="entityManagerFactory" ref="entityManagerFactory"/>
                  <property name="dataSource" ref="dataSource"/>
              </bean>
              
          	<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" depends-on="ehcache">
          		<property name="dataSource" ref="dataSource"/>
          		<property name="jpaVendorAdapter" ref="jpaAdapter"/>
          		<property name="packagesToScan" value="com.model" />
          		<property name="loadTimeWeaver">
          			<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
          		</property>
                  <property name="jpaProperties">
                      <props>
                     	 	<prop key="javax.persistence.sharedCache.mode">ENABLE_SELECTIVE</prop>
                          <prop key="hibernate.archive.autodetection">class</prop>
                          <prop key="hibernate.show_sql">false</prop>
                          <prop key="hibernate.format_sql">true</prop>
                          <prop key="hibernate.use_sql_comments">true</prop>
                          <prop key="hibernate.connection.driver_class">${jdbc.driverClassName}</prop>
                          <prop key="hibernate.connection.password">${jdbc.password}</prop>
                          <prop key="hibernate.connection.url">${jdbc.url}</prop>
                          <prop key="hibernate.connection.username">${jdbc.username}</prop>
                          <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
                          <prop key="hibernate.hbm2ddl.auto">update</prop>
                          <prop key="hibernate.c3p0.acquire_increment">3</prop>
                          <prop key="hibernate.c3p0.preferredTestQuery">select 1;</prop>
                          <prop key="hibernate.c3p0.min_size">5</prop>
                          <prop key="hibernate.c3p0.max_size">20</prop>
                          <prop key="hibernate.c3p0.timeout">300</prop>
                          <prop key="hibernate.c3p0.max_statements">50</prop>
                          <prop key="hibernate.c3p0.idle_test_period">3000</prop>
                          <prop key="hibernate.connection.characterEncoding">utf-8</prop> 
                          <prop key="hibernate.connection.CharSet">utf-8</prop>
          		        <prop key="hibernate.connection.useUnicode">true</prop>
                          <prop key="hibernate.jdbc.batch_size">50</prop>
                          <prop key="hibernate.order_update">true</prop>
                          <prop key="hibernate.id.new_generator_mappings">true</prop>
                          <prop key="hibernate.connection.autocommit">true</prop>
                          <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.DefaultNamingStrategy</prop>
                          <prop key="hibernate.cache.use_second_level_cache">true</prop>
          				<prop key="hibernate.cache.use_query_cache">true</prop>
          				<prop key="hibernate.cache.use_minimal_puts">true</prop>
          				<prop key="hibernate.cache.use_structured_entries">true</prop>
          				<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</prop>
          				<prop key="hibernate.cache.provider_class">net.sf.ehcache.hibernate.SingletonEhCacheProvider</prop>
          				<prop key="hibernate.generate_statistics">true</prop>
                      </props>
                  </property>
          	</bean>
          	
          	<bean id="jpaAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
          		<property name="showSql" value="false"/>
          		<property name="generateDdl" value="true" />
          		<property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect" />
          	</bean>
          Also I tried to step through the JdbcTemplate.execute method as you suggested. Because my ACL config looks like this:

          Code:
          public interface TestService {
              @PreAuthorize("hasPermission(#model, 'WRITE') or hasPermission(#model, 'ADMINISTRATION') or hasRole('ROLE_ADMINGROUP')")
              void test(TestModel model);
          }
          it has to go through that method 3 times - 1) to fine ACL for WRITE, 2) to find ACL for ADMINISTRATION and 3) to find ACL for ROLE_ADMINGROUP.
          The first time was OK, but the second time it got through that method I failed.

          Code:
          		try {
          			Connection conToUse = con;
          			if (this.nativeJdbcExtractor != null &&
          					this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativePreparedStatements()) {
          				conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
          			}
          			ps = psc.createPreparedStatement(conToUse);
          Straight after this line where we call the SimplePreparedStatementCreator it goes to the finally clause in the JDBCTemplate:

          Code:
          		finally {
          			if (psc instanceof ParameterDisposer) {
          				((ParameterDisposer) psc).cleanupParameters();
          			}
          			JdbcUtils.closeStatement(ps);
          			DataSourceUtils.releaseConnection(con, getDataSource());
          		}
          and I can already see the exception in the LOGS, plus this line:
          Code:
          2013-05-22 10:48:17,277 [http-bio-8080-exec-1] INFO  com.mchange.v2.c3p0.debug.AfterCloseLoggingConnectionWrapper - Method 'public abstract java.sql.PreparedStatement java.sql.Connection.prepareStatement(java.lang.String) throws java.sql.SQLException' called after call to Connection close().
          2013-05-22 10:48:17,277 [http-bio-8080-exec-1] INFO  com.mchange.v2.c3p0.debug.AfterCloseLoggingConnectionWrapper - Method 'public abstract java.sql.PreparedStatement java.sql.Connection.prepareStatement(java.lang.String) throws java.sql.SQLException' called after call to Connection close().
          2013-05-22 10:48:17,278 [http-bio-8080-exec-1] DEBUG com.mchange.v2.c3p0.debug.AfterCloseLoggingConnectionWrapper - After-close() method call stack trace:
          java.sql.SQLWarning: DEBUG STACK TRACE -- call to method of close()ed Connection.
          	at com.mchange.v2.c3p0.debug.AfterCloseLoggingConnectionWrapper$AfterCloseLoggingInvocationHandler.invoke(AfterCloseLoggingConnectionWrapper.java:84)
          	at $Proxy30.prepareStatement(Unknown Source)
          	at org.springframework.jdbc.core.JdbcTemplate$SimplePreparedStatementCreator.createPreparedStatement(JdbcTemplate.java:1438)
          	at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:581)
          Looks like there's an SQLException, but I don't know why it doesn't go pass through the catch clause, as it explicitly catches SQLException.

          Once again, thanks for your help, and if you would like to debug it I can give you a small zip file with a maven project that you can build in a few seconds.

          Comment


          • #6
            I think I can look at your ZIP to debug the problem.

            But first - try removing:
            Code:
                            <prop key="hibernate.connection.driver_class">${jdbc.driverClassName}</prop>
                            <prop key="hibernate.connection.password">${jdbc.password}</prop>
                            <prop key="hibernate.connection.url">${jdbc.url}</prop>
                            <prop key="hibernate.connection.username">${jdbc.username}</prop>
            ...
                            <prop key="hibernate.c3p0.acquire_increment">3</prop>
                            <prop key="hibernate.c3p0.preferredTestQuery">select 1;</prop>
                            <prop key="hibernate.c3p0.min_size">5</prop>
                            <prop key="hibernate.c3p0.max_size">20</prop>
                            <prop key="hibernate.c3p0.timeout">300</prop>
                            <prop key="hibernate.c3p0.max_statements">50</prop>
                            <prop key="hibernate.c3p0.idle_test_period">3000</prop>
                            <prop key="hibernate.connection.characterEncoding">utf-8</prop> 
                            <prop key="hibernate.connection.CharSet">utf-8</prop>
            	        <prop key="hibernate.connection.useUnicode">true</prop>
            ...
            and especially:
            Code:
                            <prop key="hibernate.connection.autocommit">true</prop>
            from your "entityManagerFactory" bean. If you shove dataSource into entityManagerFactory, you don't need to tell Hibernate to manage connections on its own!

            regards
            Grzegorz Grzybek

            Comment


            • #7
              Hello Grzegorz,

              I tried removing the

              Code:
               <prop key="hibernate.connection.autocommit">true</prop>
              as you suggested, but I saw no effect - still the same exception. So as I told you - the first time in JDBCTemplate goes fine, but then the second time there's an SQLException, and the Logs state that:

              Code:
               Caused by: java.lang.reflect.InvocationTargetException
              	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
              	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
              	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
              	at java.lang.reflect.Method.invoke(Method.java:601)
              	at com.mchange.v2.c3p0.debug.AfterCloseLoggingConnectionWrapper$AfterCloseLoggingInvocationHandler.invoke(AfterCloseLoggingConnectionWrapper.java:89)
              	... 88 more
              Caused by: java.sql.SQLException: You can't operate on a closed Connection!!!
              	at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:118)
              	at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:77)
              	at com.mchange.v2.c3p0.impl.NewProxyConnection.prepareStatement(NewProxyConnection.java:330)
              	... 93 more
              Caused by: java.lang.NullPointerException
              	at com.mchange.v2.c3p0.impl.NewProxyConnection.prepareStatement(NewProxyConnection.java:294)
              	... 93 more
              So there's a null-pointer exception in NewProxyConnection:294, which looks like this:

              Code:
              if(!parentPooledConnection.isStatementCaching())
              So it seems that JDBCTemplate has closed the parent connection somehow.

              Thanks a lot for your help and the zip file you can download from here:

              https://www.dropbox.com/s/myza5fhefxbqur0/draft.zip

              All you have to do is: mvn clean install
              and once it's finished type mvn tomcat7:run

              Thanks for your help!

              Comment


              • #8
                Hello

                See: https://jira.springsource.org/browse/SPR-10395

                I've changed Spring version to today-released Spring 3.2.3.RELEASE and I've added this dependency:
                Code:
                		<dependency>
                			<groupId>org.springframework</groupId>
                			<artifactId>spring-webmvc</artifactId>
                			<version>${spring.version}</version>
                		</dependency>
                And It worked - I've got:
                Code:
                org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.security.access.AccessDeniedException: Access is denied
                	org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:948)
                	org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:827)
                	javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
                	org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:812)
                	javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
                	org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
                	org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
                regards
                Grzegorz Grzybek

                Comment


                • #9
                  OMG,

                  I can't believe I lost nearly a month on this. Thanks a lot Grzegorz you are a life-saver - it works now

                  Comment


                  • #10
                    I'm happy it works now!

                    So it's worth visiting Spring forum - if only they've got rid of these chemistry-related posts...

                    regards
                    Grzegorz Grzybek

                    Comment

                    Working...
                    X