úterý 12. září 2017

Experiments with the Payara Micro, level I

New company, new project, new technologies. Okay, I'm experimenting with Payara Micro. Payara team produces more and more examples in several blogs, but they are very trivial and not always usable in production environment. At this time I'm not sure if I would be able to do the evolution to the final professional system, but it is not a problem, because the application modules are simply Java EE standard modules.
The difference is only in final organisation of the modules, deployment and container configuration, so I can create the standard EAR application in parallel to UBER jar with the Payara Micro. So this style of development is perfectly safe.

Target 

Application with the following modules:
  • DAO and bussines logic service module (JPA, JTA, EJB, CDI?), created but nearly empty in this blog 
  • Web service module (JAX-RS), only one simple service method in this blog 
  • GUI module (JSF), not resolved in this blog 
And project will have also following aggregation alternatives:
  • Uber JAR with Payara Micro - experimental, responsive development 
  • EAR for standard Payara domain 
The reason for this separation of modules is that a JSF GUI application obviously have different requirements than a Web service application. It might not be a problem for some time, but it could be a problem later. This is not any premature optimization - this will force developers to keep in mind the separation line between modules and maybe to create some clean API. That will help right now and it will be simplier to split it later.

There may be even more Maven modules:
  • superpom - common Maven plugin configurations, basic dependency management 
  • project parent - aggregator of all project modules 
  • integration tests for the web service module 
  • selenium tests for the gui module

Dead-end streets and good streets 

Well, I had hard two days with the Payara Micro. Blogs helped, but I always needed more and I was always stucked in some weird state. Yes, it was always my fault, but ... okay, now you can learn from my mistakes.

LOG4J2 

I used LOG4J with SLF4J for many years, it was pretty trivial to make it work and a bit harder to grab logs of embedded Payara in integration tests. I have found a memory leak in old LOG4J's reconfiguration and I know perhaps everything about that.
Now it is worthless with LOG4J2. But finally the configuration was also simple despite I still have not found a way to merge logs of the application and Payara. Example log4j2.properties, seems like a good street:
status = info
dest = err
name = PropertiesConfig
#log4j2.debug = true

property.path = target/logs

filter.threshold.type = ThresholdFilter
filter.threshold.level = debug

appender.console.type = Console
appender.console.name = STDOUT
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %m%n
appender.console.filter.threshold.type = ThresholdFilter
appender.console.filter.threshold.level = info

appender.rolling.type = RollingFile
appender.rolling.name = RollingFile
appender.rolling.fileName = ${path}/metering2-ws.log
appender.rolling.filePattern = ${path}/metering2-ws-%d{MM-dd-yy-HH-mm-ss}-%i.log.gz
appender.rolling.layout.type = PatternLayout
appender.rolling.layout.pattern = %d %p %C{2.} [%t] %m%n
appender.rolling.policies.type = Policies
appender.rolling.policies.time.type = TimeBasedTriggeringPolicy
appender.rolling.policies.time.interval = 2
appender.rolling.policies.time.modulate = true
appender.rolling.policies.size.type = SizeBasedTriggeringPolicy
appender.rolling.policies.size.size=1024MB
appender.rolling.strategy.type = DefaultRolloverStrategy
appender.rolling.strategy.max = 1

logger.ws.name = cz.x
logger.ws.level = debug
logger.ws.additivity = false
logger.ws.appenderRef.file.ref = RollingFile

rootLogger.level = debug
rootLogger.appenderRef.stdout.ref = STDOUT
rootLogger.appenderRef.file.ref = RollingFile  

JAX-RS, lists ... Jackson!

Creating the first JAX-RS web service is pretty trivial ... you need two classes, first to configure the context of services in the module, second to implement the service:

import javax.ws.rs.ApplicationPath;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.glassfish.jersey.server.ResourceConfig;

@ApplicationPath("rs")
public class RestAppConfig extends ResourceConfig {

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

  public RestAppConfig() {
    LOG.debug("RestAppConfig()");
    try {
      packages(RestAppConfig.class.getPackage().getName());
      LOG.info("REST configured!");
    } catch (final Exception e) {
      LOG.error("Cannot configure the REST web services!", e);
    }
  }
}
And the second:

import java.util.Arrays;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@Path("user")
public class UserRestService {

  @GET
  @Path("list")
  @Produces(MediaType.APPLICATION_JSON)
  public Response list() {
    final List<String> list = Arrays.asList("Křemílek", "Vochomůrka"); // UTFG ;-)
    return Response.ok(list).build();
  }
} 
Maven command to run the application, it will be used in all following examples:
 mvn clean install;  mvn fish.payara.maven.plugins:payara-micro-maven-plugin:start -pl :metering2-uberjar
Final part of the log:

[2017-09-12T16:31:25.369+0200] [] [INFO] [AS-WEB-GLUE-00172] [javax.enterprise.web] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1505226685369] [levelValue: 800] Loading application [metering2-ws-0.0.1-SNAPSHOT] at [/metering2-ws-0.0.1-SNAPSHOT]

[2017-09-12T16:31:25.681+0200] [] [INFO] [] [javax.enterprise.system.core] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1505226685681] [levelValue: 800] metering2-ws-0.0.1-SNAPSHOT was successfully deployed in 12 472 milliseconds.

[2017-09-12T16:31:25.685+0200] [] [INFO] [] [PayaraMicro] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1505226685685] [levelValue: 800] Deployed 1 archive(s)

[2017-09-12T16:31:25.686+0200] [] [INFO] [] [PayaraMicro] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1505226685686] [levelValue: 800] [[
 
Instance Configuration
Host: dmatej-lenovo
HTTP Port(s): 8080
HTTPS Port(s):
Instance Name: Frail-Barracuda
Instance Group: MicroShoal
Hazelcast Member UUID cbddb9aa-5f21-4ee1-984f-78be942124d4
Deployed: metering2-ws-0.0.1-SNAPSHOT ( metering2-ws-0.0.1-SNAPSHOT war /metering2-ws-0.0.1-SNAPSHOT )

]]

[2017-09-12T16:31:25.702+0200] [] [INFO] [] [PayaraMicro] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1505226685702] [levelValue: 800] [[
 
Payara Micro URLs
http://dmatej-lenovo:8080/metering2-ws-0.0.1-SNAPSHOT

'metering2-ws-0.0.1-SNAPSHOT' REST Endpoints
GET     /metering2-ws-0.0.1-SNAPSHOT/rs/user/list

]]
Ok, that was pretty simple. But the service does not work and ends up with HTTP 500 (try it with browser):

[2017-09-12T20:19:02.013+0200] [] [SEVERE] [] [org.glassfish.jersey.message.internal.WriterInterceptorExecutor] [tid: _ThreadID=22 _ThreadName=http-thread-pool::http-listener(3)] [timeMillis: 1505240342013] [levelValue: 1000] MessageBodyWriter not found for media type=application/json, type=class java.util.Arrays$ArrayList, genericType=class java.util.Arrays$ArrayList.

Solution 

Only add the dependency on Jackson to pom.xml and the following line as the first to the RestAppConfig's constructor:
register(JacksonFeature.class);
The final pom.xml of the ws module:

 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <artifactId>metering2-ws</artifactId>
  <packaging>war</packaging>
  <name>Metering2 prototype - Web Services</name>
  <parent>
    <groupId>cz.netpro.metering2</groupId>
    <artifactId>parent-pom</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <relativePath>..</relativePath>
  </parent>
  <dependencies>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-api</artifactId>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
    </dependency>
    <dependency>
      <groupId>javax.ws.rs</groupId>
      <artifactId>javax.ws.rs-api</artifactId>
    </dependency>
    <dependency>
      <groupId>javax</groupId>
      <artifactId>javaee-api</artifactId>
    </dependency>
    <!-- to use optimized JAX-RS configuration -->
    <dependency>
      <groupId>org.glassfish.jersey.core</groupId>
      <artifactId>jersey-server</artifactId>
    </dependency>
    <!-- to simply use lists in JSONs -->
    <dependency>
      <groupId>org.glassfish.jersey.media</groupId>
      <artifactId>jersey-media-json-jackson</artifactId>
    </dependency>
  </dependencies>
  <build>
    <resources>
      <resource>
        <directory>src/main/resources</directory>
        <filtering>false</filtering>
        <includes>
          <include>**/*.properties</include>
        </includes>
      </resource>
    </resources>
  </build>
</project>

JDBC Pool 

This was a real pain but solution was so simple ... do you know what I hate? NullpointerException. In fact I appreciate that it exists, because it tells "the programmer was not careful". And if the programmer was not careful, it is a bug - maybe even trivial to fix. But another exception I have seen was ClassNotFoundException ... This is the pom.xml of the UBER jar:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>cz.netpro.metering2</groupId>
  <artifactId>metering2-uberjar</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>Metering2 Uberjar</name>
  <parent>
    <groupId>cz.netpro</groupId>
    <artifactId>superpom</artifactId>
    <version>0.0.1-SNAPSHOT</version>

  </parent>
  <build>
    <resources>
      <resource>
        <directory>src/main/resources</directory>
        <filtering>false</filtering>
        <includes>
          <include>**/*.txt</include>
        </includes>
      </resource>
    </resources>
    <plugins>
      <!-- HOWTO: https://github.com/payara/maven-plugins -->
      <plugin>
        <groupId>fish.payara.maven.plugins</groupId>
        <artifactId>payara-micro-maven-plugin</artifactId>
        <version>1.0.0</version>
        <executions>
          <execution>
            <goals>
              <goal>bundle</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <payaraVersion>4.1.2.173</payaraVersion>
          <useUberJar>true</useUberJar>
          <deployArtifacts>
            <deployArtifact>
              <groupId>cz.netpro.metering2</groupId>
              <artifactId>metering2-ws</artifactId>
              <version>${project.version}</version>
              <type>war</type>
            </deployArtifact>
          </deployArtifacts>
          <customJars>
            <customJar>
              <groupId>org.apache.logging.log4j</groupId>
              <artifactId>log4j-api</artifactId>
            </customJar>
            <customJar>
              <groupId>org.apache.logging.log4j</groupId>
              <artifactId>log4j-core</artifactId>
            </customJar>
            <customJar>
              <groupId>mysql</groupId>
              <artifactId>mysql-connector-java</artifactId>
            </customJar>
          </customJars>
          <commandLineOptions>
            <commandLineOption>
              <key>--autobindhttp</key>
              <value>true</value>
            </commandLineOption>
            <commandLineOption>
              <key>--prebootcommandfile</key>
              <value>${project.build.outputDirectory}/prepare-resources.txt</value>
            </commandLineOption>
          </commandLineOptions>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
The prepare-resources.txt was this (note that empty lines are interpreted as an error):

set configs.config.server-config.admin-service.das-config.dynamic-reload-enabled=false
set configs.config.server-config.admin-service.das-config.autodeploy-enabled=false
create-jdbc-connection-pool --datasourceclassname com.mysql.cj.jdbc.MysqlDataSource --restype javax.sql.DataSource --property user=test:password=test:DatabaseName=iqhouse_metering:ServerName=localhost:port=3306:zeroDateTimeBehavior=convertToNull:useUnicode=true:useJDBCCompliantTimezoneShift=true:useLegacyDatetimeCode=true:serverTimezone=UTC:characterEncoding=UTF-8 metering-mysql
set resources.jdbc-connection-pool.metering-mysql.steady-pool-size=5
set resources.jdbc-connection-pool.metering-mysql.max-pool-size=20
set resources.jdbc-connection-pool.metering-mysql.connection-validation-method=auto-commit
set resources.jdbc-connection-pool.metering-mysql.is-connection-validation-required=true
set resources.jdbc-connection-pool.metering-mysql.fail-all-connections=true
ping-connection-pool metering-mysql
But it did not work, server startup failed and I had no idea why ... and Payara did not help me ... there were two kind of stacktraces:

java.lang.RuntimeException: Orb initialization erorr
        at org.glassfish.enterprise.iiop.api.GlassFishORBHelper.getORB(GlassFishORBHelper.java:191)
        at com.sun.enterprise.naming.impl.SerialContext.getORB(SerialContext.java:349)
        at com.sun.enterprise.naming.impl.SerialContext.getProviderCacheKey(SerialContext.java:356)
        at com.sun.enterprise.naming.impl.SerialContext.getRemoteProvider(SerialContext.java:386)
        at com.sun.enterprise.naming.impl.SerialContext.getProvider(SerialContext.java:331)
        at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:480)
        at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:440)
        at javax.naming.InitialContext.lookup(InitialContext.java:417)
        at org.glassfish.resourcebase.resources.naming.ResourceNamingService.lookup(ResourceNamingService.java:236)
        at com.sun.enterprise.connectors.service.ConnectorConnectionPoolAdminServiceImpl.getConnectorConnectionPool(ConnectorConnectionPoolAdminServiceImpl.java:799)
        at com.sun.enterprise.connectors.service.ConnectorConnectionPoolAdminServiceImpl.obtainManagedConnectionFactory(ConnectorConnectionPoolAdminServiceImpl.java:938)
        at com.sun.enterprise.connectors.service.ConnectorConnectionPoolAdminServiceImpl.getUnpooledConnection(ConnectorConnectionPoolAdminServiceImpl.java:549)
        at com.sun.enterprise.connectors.service.ConnectorConnectionPoolAdminServiceImpl.testConnectionPool(ConnectorConnectionPoolAdminServiceImpl.java:430)
        at com.sun.enterprise.connectors.ConnectorRuntime.pingConnectionPool(ConnectorRuntime.java:1162)
        at org.glassfish.connectors.admin.cli.PingConnectionPool.execute(PingConnectionPool.java:143)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl$2$1.run(CommandRunnerImpl.java:544)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl$2$1.run(CommandRunnerImpl.java:540)
        at java.security.AccessController.doPrivileged(Native Method)
        at javax.security.auth.Subject.doAs(Subject.java:360)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl$2.execute(CommandRunnerImpl.java:539)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl$3.run(CommandRunnerImpl.java:570)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl$3.run(CommandRunnerImpl.java:562)
        at java.security.AccessController.doPrivileged(Native Method)
        at javax.security.auth.Subject.doAs(Subject.java:360)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:561)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:1469)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl.access$1300(CommandRunnerImpl.java:111)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1851)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1727)
        at com.sun.enterprise.admin.cli.embeddable.CommandExecutorImpl.executeCommand(CommandExecutorImpl.java:169)
        at com.sun.enterprise.admin.cli.embeddable.CommandExecutorImpl.run(CommandExecutorImpl.java:94)
        at fish.payara.micro.boot.runtime.BootCommand.execute(BootCommand.java:65)
        at fish.payara.micro.boot.runtime.BootCommands.executeCommands(BootCommands.java:105)
        at fish.payara.micro.boot.runtime.BootCommands.executeCommands(BootCommands.java:99)
        at fish.payara.micro.impl.PayaraMicroImpl.bootStrap(PayaraMicroImpl.java:987)
        at fish.payara.micro.impl.PayaraMicroImpl.main(PayaraMicroImpl.java:186)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at fish.payara.micro.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48)
        at fish.payara.micro.boot.loader.Launcher.launch(Launcher.java:107)
        at fish.payara.micro.boot.loader.Launcher.launch(Launcher.java:70)
        at fish.payara.micro.boot.PayaraMicroLauncher.main(PayaraMicroLauncher.java:79)
        at fish.payara.micro.PayaraMicro.main(PayaraMicro.java:361)
Caused by: java.lang.NullPointerException
        at org.glassfish.enterprise.iiop.api.GlassFishORBHelper.getORB(GlassFishORBHelper.java:163)
        ... 44 more
[2017-09-12T21:42:28.782+0200] [] [SEVERE] [] [javax.enterprise.resource.resourceadapter.com.sun.enterprise.connectors] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1505245348782] [levelValue: 1000] RAR6001 : Class Not found : com.sun.gjc.spi.ResourceAdapterImpl


com.sun.appserv.connectors.internal.api.ConnectorRuntimeException: Error in creating active RAR
        at com.sun.enterprise.connectors.ActiveRAFactory.createActiveResourceAdapter(ActiveRAFactory.java:111)
        at com.sun.enterprise.connectors.service.ResourceAdapterAdminServiceImpl.createActiveResourceAdapter(ResourceAdapterAdminServiceImpl.java:212)
        at com.sun.enterprise.connectors.service.ResourceAdapterAdminServiceImpl.createActiveResourceAdapter(ResourceAdapterAdminServiceImpl.java:348)
        at com.sun.enterprise.connectors.ConnectorRuntime.createActiveResourceAdapter(ConnectorRuntime.java:405)
        at com.sun.enterprise.connectors.service.ConnectorService.loadDeferredResourceAdapter(ConnectorService.java:184)
        at com.sun.enterprise.connectors.service.ConnectorService.loadResourcesAndItsRar(ConnectorService.java:148)
        at com.sun.enterprise.connectors.service.ConnectorService.checkAndLoadPool(ConnectorService.java:325)
        at com.sun.enterprise.connectors.service.ConnectorConnectionPoolAdminServiceImpl.getUnpooledConnection(ConnectorConnectionPoolAdminServiceImpl.java:553)
        at com.sun.enterprise.connectors.service.ConnectorConnectionPoolAdminServiceImpl.testConnectionPool(ConnectorConnectionPoolAdminServiceImpl.java:430)
        at com.sun.enterprise.connectors.ConnectorRuntime.pingConnectionPool(ConnectorRuntime.java:1162)
        at org.glassfish.connectors.admin.cli.PingConnectionPool.execute(PingConnectionPool.java:143)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl$2$1.run(CommandRunnerImpl.java:544)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl$2$1.run(CommandRunnerImpl.java:540)
        at java.security.AccessController.doPrivileged(Native Method)
        at javax.security.auth.Subject.doAs(Subject.java:360)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl$2.execute(CommandRunnerImpl.java:539)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl$3.run(CommandRunnerImpl.java:570)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl$3.run(CommandRunnerImpl.java:562)
        at java.security.AccessController.doPrivileged(Native Method)
        at javax.security.auth.Subject.doAs(Subject.java:360)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:561)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:1469)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl.access$1300(CommandRunnerImpl.java:111)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1851)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1727)
        at com.sun.enterprise.admin.cli.embeddable.CommandExecutorImpl.executeCommand(CommandExecutorImpl.java:169)
        at com.sun.enterprise.admin.cli.embeddable.CommandExecutorImpl.run(CommandExecutorImpl.java:94)
        at fish.payara.micro.boot.runtime.BootCommand.execute(BootCommand.java:65)
        at fish.payara.micro.boot.runtime.BootCommands.executeCommands(BootCommands.java:105)
        at fish.payara.micro.boot.runtime.BootCommands.executeCommands(BootCommands.java:99)
        at fish.payara.micro.impl.PayaraMicroImpl.bootStrap(PayaraMicroImpl.java:987)
        at fish.payara.micro.impl.PayaraMicroImpl.main(PayaraMicroImpl.java:186)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at fish.payara.micro.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48)
        at fish.payara.micro.boot.loader.Launcher.launch(Launcher.java:107)
        at fish.payara.micro.boot.loader.Launcher.launch(Launcher.java:70)
        at fish.payara.micro.boot.PayaraMicroLauncher.main(PayaraMicroLauncher.java:79)
        at fish.payara.micro.PayaraMicro.main(PayaraMicro.java:361)
Caused by: java.lang.ClassNotFoundException: com.sun.gjc.spi.ResourceAdapterImpl
        at com.sun.enterprise.v3.server.APIClassLoaderServiceImpl$APIClassLoader.loadClass(APIClassLoaderServiceImpl.java:245)
        at com.sun.enterprise.v3.server.APIClassLoaderServiceImpl$APIClassLoader.loadClass(APIClassLoaderServiceImpl.java:237)
        at com.sun.enterprise.connectors.ActiveRAFactory.createActiveResourceAdapter(ActiveRAFactory.java:103)
        ... 40 more
]]

Dead-end street: comment out ping 

Okay, stacktraces are gone. But I don't know if the pool has been created and if it works.

Dead-end street: add dependencies 

Idea: add missing dependencies. The Payara versions are not in Maven Central, so I tried to add glassfish versions of org.glassfish.main.jdbc.jdbc-ra.jdbc-core:jdbc-core:4.1.2 and org.glassfish.main.jdbc.jdbc-ra.jdbc40:jdbc40:4.1.2 ... Result? Several warnings like this and finally exception. Dumb idea? Something similar helped us with old versions of the Embedded Payara started by JUnit integration tests but here it was only a cargo antipattern.

[2017-09-12T21:36:37.619+0200] [] [WARNING] [] [javax.enterprise.resource.resourceadapter.com.sun.enterprise.connectors.util] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1505244997619] [levelValue: 900] RAR8000 : The method setLogJdbcCalls is not present in the class : com.sun.gjc.spi.DSManagedConnectionFactory

com.sun.appserv.connectors.internal.api.ConnectorRuntimeException: Failed to create MCF for metering-mysql
        at com.sun.enterprise.connectors.service.ConnectorConnectionPoolAdminServiceImpl.createConnectorConnectionPool(ConnectorConnectionPoolAdminServiceImpl.java:195)
        at com.sun.enterprise.connectors.ConnectorRuntime.createConnectorConnectionPool(ConnectorRuntime.java:331)
        at org.glassfish.jdbc.deployer.JdbcConnectionPoolDeployer.actualDeployResource(JdbcConnectionPoolDeployer.java:201)
        at org.glassfish.jdbc.deployer.JdbcConnectionPoolDeployer.deployResource(JdbcConnectionPoolDeployer.java:170)
        at com.sun.enterprise.connectors.service.ConnectorService.loadDeferredResources(ConnectorService.java:233)
        at com.sun.enterprise.connectors.service.ConnectorService$1.run(ConnectorService.java:153)
        at java.security.AccessController.doPrivileged(Native Method)
Those two dependencies now remove, they are not needed and more, they are not compatible.

Look into the Payara sources and think

 ... and create this issue: https://github.com/payara/Payara/issues/1967 Oh, by the way, why I did not think it before? ORB factory is simply initialized after the prebootcommandfile execution! Ok, let's move the ping to a new file postboot.txt and add another commandLineOption to pom.xml:
<commandLineOption>
  <key>--postbootcommandfile</key>
  <value>${project.build.outputDirectory}/postboot.txt</value>
</commandLineOption>

MySQL and time zones 

Pool ping was still failing with some weird error message about unknown CEST timezone. StackOverflow advices did not work, neither one about configuring the JDBC driver. I have found several bugs reported to MySQL devs: https://bugs.mysql.com/bug.php?id=86425
I tried to change the server's default-time-zone via the MySql Workbench with no success until I noted that it updates incorrect file in my user's home directory. Finally I added these lines into /etc/mysql/my.cnf and restarted the mysql service ... and it worked.
[mysqld]
default-time-zone = +00:00

Success 

Yes, that was all. But ... there is nothing interesting in the log output, no logging about asadmin commands, no logging about their success. I was lazy to create my own logging.properties and to add path as another commandLineOption so I hacked the jar in maven repository (please, don't do this, don't be lazy!) ... the nearest usable loggers and logs was these:

[2017-09-12T22:23:53.622+0200] [] [FINE] [] [javax.enterprise.resource.resourceadapter.org.glassfish.jdbcruntime] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1505247833622] [levelValue: 500] [CLASSNAME: org.glassfish.jdbcruntime.JdbcRuntimeExtension] [METHODNAME: isConnectionPoolReferredInServerInstance] JDBC resource jdbc/metering2-jtarefers metering-mysqlin this server instance and is enabled
 

[2017-09-12T22:23:54.398+0200] [] [FINE] [] [org.glassfish.naming] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1505247834398] [levelValue: 500] [CLASSNAME: com.sun.enterprise.naming.util.NamingUtilsImpl] [METHODNAME: makeCopyOfObject] [[
  ** makeCopyOfObject:: ConnectorConnectionPool :: metering-mysql
steady size: 5
max pool size: 20
max wait time: 60000
pool resize qty: 2
Idle timeout: 300
failAllConnections: true
Transaction Support Level: 1
isConnectionValidationRequired_ true
preferValidateOverRecreate_ false
matchConnections_ false
associateWithThread_ false
lazyConnectionAssoc_ false
lazyConnectionEnlist_ false
maxConnectionUsage_ 0
pingPoolDuringCreation_ false
poolingOn_ true
validateAtmostOncePeriod_ 0
connectionLeakTracingTimeout_0
connectionReclaim_false
connectionCreationRetryAttempts_0
connectionCreationRetryIntervalInMilliSeconds_10
nonTransactional_ false
nonComponent_ false
ConnectorDescriptorInfo ->
rarName: __ds_jdbc_ra
resource adapter class: com.sun.gjc.spi.ResourceAdapterImpl
connection def name: javax.sql.DataSource
MCF Config properties-> ClassName:com.mysql.cj.jdbc.MysqlDataSource
ConnectionValidationRequired:true
ValidationMethod:auto-commit
ValidationTableName:
ValidationClassName:
TransactionIsolation:
GuaranteeIsolationLevel:true
StatementWrapping:true
LogJdbcCalls:false
SlowQueryThresholdInSeconds:-1
StatementTimeout:-1
PoolMonitoringSubTreeRoot:resources/metering-mysql
PoolName:metering-mysql
StatementCacheSize:0
StatementCacheType:
InitSql:null
SqlTraceListeners:fish.payara.jdbc.SilentSqlTraceListener
StatementLeakTimeoutInSeconds:0
StatementLeakReclaim:false
DatabaseName:iqhouse_metering
User:test
Password:****
ServerName:localhost
DriverProperties:setcharacterEncoding#UTF-8##setport#3306##setuseJDBCCompliantTimezoneShift#true##setuseLegacyDatetimeCode#true##setuseUnicode#true##setserverTimezone#UTC##setzeroDateTimeBehavior#convertToNull##
Delimiter:#
EscapeCharacter:\
]]  
Now I can continue to level 2: JPA services ... to be continued ;)

pátek 21. července 2017

Manday není jednotka!

Tento příspěvek bude česky, protože se týká hlavně českých firem, tvořících software, a je podložen praxí, bohužel.

Pro některé je titulek možná šokující odhalení a děsný podraz, jelikož celý život krom jednotek Kč, EURO, hodina, den, týden a procento používali též "člověkoden" (manday) a "člověkohodinu" (manhour). O té poslední se zmiňovat ani nebudu, platí pro ní totéž, co pro tu v titulku.

Manday přece funguje

Ano, někdy funguje. Třeba při výkopových pracích bývá rozdíl mezi dělníky řádový jen výjimečně. Manažer si odhad pro jistotu vynásobí nějakým koeficientem a má skoro jistotu, že se termín stihne. Může dokonce jen s malou chybou počítat "cenu manday", atd.
Jenže my se bavíme o tvorbě software, kde jsou rozdíly mezi programátory, analytiky, testery i konzultanty obrovské.

Pepa není Franta

Máte velký projekt, který tvoří statisíce řádek kódu, dvacítka příruček, desetitisíce automatických testů, tisíce uživatelských scénářů pro akceptace a školení, za projektem jsou léta vývoje, používá se poměrně velká sada kompatibilních technologií s popsaným způsobem vazeb ...
Pepa je senior, projekt vede pět let, možná nerozumí všemu, ale přinejmenším ví, "že to tam je", a po deseti minutách se umí zorientovat. Franta je též senior, firma ho zrovna přijala, má léta praxe, všechny používané technologie "má v malíčku", má taky dvojnásobný plat než Pepa.

Stejný úkol

Přesto když ti dva dostanou stejný úkol, Pepa ho má do hodiny vyřešený, Franta pravděpodobně něco rozbije. Manažer se zlobí, začíná pochybovat o tom, že přijetí Franty bylo správné. Možná ho přeřadí na jiný projekt, kde se to zopakuje. Nebo to Frantu nechá opravit a dodělat, a rozčiluje se, že místo jednoho dne, který dostal s velkou rezervou, je to už týden.
První problém totiž je, že práce nováčka se nedá odhadovat bez ohledu na jeho léta praxe na jiných projektech.

Samouk

Další zlá varianta - tým má už tak málo lidí, že Pepa nemá čas na nováčka v týmu, každý den hasí "požáry". V rychlosti na něj každý den vychrlí nové informace, nemá čas ale vysvětlovat podrobnosti. Frantu hodí do vody, ukáže mu jak plavat, a dál ať to zkouší sám. Výsledek? Utekl měsíc, Franta je stále mimo, nefunguje mu už ani editor, nechápe proč. Nadává, co je tohle za firmu. Manažer vynadá Pepovi, ten ho nevrle pošle "do lesa".

Jinak a lépe

Nejefektivnější způsob zaučování je párové programování. Prostě si spolu musí aspoň na půlku pracovní doby sednout, Pepa bude navigátor, Franta řidič. Občas může být problém Frantovo ego, ale to už je na tom, jestli nový člen týmu zapadne do kolektivu. Samozřejmě mandays běží dvojnásobným tempem, ale čert je vem. Postupem času bude Pepy třeba méně a méně, při přechodu na další odlišnou práci si zase spolu sednou a dělají spolu.

Každý je nahraditelný ...?

Je? Není? Ale jo, je ... ale je třeba zvážit, za jakou cenu, přičemž cenou je tu hlavně čas (že čas nejsou peníze, vysvětlím dále). Někteří lidé prakticky nejsou v přijatelném časovém horizontu nahraditelní jedním člověkem - je třeba si rozmyslet, kolik a jaké práce běžně odváděli, v jaké kvalitě i kvantitě (to je další problém - jak to měřit?), a ideálně dostatečný počet nových lidí přibrat do týmu dříve než dotyčný odejde. I tak dojde ke zpoždění, protože je bude třeba do vývoje "dostat".

Opět - Pepa není ekvivalentem pěti Jindřichů ani padesáti Karlíků. Vyplnit vzniklou mezeru může být velmi těžké a pro celý tým stresující, čili je třeba, aby mezera byla co nejmenší, ideálně aby vznikl překryv. Pepa si nové lidi sám zaškolí.

Struktura týmu

V ČR je bohužel zažitá praxe, že organizace firem je hierarchická, někdy dokonce s mnoho patry. Kupodivu to většinou nefunguje, nebo to přinejmenším nefunguje hladce.
Druhý nešvarem je, že firmy týmy dimenzují spíše tak, že týmu jeden člověk chybí než aby jeden člověk přebýval. Tým pak nemá žádnou rezervu na řešení náhlých problémů, výzkum alternativ, atd. Tento přístup je přímo sebevražedný.

V týmu by měli být lidé sobě rovnocenní a měli by si rozdělit práci tak trochu podle vlastních preferencí a specializací. Projektový manažer nesmí být víc než vývojář ani naopak, oba by se měli ale vzájemně respektovat, oba jsou potřebnými a rovnocennými experty, každý na svou oblast.
V týmu si lidé musí vyměňovat názory, klidně se můžou i hádat, ale nesmí sebou navzájem pohrdat nebo se sebe bát v žádném smyslu slova.

Plánování práce

Předem píšu, že se stále bavíme o velkých zakázkách, řádově stovky až tisíce mandays.
Zákazník jen málokdy dodá kompletní specifikaci toho, co chce. Ve smlouvě je často jen název zakázky, cena. No a protože je třeba nějak specifikovat, za co že ta cena vlastně je, stanoví se nějaký počet mandays na základě velmi hrubé analýzy toho, co všechno bude obsahem zakázky.
Tato hodnota smí být užita nanejvýš orientačnímu porovnání s jinými zakázkami, a to ještě s velkou opatrností, nebo k orientačnímu rozdělení zakázky na fragmenty. Jinak je k ničemu a pokud jí k něčemu použijete, nedivte se, že vám pak nic nebude vycházet.

No dobře, když ale nemůžeme používat mandays, jak máme plánovat? Hned na začátku by měly být jasné dva údaje:
  • termín
  • rozpočet
A od nich a na základě nějakého interního rozboru analýzy odvodit strukturu týmu, jaký bude na implementaci nasazen, pokud možno plným úvazkem, jelikož přepínání úloh při tvůrčí činnosti unavuje, zdržuje, a tato režie sama zabila nejeden projekt. Výjimkou můžou být lidé, kteří neprogramují, ale jsou zapotřebí například pro údržbu serverů, podporu týmu. O těch nemluvím, ale i na ně je třeba v rozpočtu myslet.

Jakýchkoliv sledování nebo nedejbože plánování mandays se v tuhle chvíli vzdejte, jen by celý tým mátly, ale žádný užitek z nich nebude. Pamatujte, nemůžete házet na jednu hromadu Pepy a Franty, ne, ani Petra!
Můžete definovat mezitermíny dokončení "milestones" (tj. částečná implementace, která se dá považovat za hotovou část celku, a dá se například předat zákazníkovi k "osahání") včetně plánu rozpočtu, což se dá snadno a poměrně přesně průběžně kontrolovat.

Po rozjetí projektu (ne nutně po podpisu smlouvy, ale s rizikem, že k němu ani nedojde je třeba vždycky počítat), je v první řadě třeba zpřesnit analýzu tak, aby se dalo začít programovat. Není třeba udělat všechno najednou, ale je třeba mít analýzu konzistentní, abyste se netočili v kruzích.
Programování pak probíhá ideálně v iteracích, co iterace, to nějaká sada dobře naplánovaných úloh, za každou úlohu nese někdo odpovědnost, po každé iteraci se vyhodnocuje stav a případně se úloha posune do další iterace.

Toto je už věc analytiků, programátorů, manažer může přihlížet, vznášet dotazy, ale do organizace práce ostatních členů týmu by neměl nekvalifikovaně zasahovat, rozhodně ne autoritativně. Měl by ale naopak průběžně informovat programátory o stavu rozpočtu a celý tým by měl dokázat zhruba odhadnout procento implementace vůči nejbližšímu plánovanému milestone.

Definice úlohy

Definici úlohy specifikují typicky programátoři ve spolupráci s analytikem. Úloha by měla mít následující atributy:
  • Název - jednoduchý a srozumitelný alespoň všem programátorům
  • Popis - co nejpečlivější a hned na začátku rozšířený o slovní návrh implementace, plus způsob ověření funkčnosti
  • Termín - u úlohy do nejbližší iterace už závazný, u úlohy plánované na později spíše orientační
  • Pracnost - odhad programátora, spíše pesimistický než optimistický. Všichni budou šťastní, pokud se to povede napoprvé, ale málokdy se to stane. Pokud je úloha ale dokončena s nižší než odhadnutou pracností, je to pořád dobré.
  • Specifikace závislostí na jiných úlohách
Právě součet pracností úlohy je pak použitelný k dalšímu odhadu toho, zda se vše stíhá, nicméně je nutné počítat i se závislostmi a vytížeností týmu, což není tak jednoduché, jak se na první pohled zdá. Pokud to zanedbáte, riskujete, že si až pozdě všimnete, že nestíháte. Na druhou stranu, plánovat příliš podrobně příliš dopředu vás také nikam neposune, jen ztratíte čas něčím, co pak buď budete zbytečně udržovat, nebo co stejně vyhodíte.

Čas nejsou peníze

Prostý fakt je ten, že zákazníka nezajímá, kolik lidí jste ve skutečnosti v týmu měli ani kolik tým strávil "mandays" prací na díle, dokonce ani jestli jste zaměstnali dvacet čerstvých absolventů vysoké školy nebo dva specialisty za 200 tisíc měsíčně. To je věc dodavatele, aby zvolil nejlepší cestu.

Ano, zákazník se může snažit zjistit, jaký máte zisk, resp. o kolik levnější daná zakázka mohla být. Pokud vás ale chce usvědčit z toho, že jste ho obrali, stejně vám při nejbližší příležitosti uteče, a svědčí to mimo jiné o tom, že jste nějak podcenili komunikaci se zákazníkem - začal vás podezřívat.

Zákazníka hlavně zajímá to, aby dílo, které zaplatil, nemělo žádné vady a fungovalo tak, jak požadoval. Mandays používá spíše na "virtuální" ohodnocení pracnosti díla. Toto číslo ale nemá s realitou interních výkazů práce nic společného. Kdyby mělo, představte si to - zákazník má problém, tak za ním pošlete:
  • experta, který je za dvě hodiny hotový
  • sekretářku, která zavolá expertovi, který jí naviguje. Dílo též skončí po nějakých čtyřech hodinách úspěchem
Vážně si myslíte, že můžete vyfakturovat celý jeden manday (2 lidi krát 4 hodiny)?*
Krom toho plat sekretářky je jen zlomek toho, co bere expert. Seriózní firma samozřejmě vždycky pošle experta. Tento příklad se obvykle řeší smluvně trochu jinak, ale jako okatý příklad nesrovnalosti takového přístupu snad stačí.

Závěr

Jinak řečeno, ano, asi jste si už u svých velkých zakázek všimli, že se nedají moc dobře plánovat. A právě proto vznikly termíny jako agilní techniky vývoje software, extrémní programování, a další, viz oba odkazy. Pro manažera, zvyklého na vodopádový model (pokud vůbec), je to celkem nepříjemná změna, ale je to jen o zvyku - nakonec je naopak příjemné, že se pořád něco děje a pořád se to posouvá, a když po každé iteraci vidí hotové dílo, už by neměnil.

Ještě bych zmínil jeden web, http://wiki.c2.com, vypadá sice triviálně a zastarale, ale obsahuje neskutečné množství nadčasových otázek a odpovědí, týkajících se vývoje software, vysvětlených podle mého soudu celkem srozumitelnou formou včetně různých pastí. Pokud ho začnete vážně číst, je možné, že budete mít problém přestat ...

P.S.: Kniha The Mythical Man-Month byla vydána v roce 1975.

EDIT: Na základě připomínky kamaráda, vztahující se k článkům "Agile is Dead" (které si najděte sami), přidávám ještě jeden odkaz s reakcí na ně: Is the Agile Manifesto Dead?
Stručně řečeno jde o to, že se z Agile stal buzzword, obchodní značka, přičemž původně šlo spíše o apel a nějaký soupis technik, které při vývoji pomáhají. "Agilní" znamená česky "snaživý", tak se snažte dělat věci dobře, chovejte se jako tým, atd. Ne, neznamená to, že se budete doslovně držet nějakého seznamu bodů. Myslete. A mluvte spolu.

*) Pro puntičkáře - zatímco sekretářka cestovala, expert se už připravoval na její podporu a studoval logy, které dostal.

sobota 17. září 2016

Stephen King, Andrej Tarkovski a uvědomnění si hranic našeho myšlení

Jsou knihy, které do duše člověka zasejí semínko a ono si tam klidně vyklíčí a roste. Takových je spousta ... poslední, kterou jsem takovou četl, bylo Svědectví od Stephena Kinga. S odstupem je to záležitost, ke které se člověk vrací a přemýšlí o ní.
Jak člověk začne pomáhat zlu a proč ho tak přitahuje, proč někteří lidé pohrdají altruismem, resp. jsou k těmto lidem jaksi nepřátelští.
Jak křehká je naše civilizace a jak snadno si pod sebou dokážeme podříznout větev, jak snadno se dokážeme sami zahubit.
Jak těžké je dostat se z takové situace, kdy je většina lidí na planetě mrtvá a je jen málo lidí, kteří vůbec tuší, jak funguje elektrárna.
Jaké místo má v tom všem víra, ne nutně přímo nějaké konkrétní náboženství, ale víra v něco, co jaksi vypluje na povrch, když přijdeme o všechny naše moderní "hračky", něco, co tu celou dobu je, ale teď to nepotřebujeme.

Podobně otevírají mysl Tarkovského filmy, ukáží na něco, co nedokážeme zpochybnit, nevysvětlí to, jen diváka nechají přemýšlet, hledat možnosti, ale stejně nakonec nechápat - nicméně i to ho posouvá mnohem dál, než kdy tušil ...
Ve filmu Solaris se "cizí inteligence" snaží komunikovat s lidmi tak, že jim zhmotňuje sny. Jakkoliv to zní ujetě, když to takhle napíšu, má to hlavu a patu. Lidská řeč, zvuk, frekvence vlnění kolem 20 Hz - 20 kHz, proč by někdo nutně musel komunikovat takhle? Jenže řeč výrazně ovlivňuje způsob myšlení, jak s takovou bytostí vůbec kdy lze komunikovat? Když člověk miloval a o lásku přišel, co se tak může stát, kdyby to šlo vzít zpátky? Ne, "americkou" verzi nečekejte. Tahle je o hodně těžší. Ve všem.
Film Stalker všichni chválili, já ho po shlédnutí zavrhl jako "nic moc" - o týden později mi teprve začaly docházet souvislosti některých pasáží. Nedokážu ho vyprávět, vím, o čem je pocitově, ale slova moc nejsou. O strachu. O strachu, který dobře znáte, ale nedokážete ho popsat, jen víte, že je odůvodněný, o čemž zároveň pochybujete.
Film Andrej Rublev, kdy víc než kde jinde jste nuceni pochopit, že středověk "fungoval" opravdu jinak, úplně jinak, a taky se dozvíte, jak se odlévaly zvony, veliké zvony.

Sledování primitivních filmů a čtení kratičkých textů z Facebooku nebo českých novin má zjevně také vliv na způsob uvažování. Chybí empatie, chybí schopnost "podívat se za roh" a snaha domýšlet možnosti, co všechno může událost způsobit a co jí mohlo způsobit. Tohle nás nakonec zničí, protože to vede k nezodpovědnosti. Nedokážeme zvážit kontext událostí ani možnosti.

neděle 4. září 2016

Holocaust, zbrojní obchody, války, utečenci.

K tomu holocaustu. Já si tenhle web dávkuji už snad pár let, prokládám jinými dokumenty. Jednu dobu tu stránku kdosi dokonce označil za malware.

http://www.holocaustresearchproject.org/nazioccupation/Holocaust-in-the-east.html
Nezajímá mě to až tak kvůli historii, ale spíš kvůli budoucnosti, nebo vazbě minulosti a budoucnosti, události, které souvisí a způsobují události ...
Andreas Eberl, německý voják, který byl svědkem Akce v lotyšském městě Daugavpils:

Slyšeli jsme, že ve věznici popravují Židy, tak jsme se šli podívat. Popravy ale neprobíhaly ve věznici, ale nedaleko od ní.
Výkopy byly dva a půl metru široké a 40 až 50 metrů dlouhé. Neviděl jsem na dno výkopu, protože v něm byla těla stovek už zastřelených Židů. Odsouzenci si museli kleknout na břehu tváří k výkopu. Komando je pak střílela do zátylku. Vzdálenost mezi střelcem a zastřelovanou osobou byla kolem půl metru.
Po zastřelení lidé padali do výkopu. Komando sestávalo z desítky lidí, polovina střílela, druhá polovina stála za nimi a občas si vyměnily místa. Ve výkopu jsem viděl muže kolem šedesátky, obvykle Žida, jehož prací bylo rovnat těla do řad. Když byla řada kompletní, muž vylezl ven a posypal jí něčím bílým, nejspíš chlornanem vápenatým (?).
Asi hodinu jsme s kamarády sledovali popravy. Během té doby bylo zastřeleno asi 150 Židů. Počet přihlížejících se pohyboval kolem 60 až 80.
Židé, teří byli střelení a spadli do výkopu a nepřestávali se hýbat, byli zastřeleni samopalem jedním z komanda. Munice pro nabíjení leželo na stole vedle lahví vodky, a čas od času někdo z komanda šel a napil se.

Vsevelod Klokov v rozhovoru v historickém seriálu The World at War:
Okupanti byli hrubí, velmi hrubí, obzvlášť v oblastech, kde byli aktivní partyzáni. Jen na Ukrajině bylo nacisty vypáleno 511 vesnic dříve, než jejich obyvatelstvo stihlo utéct.

Ve vesnici Pinsk nedaleko Kyjeva, která byla vypálena do základů, házeli do ohně i děti.
No a dál si to přeložte sami, pokud možno dřív, než budete díky své vlastní volbě a svému vlastnímu vyjadřování podpory tomu či onomu nuceni též přihlížet něčemu takovému. Možná se to zkraje také budete pokoušet omlouvat, odůvodňovat, ospravedlňovat s tím, že Židé si za to přece sami mohli, ale v hloubi duše budete vědět, že vy jste tím, kdo pomohl k moci těm, kteří o vraždění rozhodli.
V horším případě budete na okraji příkopu klečet sami ...

Spoustu věcí zveličujeme, spoustu věcí podceňujeme, některé jsou tiché a plíživé, ale zažírají se pod kůži všem. Třeba obhroublost politiků i jejich oponentů a aktivistů. Učí se jí všichni a v kombinaci s přímočarými triviálními řešeními je to dost nebezpečná záležitost.

A pak je tu záležitost, že se neumíme vzepřít autoritám. Zvlášť když se díváme, jak se vraždí lidi, ale sami netušíme, jak to zastavit.

Firmy se souhlasem státu obchází zbrojní embarga, na která už kašlou skoro všichni. Je ticho, všichni mlčí, nikdo nemá páku, jak to zastavit, nikdo neví na koho se obrátit, koho obvinit, nikdo se ani nepokouší. Funguje to, a lidi umírají. Umírají ale daleko.

A ještě jeden citát, nechte si ho projít hlavou a zapamatujte si ho. Vzpomeňte si na něj třeba u přihlouplých estrád, nebo při výrocích politiků i svých vlastních, dokonce i těch, kdy na hloupost a hrubost politika reagujete nějakou hrubou nadávkou.
Mnoho lidí váhá, mají-li říct dobré slovo, a stydlivě se červená, zatímco lehkomyslné vynáší směle a hlučně, netušíc, že takové naneštěstí nevyzní naprázdno, ale zanechá dlouhou stopu zla, někdy nenapravitelného.
(Ivan Gončarov - Oblomov)

neděle 3. července 2016

Javisté a databáze - základní chyby

Na školení jsem všem implicitně vynadal, že nikdo pořádně neumí používat ani JDBC, ale chtějí (dobře) používat JPA nebo JTA. A co teprve, aby rozuměli trochu tomu, co jejich zacházení způsobí na databázovém serveru ...
Pak se diví, že jim to celé padá na hubu - typicky začnou optimalizovat SQL, ale marně. Jen málokoho napadne, že databázi dávají kapky - pro každý váš požadavek databáze alokuje nějaké zdroje v operační paměti, přidělí vlákno, příkaz zabere nějaké místo na file systému, a databáze čeká až na signál od vás, že už jste hotoví.
Tož si překontrolujte, jestli si databázi nepřetěžujete ...
  • zavíráním statementů
    • kolikrát za sebou generujete stejný prepared statement?
    • nezavíráním statementů
    • jak dlouho má db držet statement?
    • kolik stejných prepared statementů držíte najednou?
    • poslední pomoc: omezit počet použití konexe z poolu, pak jí AS nuceně zavře a otevře novou (Payara)
  • nezavíráním result setů
    • jak dlouho má db držet nalezená data, kurzor?
    • kolik dat to je?
    • fetch size
  • zbytečně dlouhými transakcemi se spoustou změn
    • víte, co se děje v databázi?
    • víte, že i zdroje databáze jsou omezené?
  • nemazáním dočasných tabulek, vytvořených v poolovaných konexích
    • je to v podstatě leak, zapomenutá data pak můžou být náhodně nalezena jiným dotazem, čili hypoteticky to může být i bezpečnostní díra.
  • nadměrným využíváním indexů nebo naopak jejich absencí
    • každé vložení dat způsobí změnu v indexech
    • databáze jsou různě chytré/hloupé při optimalizacích
    • indexy pomáhají při čtení
    • indexy škodí při zápisu
    • někdy se hodí replikace db tak, aby db pro vyhledávání byla důsledněji indexovaná než ta pro zápis
  • chybným nastavením JDBC poolu
    • default lock timeout
    • velikosti bufferů
    • různé optimalizace
    • rozdělení datových zdrojů podle použití (exporty vs. CRUD)
  • chybným nastavením:
    • stmt.setQueryTimeout
    • stmt.setMaxRows
    • stmt.setFetchSize
    • conn.setTransactionIsolation
    • conn.setResultSetConcurrency
    • conn.setHoldability
JPA samozřejmě k relační db přistupují přes jdbc, ale pozor!
  • JDBC transakce není nutně pod JPA transakcí a už vůbec nejsou svázané 1:1
  • JPA transakce není JTA transakce
  • můžou. Je to záležitost nastavení "persistence unit" a také nastavení hintů (viz Query.setHint)
  • EntityManager.unwrap(Connection.class) znamená spuštění JDBC transakce a svázání 1:1 JDBC konexe a JPA manažera se vším, co to obnáší.
JTA se pak řídí hlavně anotacemi, ale pozor pozor!!!
  • pokud si nedáte pozor, můžete si připravit neskutečné magické transakční peklo
  • „requires“ je dobrý default
  • „requires new“ se někdy hodí, ale … uvědomte si, co to znamená!
  • některé posloupnosti těchto hodnot dokážou naprosto šílené věci – s kolika JPA kontexty a JDBC konexemi nakonec opravdu pracujete?
Jste programátoři, vědci, ne pistolníci z televizního divokého západu. Na riskování není čas, musíte přesně vědět, co děláte ;-)

sobota 26. března 2016

Proč je důležité mluvit s blbci

Lůza je hrozně hanlivé označení lidí. Blbci není o moc lepší. Řeklo by se nadávka, jenže ono je to tak ... 
Učitelé musí mít hroznou trpělivost. Já se pustím jednou za čas do konverzace s blby a po hodině házení hrachu na zeď je mi z nich špatně. Jenže - za prvé - když to nikdo dělat nebude, blbci mají ohromnou schopnost se v blbství utvrzovat a stupňovat ho. Postupně tak vznikne ohromné stádo pološílených blbů, schopných silou prosazovat stejně pološílené nápady. Bolševismus, nacismus, supremacismy obecně ... 
 Za druhé - je dobré vědět, že zdaleka ne každý má rozhled a že spousta lidí opravdu má problémy s logickým myšlením (resp. ano, nikdo nemá kompletní rozhled a perfektní logiku, ale u některých lidí jde opravdu o katastrofální úroveň). Fungují často na obrázky, které si spojí s jediným slovem v textu, spojeným s nějakou emocí. To je všechno, veškerá politika a schopnost volit stojí jen na tomhle.
Nestačí tím ale pohrdat a zesměšňovat to, ono je to spíš na škodu. Takových lidí bude vždycky dost, jenže pokud chcete něco změnit na čím dál absurdnější politice, budete muset začít odspoda.
Vzdělání je lékem na blbost. 
A tak se obloukem opět dostávám k České televizi. Ta totiž svou funkci vůbec neplní. Krom několika málo pořadů se vzdělávání obyvatelstva nijak nevěnuje, spíš se snaží urvat procenta sledovanosti v konkurenci komerčního tupého vysílání - komerčním tupým vysíláním.
Jenže když svou funkci plnit začne, stejně se na ní blbci dívat nebudou.
Depresivní, že?
A tak nezbývá, než aby bylo vzdělání všude, kde se na něj dá vůbec narazit. Snad i proto se začaly prosazovat všelijaké Techmánie, na ulici přístupné knihovny, dokonce piana, ...
Zázrak to neudělá, ale znamená to, že to někdo s touhle společností ještě nevzdal a i jen tenhle vliv může způsobit, že blbci nikdy vládnout nebudou. Někdy totiž opravdu stačí málo.

neděle 13. března 2016

How to upgrade BIOS on Lenovo Ideapad if you have Linux OS?

Why?

I have some problems with suspending of the system. CPU frequency is lowered from 3 GHz to 1.3 GHz and next time to 600 MHz after each wakeup. Maybe it is a problem of the operating system, but on another machine (Dell) with the same system it's alright.
EDIT: updating BIOS did not help.
EDIT2: updating Kubuntu to current 16.10 helped. ;-)

What?

  • Notebook Lenovo Ideapad Y510P
  • Kubuntu Linux 15.10 64bit
  • any USB stick (or SDHC card, etc.)

What does not work?

The root of the problem is that the new BIOS version is distributed only in EXE file. It is some installer with another installer inside and it is an 32bit Windows application.
So you need some 32 bit Windows and minimal supported Windows is Windows 7. I thought okay, it is ugly, but I will download some Trial from Microsoft, I will burn the ISO to the USB or DVD and use it's console to start the EXE.
It wasn't so simple ...
  • burned ISO does not boot - I have tried Win7 32b, Win10 32b and 64b.
  • following any instructions with dd failed - USB does not boot
  • Startup Disk Creator failed - does not work at all, I have selected ISO file but it was not shown in GUI.
  • UNetbootin failed - successfuly created bootable disk, but it could not boot.

Results

  • downloaded about 12 GB data from the internet.
  • registered on Microsoft's web ("sorry, Microsoft, but I don't want your system, I only need to run this 5 MB exe file - only once!")
  • maybe 20 times deleted and recreated the file system on three USB discs.
  • maybe 100 reboots of two notebooks used for this.
  • maybe 12 hours of work that made me crazy :D
  • and ... SUCCESS! :D

Howto

  1. Download BIOS
  2. Download any 32 bit Microsoft Windows (or some Live CD?)
  3. Start gparted
    1. Select the USB that you will use
    2. Delete all partitions or create new Partition table
    3. Create new NTFS partition (any other would not boot!) and give it some pretty name.
    4. Apply changes
    5. Set attributes to the partition (boot)
    6. Exit gparted.
  4. Start UNetbootin from the command line to prevent bug - if you would start from the menu, you could not select the NTFS partitions:
    1. sudo unetbootin installtype=USB targetdrive=/dev/sd[c][n]
    2. Select the downloaded ISO, click OK and wait ...
  5. Mount the USB disk
  6. Copy the downloaded BIOS exe to the root directory
  7. Unmount and reboot
  8. Hit F12 and start the Windows system installer
    1. I can't remember exactly but you need to get to the windows command line.
    2. Type c: and hit enter, now you are in the root directory of the USB partition.
    3. Type dir, then type the name of the exe file and hit the ENTER.
    4. Follow instructions of the BIOS installer. 
  9. Happyend? ... Maybe ... it took me two days ;-)

Sources

The best source that helped me is this answer on AskUbuntu: http://askubuntu.com/a/252910/160422


Fail 17 - 64 bit version of Windows

Success - bios updater started on Win installer 10 32bit

BIOS update in progress

Finally done.



středa 23. prosince 2015

Pravda ve víně?

Jojo, tak už to chodí ... "jo to byl ten, jak von se jmenoval ... už nevim ... byl divnej ... koupil si barák po K., ale zůstal v něm sám. Všechno mu trvalo, ale byl celkem šikovnej, spoustu toho udělal sám. No jo, ale k čemu mu to bylo dobrý ... "
No k čemu ... prostě tak, zajímalo ho, jak se co dělá a funguje a jestli to zvládne. A nechtělo se mu dohadovat s lidmi, neměl rád hádky a kibicování, v práci musel kibicovat dost ostatní. Byl si vědom, že tu dřinu nikdo nikdy nedocení, ale to mu vůbec nevadilo. Vlastně ho vždycky překvapovalo, když pro někoho "byl dobrej". Byl zvyklý být outsiderem, ale jen tak naoko, vždycky věřil, že sice je outsider, ale umí překvapit. Nedá se mu totiž upřít to, že když se do něčeho pustil, prostě to dotáhl do konce, i když to bylo sebetěžší. 
K čemu mu to? Intuice. Třeba se plete? Možná. Jsem už takový. Jsem pesimista, který se ale nevzdává a věci se snaží zlepšovat, i kdyby to bylo sebebeznadějnější. K čemu mi to? K prdu, spousta dřiny a starostí a posměchu, ale život je fajn pokaždé, když se mi podaří udělat "z prdu kuličku" nebo nějaký "malý zázrak".

No ale stejně. Bývaly časy, kdy se mi lidi posmívali. Jen tak, svým způsobem je to taková dětská šikana. Svět byl normální a dával smysl jen u babičky - mezi kočkami, králíky, slepicemi, knížkami a štípáním dříví, což jsem měl zkraje zakázáno, ale (opět logicky) když se ukázalo, že nejsem sám sobě nadmíru nebezpečný, bylo to tolerováno. Bavilo mě posouvat hranici a učit se další a další věci.
A pak byly časy na sš, kdy se po přijímačkách jeden učitel podivil, že jsem dopadl celkem dobře, ale co prý tam lezu, když stejně odpadnu. Nebyl daleko od pravdy ... jenže ono všechno má svůj "kontext" ... se kterým jsem se teprve musel naučit "vycházet". To se nakonec povedlo, prospěch se zlepšil o víc jak stupeň, zatímco jedničkáři odpadli. Outsider začal bodovat.
A pak byly časy na vš, kdy jsem se učil, že "boj vypadá jinak, štěstí neexistuje a naopak každá škvíra bude vyplněna smůlou". Učil jsem se nenechávat škvíry a ze smůly si nic nedělat. Dělat věci správně. Rozhodovat. Nebýt nervozní, nebát se. Dá se říct, že se to povedlo, byť jsem naposledy selhal ještě u státnic, leč prošel jsem.
A pak byly časy, kdy školní etapa skončila úspěchem, a já byl samostatný, normální. A postupem času se nějak upevnila ta chuť se učit a rozšiřovat svůj rozhled. A taky zjištění, že všechno se stihnout přečíst nedá. A spousta dalších zjištěních, třeba že experti toho často neví vůbec o moc víc, než neexperti. A taky že má smysl se ptát a má smysl hledat lidi, kteří se ptají a taky odpovídají. 
Dneska si říkám, že diskuze bez nenávisti je důležitější než cokoliv jiného jak pro demokracii, tak pro svobodu lidí. Člověk si vybírá, co bude číst, na co se bude dívat, co bude poslouchat. Ale jen zřídka si vybere, co mu jiní řeknou.
Cenzuře nevadí, že říkáte, co se jí nelíbí. Cenzuře vadí, když se tím lidé začínají zaobírat, dokazují, argumentují, přemýšlí, přehodnocují.

A zvláštní kategorie jsou vztahy. Vsadím se, že pokud tohle vůbec někdo čte, řekl si, že je to špatný text, že tenhle skok v něm je velký. Jenže ... ono to všechno souvisí. Nemáme jedno uvažování pro to a druhé pro ono. Máme jen jediný způsob uvažování ...
Časem jsem změnil přístup v mnoha věcech. Kdysi jsem žárlil. Jak já jsem žárlil! Oba jsme žárlili. Pro úsměvy jiným, pro pohledy jiným, pro pohledy jiných, no, pravda, taky pro zálety se žárlilo, ale tak jako tak to vlastně nemělo moc smysl. A taky jsem ještě nedávno věřil, že když dostanu facku, mám právo jí vrátit. Což o to, pořád si myslím, že to právo i mám, ale ... ničemu to nepomůže. Vlastně ve chvíli, kdy se dva začnou bít, je na čase odejít pryč. A mít z té neoplacené facky aspoň trochu "vítězoslavný" pocit, že jsem neklesl tak hluboko, když už jsem na dně.
S čím to souvisí? Pár nápadů bych měl, ale počkám si na vaše. Zkrátka, se vším.

No, a včil co?

To be continued ... v roce 2016 ... a zatím si dopiju to víno ;-)

čtvrtek 10. prosince 2015

Loadbalancing with Payara cluster and Apache2

I have tested the configuration on two operating systems - CentOS 7 and Kubuntu 15.10. These instructions are for CentOS, because there it is a bit more complicated.

All application server instances run on Payara 4.1.1.154 - two on CentOS 7 and one on Solaris5.10. Note that for correct cluster replication you need to have configured the multicast routing - or to have all instances on the same network ;)

1) You need to do this as root:
# aka apache2 and modules in debian linuxes
yum install httpd
2) You need time synchronization - one minute error is fatal. You can select another server for time sychronization - check also if it is accessible from the server.
yum install ntp ntpdate
chkconfig ntpd on
ntpdate pool.ntp.org
3) Edit the httpd.conf file and add two lines (use correct hostname, valid from the outside world):
vim /etc/httpd/conf/httpd.conf
ServerName myhost.mydomain.org
LoadModule rewrite_module modules/mod_rewrite.so

4) Edit httpd configuration file and put the loadbalancer settings in it. 
vim /etc/httpd/conf.d/00-default.conf
  • VirtualHost will run on port 80
  • you need some time to hold the session on one instance, where the user logs in - in this time the new session will be replicated to other payara instances in cluster. If the next request would be faster, the session would be invalidated. This is the reason why you need also the ROUTEID cookie - the stickysession changes it's value because a new session is created.
  • third internal host has status +H - that means "hot standby" - it will be routed only if other hosts would be inaccessible.
<VirtualHost *:80>
    ProxyRequests Off
    RewriteEngine Off
    Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/; Max-Age=60;" env=BALANCER_ROUTE_CHANGED
    ServerName myhost.mydomain.org
    LogLevel debug
    ProxyPass /balancer-manager !
    ProxyPass / balancer://myhost.mydomain.org/ stickysession=ROUTEID
    ProxyPassReverse /  http://myinternal1.mydomain:8300/
    ProxyPassReverse /  http://myinternal2.mydomain:8300/
    ProxyPassReverse /  http://myinternal3.mydomain:8300/
    ProxyPassReverseCookieDomain myinternal1.mydomain myhost.mydomain.org
    ProxyPassReverseCookieDomain myinternal2.mydomain myhost.mydomain.org
    ProxyPassReverseCookieDomain myinternal3.mydomain myhost.mydomain.org
    ProxyPassReverseCookiePath / /
    <Proxy balancer://cisas1.lab.i.cz>
        BalancerMember http://myinternal1.mydomain:8300 loadfactor=1 route=int1
        BalancerMember http://myinternal2.mydomain:8300 loadfactor=1 route=int2
        BalancerMember http://myinternal3.mydomain:8300 status=+H route=int3
        Order Deny,Allow
        Deny from none
        Allow from all
        ProxySet stickysession=ROUTEID
        ProxySet lbmethod=bytraffic
    </Proxy>
</VirtualHost>
5) Allow the httpd service to access the outside world via TCP - without that you will get only HTTP 503 and some message in error_log that the action was declined.
/usr/sbin/setsebool httpd_can_network_connect 1
6) Run the httpd service (and loadbalancing) and watch logs
systemctl restart httpd.service
journalctl -xn
tail -1000f /var/log/httpd/error_log

Payara, aneb zmrtvýchvstání aplikačního serveru Glassfish

SunOne7

Glassfish má za sebou dlouhatánskou cestu ... první, s čím jsem přišel do styku, byl SunOne7. Ten vznikl jako výsledek spolupráce s Netscape a Oracle stále ještě udržuje jakousi stařičkou dokumentaci.

Jeho výhodou byla jedna z prvních podpor EJB (což se ukázalo i nevýhodou, jelikož tato technologie, tj. EJB2 byla prakticky nepoužitelná) a administrační GUI, které naopak použitelné bylo. Z něj se daly konfigurovat a spouštět i vypínat instance aplikačního serveru, čili na tu dobu šlo o vskutku "enterprise" řešení, které v té době nemělo obdoby.
Výhod bylo víc, například integrace JMS a dalších J2EE technologií, aktuálních v roce 2003.
Největší výhodou byla ovšem výborná stabilita. Tenhle aplikáč nám snad nikdy nespadnul, pokud nebudu počítat memory leaky v našich vlastních aplikacích.

Mezi nevýhody patřilo využívání nativních knihoven a tudíž možnost spuštění pouze na OS Solaris, Windows a RedHat 7.2. Rozchodit monstrum jinde vyžadovalo náhodné vyměňování nativních knihoven, než se administrátor "trefil" do konstelace, která fungovala.
A jedna nevýhoda fatální - zapomeňte na zdrojáky nebo artefakty ve veřejných úložištích.
No, pryč od toho ... užili jsme si dost srandy :-)

Sun Java Application Server 8

V roce 2005 přišly specifikace JEE5, JDK5, EJB3, JPA a další, díky kterým se stal starý aplikáč v podstatě mrtvou technologií. Sun se do toho vrhnul po hlavě, část implementací integroval od jiných autorů, část napsal sám - práce odvedl mnoho a velmi rychle, byť s diskutabilní kvalitou.

Tou dobou se už začínala probouzet konkurence - z největších aktuálních jmenujme WebSphere, WebLogic, TomEE, WildFly/JBoss, Jetty, a další. Mírně zastarávající seznamy s malým srovnáním viz wiki nebo wiki.

Přiznávám, že s touto verzí jsem neměl moc zkušeností, jen si pamatuji, že to byl takový mezičlánek, ve kterém byla spousta nedodělků a chyb. V principu to fungovalo, ale ...

Sun Java Application Server 9 aneb Sun Glassfish Enterprise Server 2

SJAS8 byl v podstatě prototyp. SJAS9, později přejmenován na SGES2, znamenal dotažení technologií do konce. Velké problémy byly s JMS (OpenMQ), což byl server provozovaný v serveru, při chybné konfiguraci se prostě zasekl. Důvody jste museli hledat mimo Glassfish, přičemž trvalo dlouho, než jste přišli na to, v čem je přesně problém.
Komunikace mezi GF a MQ probíhala přes porty, a variant konfigurací bylo mnoho, snad ani autorům původně nedošlo, jakou variabilitu jim to dává. To je ale námět spíše na jiný blog - stručně - triviální varianta je "embedded", resp. "zapouzdřený" server, složitá a škálovatelná varianta je "remote" server ...

SJAS9 přišel v roce 2007, zkraje se ještě stávalo, že "zůstal viset" právě díky JMS, později ale byly i tyto chyby vyřešeny a poslední verze SGES2.1.1 byla velmi stabilní a bezproblémová (pokud jí nedestabilizovaly vaše chyby ve vašich aplikacích ;-) ).

Hlavní výhody proti dřívějším verzím:
  • podpora clusteringu - bylo možné provozovat n instancí na m strojích, mezi nimiž se replikovaly sessions uživatelů a bylo možné např. postupné nasazení nové verze, aniž by došlo k výpadku (to samozřejmě mělo svá pravidla, o tom zase jindy).
  • žádné nativní knihovny - kde běží JDK5/6, běží Glassfish (jen občas narazíte na problémy např. s Windows).
  • propracovaná dokumentace
  • kompletní podpora JEE5

Glassfish3

V roce 2010 Oracle koupil Sun a všechno se změnilo. Již předtím jaksi "v ilegalitě" vznikl tzv. Embedded Glassfish, na který jsem narazil prakticky náhodou - kupodivu ve veřejných úložištích, a začal ho používat k integračním testům. Ano, stačil mi JUnit, abych mohl testovat EJB3 s JMS, JPA a se vším všudy!
Embedded Glassfish je totiž přebalené vydání Glassfishe3 do jediného jar souboru. Chybí mu pár XML, která se dají snadno dotáhnout z jiných závislostí a jedete - tak málo stačilo k odstranění největší výtky vůči "enterprise aplikačním serverům"!

Glassfish3 náš tým nakonec přeskočil, ale pomohl nám díky zpětné kompatibilitě se SGES2 právě s testováním.
Embedded Glassfish byl nakonec opět pohlcen Glassfishem a stal se součástí jeho buildu a repository.

V roce 2011 Oracle oficiálně vydal Glassfish3 jako referenční implementaci JEE6. Vzápětí se ale probral JBoss a prakticky Glassfish3 převálcoval.

Byrokracie Oracle navíc silně rozladila některé vývojáře a byla poslední kapkou k jejich přestupu - kam jinam - často k JBoss.
Po vyjádření Oracle o ukončení komerční podpory Glassfishe a o vyčlenění "clusteringu" pouze do komerční verze to vypadalo, že Glassfish prostě skončí, zemře, je konec. Jenže se ukázalo, že tak snadné to mít Oracle nebude ...

Glassfish4 - smrt ... NEBUDE!

Oracle totiž narazil na pár háčků. Nápad s clusteringem vyprovokoval poměrně hlučnou odezvu vývojářů i mimo Oracle, takže od něj bylo ustoupeno. Nápad s ukončením komerční podpory sice Oracle dotáhl do konce, ale nikoho to zase tak moc netrápilo, protože i má osobní zkušenost s komeční podporou Sun/Oracle byla velmi špatná (a je doposud).
Licence Glassfishe ale umožňovala alespoň pasivní přístup ke zdrojákům, a bylo otázkou času, kdy dojde k tomu, kdy je někdo vyžene na GitHub (ano, byl jsem to já, ale ukázalo se, že nejsem první, viz níže :D).

Druhý háček spočíval v tom, že komunita JEE stále potřebuje referenční implementaci a nikdo si jí nemohl jen tak přivlastnit a zamezit přístupu k ní ostatním.

No a poslední háček - všechny implementace JEE7 používají komponenty svých konkurentů. Weld vyvíjí JBoss. EclipseLink vyvíjí Eclipse+Oracle. Catalinu vyvíjí Apache. Pokud by Oracle "zařízl" Glassfish, patrně by nevydal už víc jak jednu jedinou verzi WebLogic, vývoj by se zbrzdil.

A tak v roce 2013 Oracle vydal Glassfish4 jako referenční implementaci JEE7. Změn nebylo mnoho oproti GF3, spoustu práce odvedli externí programátoři, Oracle se zjevně snažil investovat minimum. Čím více chyb v GF, tím lépe pro WebLogic.

Přišel rok 2014 a náš zákazník nakonec souhlasil s "upgrade". Udělali jsme skok z JEE5+JDK6+GF2.1.1 rovnou na GF4+JDK8. Ukázalo se, že to není až tak snadné díky chybám, které ve zdrojácích zevlovaly už od Glassfishe3, čili často 5 i více let.

Payara

Začal jsem zkoumat licence a možnosti, jak se dostat do SVN. Po dnech a nocích, kdy se exporty po různu zasekávaly a padaly díky vadné integritě dat v SVN Oracle, se mi to nakonec podařilo. Nakonec jsem posílal patche přímo do JIRA ... a odezva ... na přijetí jsem čekal nějaké 3 měsíce.

To mě moc nebavilo a proběhla komunikace s šéfem Glassfishe Rezou Rahmannem - pravda, překvapilo mě už, že se se mnou baví, ale ještě víc mě překvapilo, že mě nasměroval na Steva Millige z C2B2 a Payaru.
Po další komunikaci byla moje oprava přijata během 3 dnů do Payary.

Po zastavení komerční podpory Glassfishe se totiž Steve rozhodl, že jí tudíž začne poskytovat sám. Udělal stejný krok jako já, převedl SVN na GitHub, domluvil se s Rezou, a postupně se k němu přidali další a další lidé z komunity, včetně mě.

Tým Payara a jeho přispěvatelé od té doby opravili spoustu chyb a jen tak mezi řečí pomalu posouvají Payaru dál ke specifikaci JEE8. Objevily se požadavky pro podporu Javy IBM, podporována je i Oracle JDK8 (původně 7), opravy se promítají zpětně do Glassfishe od Oracle a samozřejmě - existuje placená podpora, které bych vytkl jen to, že její ceny jsou typicky "enterprise", leč konkurenti nejsou levnější.

Krom toho žije dál i Embedded Payara a vznikly i další distribuce, většinou osekané o nepotřebnou funkcionalitu.

Nakonec vzniklo i toto zábavné reklamní video:

Placená podpora

Na druhou stranu, za Tomcat podporu taky neplatíte, že? Ale to, co umí Payara rozhodně neumí ;-)

Ve výsledku placená podpora slouží hlavně k čestné podpoře profesionálních týmů, které se o tyto projekty starají. Proto pokud pracujete na projektech pro velké firmy či stát, měli byste podporu platit.
Ve skutečnosti sice nezískáte větší podporu, než máte, leč přispíváte tím k tomu, aby aplikační server, který používáte, mohl být dál vyvíjen a udržován, tj. abyste po dvou letech nezjistili, že projekt prostě skončil a vy jedete na mrtvém koni. Což není tak málo, když si to tak vezmete, ne?

V tuto chvíli to vypadá tak, že C2B2 míří mezi velké firmy. Její vývojáři konzultují chyby ve Weld i EclipseLinku, komunita si vypomáhá, zatímco Oracle si stále něco plácá na svém písečku a o ostatní se moc nestará, ale možná je to jen můj pocit ...

Budoucnost

Kdyby někdo měl pocit, že na to má, tým Payary shání vývojáře. Já tam zatím nejdu, mám svůj boj jinde, který navíc slouží jako výborný obří reálný testovací příklad pro Payaru. A zatím můžu říct, že se všichni lepšíme.

Momentálně Websphere, WebLogic ani WildFly neumí všechno, co umí Payara. Možná umí něco navíc, možná něco umí lépe, ale ne dost na to, aby mělo cenu přecházet. Zvlášť když je Payara možná ještě převálcuje :-)

Řekl bych, že i Oracle na to nakonec nutně přijde a bude muset se k tomu postavit čelem. Těžko může komplikovat práci ostatním skrz licence, spíše bych hádal, že se C2B2 pokusí koupit a zklikvidovat, pokud tato konkurence začne být až moc silná.
Dokud ale bude Glassfish na GitHub, bude velmi těžké jeho "lepší klony" kontrolovat, uzmout zpět, takže tahle cesta Oracle asi nepomůže, ne na dlouho. Glassfish není jediný takový projekt, open source se dá sice všelijak poškodit, ale jen stěží kontrolovat, pokud má pod nohama tak silnou infrastrukturu, která má navíc pozitivní efekt i pro komerční obříky.

Časy se mění ... jedno riziko bych tu ale viděl - a tím je paradoxně TTIP (v odkazu TPP, což je podobná dohoda) a různé formy kontroly internetu. Představte si, že vám vláda zablokuje, omezí nebo zpoplatní přístup na GitHub ... že je to paranoidní nápad? Možná - ale třeba tlak například na omezování síly šifer už tu dávno je.
Mimochodem, na některé weby a videa se nedostanete už teď. BBC blokuje přístup na (některá?) videa mimo UK, Youtube "tají" videa před Němci, atd. - co nevidíme, o tom nevím, tohle jsou jen zkušenosti, na které jsme narazili náhodou s kamarády v zahraničí.
To je ale už úplně jiné téma ...