November 30, 2017

Dogtag Certificate System 10 pkispawn Configuraiton file

Introduction

Reference from pki_default.cfg(5) that ships with pki-server version 10.


# man 5 pki_default.cfg

pki_default.cfg(5)                      PKI Default Instance Configuration                      pki_default.cfg(5)

NAME
       pki_default.cfg - Certificate Server instance default config file.

LOCATION
       /etc/pki/default.cfg

DESCRIPTION
       This  file  contains  the  default settings for a Certificate Server instance created using pkispawn.  This
       file should not be edited, as it can  be  modified  when  the  Certificate  Server  packages  are  updated.
       Instead, when setting up a Certificate Server instance, a user should provide pkispawn with a configuration
       file containing overrides to the defaults in /etc/pki/default.cfg.  See pkispawn(8) for details.

SECTIONS
       default.cfg contains parameters that are grouped into sections.  These sections are stacked, so that param‐
       eters  defined in earlier sections can be overwritten by parameters defined in later sections. The sections
       are read in the following order: [DEFAULT], [Tomcat], and  the  subsystem  section  ([CA],  [KRA],  [OCSP],
       [TKS], or [TPS]). This allows the ability to specify parameters to be shared by all subsystems in [DEFAULT]
       or [Tomcat], and subsystem-specific customization.

       There are a small number of bootstrap parameters which are passed in the configuration  file  by  pkispawn.
       Other parameter's values can be interpolated tokens rather than explicit values. For example:

       pki_ca_signing_nickname=caSigningCert cert-%(pki_instance_name)s CA

       This  substitutes  the  value of pki_instance_name into the parameter value.  It is possible to interpolate
       any non-password parameter within a section or in [DEFAULT]. Any parameter used in interpolation  can  ONLY
       be  overridden  within  the  same section.  So, for example, pki_instance_name should only be overridden in
       [DEFAULT]; otherwise, interpolations can fail.

       Note:  Any non-password related parameter values in the configuration file that needs to contain a %  char‐
              acter  must  be properly escaped.  For example, a value of foo%bar would be specified as foo%%bar in
              the configuration file.

PRE-CHECK PARAMETERS
       Once the configuration parameters have been constructed from the above  sections  and  overrides,  pkispawn
       will  perform  a series of basic tests to determine if the parameters being passed in are valid and consis‐
       tent, before starting any installation.  In pre-check mode, these tests  are  executed  and  then  pkispawn
       exits.

       It  is  possible  to  disable specific tests by setting the directives below.  While all these tests should
       pass to ensure a successful installation, it may be reasonable to skip tests in pre-check mode.

       pki_skip_ds_verify

              Skip verification of the Directory Server credentials.  In this test, pkispawn attempts to  bind  to
              the  directory server instance for the internal database using the provided credentials.  This could
              be skipped if the directory server instance does not yet exist  or  is  inaccessible.   Defaults  to
              False.

       pki_skip_sd_verify

              Skip verification of the security domain user/password.  In this test, pkispawn attempts to log onto
              the security domain using the provided credentials.  This can be skipped if the security  domain  is
              unavailable. Defaults to False.

GENERAL INSTANCE PARAMETERS
       The parameters described below, as well as the parameters located in the following sections, can be custom‐
       ized as part of a deployment.  This list is not exhaustive.

       pki_instance_name

              Name of the instance. The instance is located at /var/lib/pki/<instance_name>.  For Java subsystems,
              the default is specified as pki-tomcat.

       pki_https_port, pki_http_port

              Secure  and unsecure ports.  Defaults to standard Tomcat ports 8443 and 8080, respectively, for Java
              subsystems.

       pki_ajp_port, pki_tomcat_server_port

              Ports for Tomcat subsystems.  Defaults to standard Tomcat ports of 8009 and 8005, respectively.

       pki_ajp_host

              Host on which to listen for AJP requests.  Defaults to localhost to listen to local traffic only.

       pki_proxy_http_port, pki_proxy_https_port, pki_enable_proxy

              Ports for an Apache proxy server. Certificate Server instances can be run  behind  an  Apache  proxy
              server,  which will communicate with the Tomcat instance through the AJP port.  See the Red Hat Cer‐
              tificate System documentation  at  https://access.redhat.com/knowledge/docs/Red_Hat_Certificate_Sys‐
              tem/ for details.

       pki_user, pki_group, pki_audit_group

              Specifies  the  default  administrative user, group, and auditor group identities for PKI instances.
              The default user and group are both specified as pkiuser, and the default audit group  is  specified
              as pkiaudit.

       pki_token_name, pki_token_password

              The  token  and  password where this instance's system certificate and keys are stored.  Defaults to
              the NSS internal software token.

       pki_hsm_enable, pki_hsm_libfile, pki_hsm_modulename

              If an optional hardware security module (HSM) is being utilized (rather than  the  default  software
              security  module  included  in  NSS),  then  the  pki_hsm_enable parameter must be set to 'True' (by
              default this parameter is 'False'), and values must be supplied for both the pki_hsm_libfile (e.  g.
              - pki_hsm_libfile=/opt/nfast/toolkits/pkcs11/libcknfast.so) and pki_hsm_modulename parameters (e. g.
              - pki_hsm_modulename=nethsm).

   SYSTEM CERTIFICATE PARAMETERS
       pkispawn sets up a number of system certificates for each subsystem.  The  system  certificates  which  are
       required differ between subsystems.  Each system certificate is denoted by a tag, as noted below.  The dif‐
       ferent system certificates are:

              * signing certificate ("ca_signing").  Used to sign other certificates.  Required for CA.

              * OCSP signing certificate ("ocsp_signing" in CA, "signing" in OCSP).  Used to sign CRLs.   Required
              for OCSP and CA.

              * storage certificate ("storage").  Used to encrypt keys for storage in KRA.  Required for KRA only.

              *  transport certificate ("transport").  Used to encrypt keys in transport to the KRA.  Required for
              KRA only.

              * subsystem certificate ("subsystem").  Used to communicate between subsystems within  the  security
              domain.  Issued by the security domain CA.  Required for all subsystems.

              * server certificate ("sslserver").  Used for communication with the server.  One server certificate
              is required for each Certificate Server instance.

              * audit signing certificate ("audit_signing").  Used to sign audit logs.  Required for  all  subsys‐
              tems except the RA.

       Each system certificate can be customized using the parameters below:

       pki_<tag>_key_type, pki_<type>_keysize, pki_<tag>_key_algorithm

              Characteristics   of  the  private  key.  See  the  Red  Hat  Certificate  System  documentation  at
              https://access.redhat.com/knowledge/docs/Red_Hat_Certificate_System/  for  possible  options.    The
              defaults are RSA for the type, 2048 bits for the key size, and SHA256withRSA for the algorithm.

       pki_<tag>_signing_algorithm

              For signing certificates, the algorithm used for signing.  Defaults to SHA256withRSA.

       pki_<tag>_token

              Location  where  the  certificate and private key are stored.  Defaults to the internal software NSS
              token database.

       pki_<tag>_nickname

              Nickname for the certificate in the token database.

       pki_<tag>_subject_dn

              Subject DN for the certificate.  The  subject  DN  for  the  SSL  Server  certificate  must  include
              CN=<hostname>.

   ADMIN USER PARAMETERS
       pkispawn creates a bootstrap administrative user that is a member of all the necessary groups to administer
       the installed subsystem.  On a security domain CA, the CA administrative user  is  also  a  member  of  the
       groups  required  to  register  a  new subsystem on the security domain.  The certificate and keys for this
       administrative user are stored in a PKCS #12 file in pki_client_dir, and can be imported into a browser  to
       administer the system.

       pki_admin_name, pki_admin_uid

              Name and UID of this administrative user.  Defaults to caadmin for CA, kraadmin for KRA, etc.

       pki_admin_password

              Password  for  the  admin  user.   This  password is used to log into the pki-console (unless client
              authentication is enabled), as well as log into the security domain CA.

       pki_admin_email

              Email address for the admin user.

       pki_admin_dualkey, pki_admin_keysize, pki_admin_key_type

              Settings for the administrator certificate and keys.

       pki_admin_subject_dn

              Subject   DN   for   the   administrator   certificate.    Defaults   to    cn=PKI    Administrator,
              e=%(pki_admin_email)s, o=%(pki_security_domain_name)s.

       pki_admin_nickname
              Nickname for the administrator certificate.

       pki_import_admin_cert

              Set to True to import an existing admin certificate for the admin user, rather than generating a new
              one.  A subsystem-specific administrator will still be created within  the  subsystem's  LDAP  tree.
              This  is useful to allow multiple subsystems within the same instance to be more easily administered
              from the same browser by using a single certificate.

              By default, this is set to False for CA subsystems and true for KRA, OCSP, TKS, and TPS  subsystems.
              In this case, the admin certificate is read from the file ca_admin.cert in pki_client_dir.

              Note that cloned subsystems do not create a new administrative user.  The administrative user of the
              master subsystem is used instead, and the details of this master  user  are  replicated  during  the
              install.

       pki_client_admin_cert_p12

              Location for the PKCS #12 file containing the administrative user's certificate and keys.  For a CA,
              this defaults to ca_admin_cert.p12 in the pki_client_dir directory.

   BACKUP PARAMETERS
       pki_backup_keys, pki_backup_password

              Set to True to back up the subsystem certificates and keys to a PKCS #12 file.  This  file  will  be
              located  in  /var/lib/pki/<instance_name>/alias.  pki_backup_password is the password of the PKCS#12
              file.

       Important:
              Since HSM keys are stored in the HSM (hardware), they cannot be backed up to a PKCS #12 file  (soft‐
              ware).   Therefore,  if  pki_hsm_enable  is  set to True, pki_backup_keys should be set to False and
              pki_backup_password should be left unset (the default values in /etc/pki/default.cfg).   Failure  to
              do so will result in pkispawn reporting this error and exiting.

   CLIENT DIRECTORY PARAMETERS
       pki_client_dir

              This  is  the  location where all client data used during the installation is stored.  At the end of
              the invocation of pkispawn, the administrative user's certificate and keys are stored in a PKCS  #12
              file in this location.

              Note:  When using an HSM, it is currently recommended to NOT specify a value for pki_client_dir that
              is different from the default value.

       pki_client_database_dir, pki_client_database_password

              Location where an NSS token database is created in order to generate a key  for  the  administrative
              user.  Usually, the data in this location is removed at the end of the installation, as the keys and
              certificates are stored in a PKCS #12 file in pki_client_dir.

       pki_client_database_purge

              Set to True to remove pki_client_database_dir at the end of the installation.  Defaults to True.

   INTERNAL DATABASE PARAMETERS

       pki_ds_hostname, pki_ds_ldap_port, pki_ds_ldaps_port

              Hostname and ports for the internal database.  Defaults to localhost, 389, and 636, respectively.

       pki_ds_bind_dn, pki_ds_password

              Credentials to connect to the database  during  installation.   Directory  Manager-level  access  is
              required during installation to set up the relevant schema and database.  During the installation, a
              more restricted Certificate Server user is set up to client authentication connections to the  data‐
              base.   Some  additional configuration is required, including setting up the directory server to use
              SSL.  See the documentation for details.

       pki_ds_secure_connection

              Sets whether to require connections to the Directory Server using LDAPS.  This requires  SSL  to  be
              set up on the Directory Server first.  Defaults to false.

       pki_ds_secure_connection_ca_nickname

              Once  a  Directory  Server  CA  certificate  has  been imported into the PKI security databases (see
              pki_ds_secure_connection_ca_pem_file), pki_ds_secure_connection_ca_nickname will contain  the  nick‐
              name  under  which  it  is stored.  The default.cfg file contains a default value for this nickname.
              This parameter is only utilized when pki_ds_secure_connection has been set to true.

       pki_ds_secure_connection_ca_pem_file

              The pki_ds_secure_connection_ca_pem_file parameter will consist of the fully-qualified path  includ‐
              ing  the  filename of a file which contains an exported copy of a Directory Server's CA certificate.
              While this parameter is only utilized when pki_ds_secure_connection has been set to  true,  a  valid
              value is required for this parameter whenever this condition exists.

       pki_ds_remove_data

              Sets  whether  to  remove  any  data from the base DN before starting the installation.  Defaults to
              True.

       pki_ds_base_dn

              The base DN for the internal database.  It is advised that the Certificate Server have its own  base
              DN  for its internal database.  If the base DN does not exist, it will be created during the running
              of pkispawn.  For a cloned subsystem, the base DN for the clone subsystem MUST be the  same  as  for
              the master subsystem.

       pki_ds_database

              Name  of  the back-end database.  It is advised that the Certificate Server have its own base DN for
              its internal database.  If the back-end does not exist, it will be created  during  the  running  of
              pkispawn.

   ISSUING CA PARAMETERS

       pki_issuing_ca_hostname, pki_issuing_ca_https_port, pki_issuing_ca_uri

              Hostname  and port, or URI of the issuing CA.  Required for installations of subordinate CA and non-
              CA subsystems.  This should point to the CA that will issue the relevant system certificates for the
              subsystem.   In  a default install, this defaults to the CA subsystem within the same instance.  The
              URI has the format https://<ca_hostname>:<ca_https_port>.

   MISCELLANEOUS PARAMETERS

       pki_restart_configured_instance

              Sets whether to restart the instance after configuration is complete.  Defaults to True.

       pki_enable_access_log

              Located in the [Tomcat] section, this variable determines whether the instance will enable (True) or
              disable (False) Tomcat access logging.  Defaults to True.

       pki_enable_java_debugger

              Sets  whether  to  attach  a  Java  debugger  such  as  Eclipse to the instance for troubleshooting.
              Defaults to False.

       pki_enable_on_system_boot

              Sets whether or not PKI instances should be started upon system boot.

              Currently, if this PKI subsystem exists within a shared instance, and  it  has  been  configured  to
              start  upon  system  boot,  then  ALL  other previously configured PKI subsystems within this shared
              instance will start upon system boot.

              Similarly, if this PKI subsystem exists within a shared instance, and it has been configured to  NOT
              start  upon  system  boot,  then  ALL  other previously configured PKI subsystems within this shared
              instance will NOT start upon system boot.

              Additionally, if more than one PKI instance exists, no  granularity  exists  which  allows  one  PKI
              instance  to  be enabled while another PKI instance is disabled (i.e. - PKI instances are either all
              enabled or all disabled).  To provide this capability, the PKI instances  must  reside  on  separate
              machines.

              Defaults to True (see the following note on why this was previously 'False').

       Note:  Since  this parameter did not exist prior to Dogtag 10.2.3, the default behavior of PKI instances in
              Dogtag 10.2.2 and prior was False.  To manually enable this behavior, obtain  superuser  privileges,
              and  execute 'systemctl enable pki-tomcatd.target'; to manually disable this behavior, execute 'sys‐
              temctl disable pki-tomcatd.target'.

       pki_security_manager

              Enables the Java security manager policies provided by  the  JDK  to  be  used  with  the  instance.
              Defaults to True.

   SECURITY DOMAIN PARAMETERS
       The  security  domain  is  a  component  that  facilitates  communication between subsystems.  The first CA
       installed hosts this component and is used to register subsequent  subsystems  with  the  security  domain.
       These  subsystems can communicate with each other using their subsystem certificate, which is issued by the
       security domain CA.  For more information about the security domain component, see the Red Hat  Certificate
       System documentation at https://access.redhat.com/knowledge/docs/Red_Hat_Certificate_System/.

       pki_security_domain_hostname, pki_security_domain_https_port

              Location of the security domain.  Required for KRA, OCSP, TKS, and TPS subsystems and for CA subsys‐
              tems joining a security domain.  Defaults to the location  of  the  CA  subsystem  within  the  same
              instance.

       pki_security_domain_user, pki_security_domain_password

              Administrative  user  of  the security domain.  Required for KRA, OCSP, TKS, and TPS subsystems, and
              for CA subsystems joining a security domain.  Defaults to the administrative user for the CA subsys‐
              tem within the same instance (caadmin).

       pki_security_domain_name

              The name of the security domain. This is required for the security domain CA.

   CLONE PARAMETERS
       pki_clone

              Installs a clone, rather than original, subsystem.

       pki_clone_pkcs12_password, pki_clone_pkcs12_path

              Location and password of the PKCS #12 file containing the system certificates for the master subsys‐
              tem being cloned.  This file should be readable by the user that the Certificate Server  is  running
              as  (default  of  pkiuser),  and  have the correct selinux context (pki_tomcat_cert_t).  This can be
              achieved by placing the file in /var/lib/pki/<instance_name>/alias.

       Important:
              Since HSM keys are stored in the HSM (hardware), they cannot be copied to a  PKCS  #12  file  (soft‐
              ware).  For the case of clones using an HSM, this means that the HSM keys must be shared between the
              master and its clones.  Therefore, if pki_hsm_enable is set to True, both pki_clone_pkcs12_path  and
              pki_clone_pkcs12_password  should be left unset (the default values in /etc/pki/default.cfg).  Fail‐
              ure to do so will result in pkispawn reporting this error and exiting.

       pki_clone_setup_replication

              Defaults to True.  If set to False, the installer does not set up replication  agreements  from  the
              master  to  the clone as part of the subsystem configuration.  In this case, it is expected that the
              top level suffix already exists, and that the data has already been replicated.  This option is use‐
              ful  if you want to use other tools to create and manage your replication topology, or if the baseDN
              is already replicated as part of a top-level suffix.

       pki_clone_reindex_data

              Defaults to False.  This parameter is only  relevant  when  pki_clone_setup_replication  is  set  to
              False.   In  this  case,  it is expected that the database has been prepared and replicated as noted
              above.  Part of that preparation could involve adding indexes and indexing the data.  If  you  would
              like  the  Dogtag  installer  to  add  the indexes and reindex the data instead, set pki_clone_rein‐
              dex_data to True.

       pki_clone_replication_master_port, pki_clone_replication_clone_port

              Ports on which replication occurs.  These are the ports on the master and  clone  databases  respec‐
              tively.  Defaults to the internal database port.

       pki_clone_replicate_schema

              Replicate  schema  when  the replication agreement is set up and the new instance (consumer) is ini‐
              tialized.  Otherwise, the schema must be installed in the clone as a separate step beforehand.  This
              does not usually have to be changed.  Defaults to True.

       pki_clone_replication_security

              The  type  of security used for the replication data.  This can be set to SSL (using LDAPS), TLS, or
              None.  Defaults to None.  For SSL and TLS, SSL must be set up for the database instances beforehand.

       pki_master_hostname, pki_master_https_port, pki_clone_uri

              Hostname and port, or URI of the subsystem being cloned.  The URI  format  is  https://<master_host‐
              name>:<master_https_port>  where  the default master hostname and https port are set to be the secu‐
              rity domain's hostname and https port.

   CA SERIAL NUMBER PARAMETERS

       pki_serial_number_range_start, pki_serial_number_range_end

              Sets the range of serial numbers to be used when issuing certificates.  Values here are  hexadecimal
              (without the 0x prefix).  It is useful to override these values when migrating data from another CA,
              so that serial number conflicts do not occur.  Defaults to 1 and 10000000 respectively.

       pki_request_number_range_start, pki_request_number_range_end

              Sets the range of request numbers to be used by the CA.  Values here are decimal.  It is  useful  to
              override  these  values when migrating data from another CA, so that request number conflicts do not
              occur.  Defaults to 1 and 10000000 respectively.

       pki_replica_number_range_start, pki_replica_number_range_end

              Sets the range of replica numbers to be used by the CA.  These numbers are used to identify database
              replicas in a replication topology.  Values here are decimal.  Defaults to 1 and 100 respectively.

   EXTERNAL CA CERTIFICATE PARAMETERS

       pki_external

              Sets whether the new CA will have a signing certificate that will be issued by an external CA.  This
              is a two step process.  In the first step, a CSR to be presented to the external  CA  is  generated.
              In  the  second  step,  the  issued  signing  certificate  and certificate chain are provided to the
              pkispawn utility to complete the installation.  Defaults to False.

       pki_external_csr_path

              Required in the first step of the external CA signing process.  The  CSR  will  be  printed  to  the
              screen and stored in this location.

       pki_external_step_two

              Specifies that this is the second step of the external CA process.  Defaults to False.

       pki_external_ca_cert_path, pki_external_ca_cert_chain_path

              Required  for  the  second  step of the external CA signing process.  This is the location of the CA
              signing cert (as issued by the external CA) and the external CA's certificate chain.

   SUBORDINATE CA CERTIFICATE PARAMETERS

       pki_subordinate

              Specifies whether the new CA which will be a subordinate of another CA.  The master CA is  specified
              by pki_issuing_ca.  Defaults to False.

       pki_subordinate_create_new_security_domain

              Set to True if the subordinate CA will host its own security domain.  Defaults to False.

       pki_subordinate_security_domain_name

              Used when pki_subordinate_create_security_domain is set to True.  Specifies the name of the security
              domain to be hosted on the subordinate CA.

   STANDALONE PKI PARAMETERS
       A stand-alone PKI subsystem is defined as a non-CA PKI subsystem that does not contain a CA as  a  part  of
       its deployment, and functions as its own security domain.  Currently, only stand-alone KRAs are supported.

       pki_standalone

              Sets  whether or not the new PKI subsystem will be stand-alone.  This is a two step process.  In the
              first step, CSRs for each of this stand-alone PKI subsystem's certificates will be generated so that
              they  may be presented to the external CA.  In the second step, the issued certificates, external CA
              certificate, and external CA certificate chain are provided to the pkispawn utility to complete  the
              installation.  Defaults to False.

       pki_external_admin_csr_path

              Will  be generated by the first step of a stand-alone PKI process.  This is the location of the file
              containing the administrator's CSR (which will be  presented  to  the  external  CA).   Defaults  to
              '%(pki_instance_configuration_path)s/%(pki_subsystem_type)s_admin.csr'.

       pki_external_audit_signing_csr_path

              Will  be generated by the first step of a stand-alone PKI process.  This is the location of the file
              containing the audit signing CSR (which  will  be  presented  to  the  external  CA).   Defaults  to
              '%(pki_instance_configuration_path)s/%(pki_subsystem_type)s_audit_signing.csr'.

       pki_external_sslserver_csr_path

              Will  be generated by the first step of a stand-alone PKI process.  This is the location of the file
              containing the SSL  server  CSR  (which  will  be  presented  to  the  external  CA).   Defaults  to
              '%(pki_instance_configuration_path)s/%(pki_subsystem_type)s_sslserver.csr'.

       pki_external_storage_csr_path

              [KRA  ONLY]  Will be generated by the first step of a stand-alone KRA process.  This is the location
              of the file containing the storage CSR (which will be presented to the external  CA).   Defaults  to
              '%(pki_instance_configuration_path)s/kra_storage.csr'.

       pki_external_subsystem_csr_path

              Will  be generated by the first step of a stand-alone PKI process.  This is the location of the file
              containing  the  subsystem  CSR  (which  will  be  presented  to  the  external  CA).   Defaults  to
              '%(pki_instance_configuration_path)s/%(pki_subsystem_type)s_subsystem.csr'.

       pki_external_transport_csr_path

              [KRA  ONLY]  Will be generated by the first step of a stand-alone KRA process.  This is the location
              of the file containing the transport CSR (which will be presented to the external CA).  Defaults  to
              '%(pki_instance_configuration_path)s/kra_transport.csr'.

       pki_external_step_two

              Specifies that this is the second step of a standalone PKI process.  Defaults to False.

       pki_external_ca_cert_chain_path

              Required  for  the  second step of a stand-alone PKI process.  This is the location of the file con‐
              taining the  external  CA  signing  certificate  (as  issued  by  the  external  CA).   Defaults  to
              '%(pki_instance_configuration_path)s/external_ca.cert'.

       pki_external_ca_cert_path

              Required  for  the  second step of a stand-alone PKI process.  This is the location of the file con‐
              taining the  external  CA's  certificate  chain  (as  issued  by  the  external  CA).   Defaults  to
              '%(pki_instance_configuration_path)s/external_ca_chain.cert'.

       pki_external_admin_cert_path

              Required  for  the  second step of a stand-alone PKI process.  This is the location of the file con‐
              taining  the  administrator's  certificate  (as  issued  by   the   external   CA).    Defaults   to
              '%(pki_instance_configuration_path)s/%(pki_subsystem_type)s_admin.cert'.

       pki_external_audit_signing_cert_path

              Required  for  the  second step of a stand-alone PKI process.  This is the location of the file con‐
              taining  the  audit  signing  certificate  (as  issued   by   the   external   CA).    Defaults   to
              '%(pki_instance_configuration_path)s/%(pki_subsystem_type)s_audit_signing.cert'.

       pki_external_sslserver_cert_path

              Required  for  the  second step of a stand-alone PKI process.  This is the location of the file con‐
              taining the sslserver certificate (as issued by the external CA).  Defaults to  '%(pki_instance_con‐
              figuration_path)s/%(pki_subsystem_type)s_sslserver.cert'.

       pki_external_storage_cert_path

              [KRA  ONLY]  Required for the second step of a stand-alone KRA process.  This is the location of the
              file  containing  the  storage  certificate  (as  issued  by  the   external   CA).    Defaults   to
              '%(pki_instance_configuration_path)s/kra_storage.cert'.

       pki_external_subsystem_cert_path

              Required  for  the  second step of a stand-alone PKI process.  This is the location of the file con‐
              taining the subsystem certificate (as issued by the external CA).  Defaults to  '%(pki_instance_con‐
              figuration_path)s/%(pki_subsystem_type)s_subsystem.cert'.

       pki_external_transport_cert_path

              [KRA  ONLY]  Required for the second step of a stand-alone KRA process.  This is the location of the
              file  containing  the  transport  certificate  (as  issued  by  the  external  CA).    Defaults   to
              '%(pki_instance_configuration_path)s/kra_transport.cert'.

   TPS PARAMETERS

       pki_authdb_basedn

              Specifies the base DN of TPS authentication database.

       pki_authdb_hostname

              Specifies the hostname of TPS authentication database. Defaults to localhost.

       pki_authdb_port

              Specifies the port number of TPS authentication database. Defaults to 389.

       pki_authdb_secure_conn

              Specifies whether to use a secure connection to TPS authentication database.  Defaults to False.

       pki_enable_server_side_keygen

              Specifies  whether to enable server-side key generation. Defaults to False.  The location of the KRA
              instance should be specified in the pki_kra_uri parameter.

       pki_ca_uri

              Specifies the URI of the CA instance used by TPS to create and revoke user certificates. Defaults to
              the instance in which the TPS is running.

       pki_kra_uri

              Specifies  the  URI of the KRA instance used by TPS to archive and recover keys. Required if server-
              side key generation is enabled using the pki_enable_server_side_keygen parameter.  Defaults  to  the
              instance in which the TPS is running.

       pki_tks_uri

              Specifies  the  URI  of  the  TKS  instance used by TPS to generate symmetric keys.  Defaults to the
              instance in which the TPS is running.

AUTHORS
       Ade Lee <alee@redhat.com>.  pkispawn was written by the Dogtag project.

COPYRIGHT
       Copyright (c) 2012 Red Hat, Inc. This is licensed under the GNU General Public License, version 2  (GPLv2).
       A copy of this license is available at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.

SEE ALSO
       pkispawn(8)

version 1.0                                      December 13, 2012                              pki_default.cfg(5)

Apache Directory GUI Tool for Managing LDAP Server

Apache Directory is good GUI tool for managing general LDAP server, but it is particularly designed for ApacheDS.

http://directory.apache.org/studio/

curl Usage

HTTP request method:
  • -X POST
  • -X PUT
HTTP request Content-Type header
  • -H "Content-Type: application/x-www-form-urlencoded"
  • -H "Content-Type: application/json"
HTTP request body/data
  • form urlencoded: -d "param1=value1&param2=value2"
  • json: -d '{"key1":"value1", "key2":"value2"}'
Examples

curl -X GET http://www.google.se
curl -d "param1=value1&param2=value2" -H "Content-Type: application/x-www-form-urlencoded" -X POST http://localhost:3000/data
curl -d '{"key1":"value1", "key2":"value2"}' -H "Content-Type: application/json" -X POST http://localhost:3000/data

https://gist.github.com/subfuzion/08c5d85437d5d4f00e58

November 29, 2017

Installing and Testing Dogtag Certificate System 10 on CentOS 7

Introduction

Dogtag Certificate System is CA and is the upstream project for Red Hat Certificate System.

Prerequisite

CentOS 7

# cat /etc/redhat-release 
CentOS Linux release 7.2.1511 (Core) 
Check that FQDN is set, otherwise set it with:

# hostnamectl set-hostname dogtag.magnuskkarlsson.se
Set firewall rules otherwise disable it.

# systemctl stop firewalld; systemctl disable firewalld
If running in a lab environment without DNS set dns in hosts file.

# ip addr show
...
    inet 192.168.122.63/24 brd 192.168.122.255 scope global dynamic eth0
...

# echo "192.168.122.63 dogtag.magnuskkarlsson.se" >> /etc/hosts

389 Directory Server - Installation of just Base DS

Dogtag Certificate System requires ldap server 389 Directory Server, DS. On CentOS there is a light version (389-ds-base) and the full blown solution (389-ds). Here we stick to the light version.
  • 389-ds-base - 389 Directory Server is an LDAPv3 compliant server. The base package includes the LDAP server and command line utilities for server administration.
  • 389-ds - The 389 Directory Server, Administration Server, and Console Suite provide the LDAPv3 server, the httpd daemon used to administer the server, and the console GUI application used for server and user/group administration.

# yum install 389-ds-base -y
The installation rpm creates a default user for 389 DS.

# grep dirsrv /etc/passwd; grep dirsrv /etc/group
dirsrv:x:389:389:user for 389-ds-base:/usr/share/dirsrv:/sbin/nologin
dirsrv:x:389:
Now configure 389 DS.

# setup-ds.pl --silent\
  General.FullMachineName='dogtag.magnuskkarlsson.se'\
  General.SuiteSpotUserID=dirsrv\
  General.SuiteSpotGroup=dirsrv\
  slapd.ServerPort=389\
  slapd.ServerIdentifier=pki-tomcat\
  slapd.Suffix=dc=magnuskkarlsson,dc=se\
  slapd.RootDN="cn=Directory Manager"\
  slapd.RootDNPwd=redhat123
Your new DS instance 'pki-tomcat' was successfully created.
Exiting . . .
Log file is '/tmp/setupKwxW67.log'
Property Comment Default
FullMachineName Specifies the fully qualified domain name of the machine on which you are installing the server. The default is the local host name.
SuiteSpotUserID Specifies the user name as which the Directory Server instance runs. This parameter does not apply to the user as which the Administration Server runs. This should be changed for most deployments. The default is user nobody on Linux and Solaris and daemon on HP-UX.
SuiteSpotGroup Specifies the group as which the servers will run. This should be changed for most deployments. The default is group nobodyon Linux and Solaris and daemon on HP-UX.
ServerPort Specifies the port the server will use for LDAP connections. For information on selecting server port numbers.
ServerIdentifier Specifies the server identifier. This value is used as part of the name of the directory in which the Directory Server instance is installed.
For example, if the machine's hostname is phonebook, then this name is the default, and selecting it installs the Directory Server instance in a directory labeled slapd-phonebook.
Suffix Specifies the suffix under which to store the directory data.
RootDN Specifies the distinguished name used by the Directory Manager.
RootDNPwd Specifies the Directory Manager's password.
And check configuration log file.

# cat /tmp/setupKwxW67.log
[17/11/27:14:20:40] - [Setup] Info Your new DS instance 'pki-tomcat' was successfully created.
[17/11/27:14:20:40] - [Setup] Success Exiting . . .
Log file is '/tmp/setupKwxW67.log'
To start and stop the 389 DS server.

# systemctl status dirsrv@pki-tomcat
And to verify the installation.

# ldapsearch -x -h dogtag.magnuskkarlsson.se -p 389 -s base -b "" "objectclass=*" 
# extended LDIF
#
# LDAPv3
# base <> with scope baseObject
# filter: objectclass=*
# requesting: ALL
#

#
dn:
objectClass: top
defaultnamingcontext: dc=magnuskkarlsson,dc=se
dataversion: 020171127105053
netscapemdsuffix: cn=ldap://dc=dogtag,dc=magnuskkarlsson,dc=se:389

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1
Reference:

Dogtag Certificate System

Now when 389 DS is up running continue with the installation of Dogtag.

# yum install pki-ca -y
Then configure it.

# pkispawn 

IMPORTANT:

    Interactive installation currently only exists for very basic deployments!

    For example, deployments intent upon using advanced features such as:

        * Cloning,
        * Elliptic Curve Cryptography (ECC),
        * External CA,
        * Hardware Security Module (HSM),
        * Subordinate CA,
        * etc.,

    must provide the necessary override parameters in a separate
    configuration file.

    Run 'man pkispawn' for details.

Subsystem (CA/KRA/OCSP/TKS/TPS) [CA]: 

Tomcat:
  Instance [pki-tomcat]: 
  HTTP port [8080]: 
  Secure HTTP port [8443]: 
  AJP port [8009]: 
  Management port [8005]: 

Administrator:
  Username [caadmin]: 
  Password: 
  Verify password: 
  Import certificate (Yes/No) [N]? 
  Export certificate to [/root/.dogtag/pki-tomcat/ca_admin.cert]: 

Directory Server:
  Hostname [dogtag.magnuskkarlsson.se]: 
  Use a secure LDAPS connection (Yes/No/Quit) [N]? 
  LDAP Port [389]: 
  Bind DN [cn=Directory Manager]: 
  Password: 
  Base DN [o=pki-tomcat-CA]: 

Security Domain:
  Name [magnuskkarlsson.se Security Domain]: 

Begin installation (Yes/No/Quit)? Yes

Log file: /var/log/pki/pki-ca-spawn.20171127145419.log
Installing CA into /var/lib/pki/pki-tomcat.
Storing deployment configuration into /etc/sysconfig/pki/tomcat/pki-tomcat/ca/deployment.cfg.
Notice: Trust flag u is set automatically if the private key is present.
Created symlink from /etc/systemd/system/multi-user.target.wants/pki-tomcatd.target to /usr/lib/systemd/system/pki-tomcatd.target.

    ==========================================================================
                                INSTALLATION SUMMARY
    ==========================================================================

      Administrator's username:             caadmin
      Administrator's PKCS #12 file:
            /root/.dogtag/pki-tomcat/ca_admin_cert.p12

      To check the status of the subsystem:
            systemctl status pki-tomcatd@pki-tomcat.service

      To restart the subsystem:
            systemctl restart pki-tomcatd@pki-tomcat.service

      The URL for the subsystem is:
            https://dogtag.magnuskkarlsson.se:8443/ca

      PKI instances will be enabled upon system boot

    ==========================================================================
Checkout the used configuration for the interactive configuration.

# cat /etc/sysconfig/pki/tomcat/pki-tomcat/ca/deployment.cfg
[DEFAULT]
pki_instance_name = pki-tomcat

[CA]
pki_http_port = 8080
pki_https_port = 8443
pki_ajp_port = 8009
pki_tomcat_server_port = 8005
pki_admin_uid = caadmin
pki_admin_password = XXXXXXXX
pki_backup_password = XXXXXXXX
pki_client_database_password = XXXXXXXX
pki_client_pkcs12_password = XXXXXXXX
pki_import_admin_cert = False
pki_client_admin_cert = /root/.dogtag/pki-tomcat/ca_admin.cert
pki_ds_hostname = dogtag.magnuskkarlsson.se
pki_ds_ldap_port = 389
pki_ds_bind_dn = cn=Directory Manager
pki_ds_password = XXXXXXXX
pki_ds_base_dn = o=pki-tomcat-CA
pki_security_domain_name = magnuskkarlsson.se Security Domain
pki_client_pin = XXXXXXXX
pki_clone_pkcs12_password = XXXXXXXX
pki_external_pkcs12_password = XXXXXXXX
pki_pkcs12_password = XXXXXXXX
pki_one_time_pin = XXXXXXXX
pki_pin = XXXXXXXX
pki_replication_password = XXXXXXXX
pki_security_domain_password = XXXXXXXX
pki_server_pkcs12_password = XXXXXXXX
pki_token_password = XXXXXXXX

Overview of the installed Dogtag instance.


# pkidaemon status pki-tomcat
Status for pki-tomcat: pki-tomcat is running ..

    [CA Status Definitions]
    Unsecure URL        = http://dogtagsofthsm.magnuskkarlsson.se:8080/ca/ee/ca
    Secure Agent URL    = https://dogtagsofthsm.magnuskkarlsson.se:8443/ca/agent/ca
    Secure EE URL       = https://dogtagsofthsm.magnuskkarlsson.se:8443/ca/ee/ca
    Secure Admin URL    = https://dogtagsofthsm.magnuskkarlsson.se:8443/ca/services
    PKI Console Command = pkiconsole https://dogtagsofthsm.magnuskkarlsson.se:8443/ca
    Tomcat Port         = 8005 (for shutdown)

    [CA Configuration Definitions]
    PKI Instance Name:   pki-tomcat

    PKI Subsystem Type:  Root CA (Security Domain)

    Registered PKI Security Domain Information:
    ==========================================================================
    Name:  magnuskkarlsson.se Security Domain
    URL:   https://dogtagsofthsm.magnuskkarlsson.se:8443
    ==========================================================================

Test Dogtag Certificate System

Now lets test it! First copy admin p12 file (/root/.dogtag/pki-tomcat/ca_admin_cert.p12) to your remote test machine and import it to your web browser.
Then we will test the CA by creating a new certificate signing request, but first we need to create a new private key.

$ openssl genrsa -out mynewkey.key.pem 2048
Then create the certificate signing request.

$ openssl req -new -key mynewkey.key.pem -out mynewkey.cert.pem
Now we are ready to sign the request. Open https://dogtag.magnuskkarlsson.se:8443/ca/ee/ca/
On the Enrollment/Renewal tab select Manual Server Certificate Enrollment and on that page copy your certificate signing request.
After submitting we need to approve that submitting. Change url to https://dogtag.magnuskkarlsson.se:8443/ca/services and click on Agent Services.

November 25, 2017

Java EE 7 Concurrency Utilities (JSR 236) and Example

Introduction

The Concurrency Utilities (JSR 236) is completely new in Java EE 7 and is also new in the Java EE mindset. In previous Java EE version the idea of creating new threads was forbidden and the motivation behind it was that thread was error prune and the standard components in Java EE should be enough. In Java EE 7 that has changed and the developer is free again to create threads.

But the above reason holds still true, that threads can help your application to scale better and increase performance, but it can also introduce:

  • Deadlocks
  • Thread starvation
  • Concurrent accessing of shared resources

Reference Oracle The Java EE 7 Tutorial Chapter 56.1 Concurrency Basics

And if you are new to Threads you should also read the Java Standard Edition Tutorial about Threads Oracle Java Tutorial Lesson: Concurrency. Which also points out the following pitfalls

  • Thread Interference describes how errors are introduced when multiple threads access shared data.
  • Memory Consistency Errors describes errors that result from inconsistent views of shared memory.
  • Synchronized Methods describes a simple idiom that can effectively prevent thread interference and memory consistency errors.
  • Implicit Locks and Synchronization describes a more general synchronization idiom, and describes how synchronization is based on implicit locks.
  • Atomic Access talks about the general idea of operations that can't be interfered with by other threads.

Reference Oracle Java Tutorial Synchronization

And if you are dead serious about threads you should read the book Java Concurrency in Practice by Brian Goetz.

Out first Thread

So now when we knew that we must pay extra attention when writing our threads, lets create a new thread. This can be done from either: java.lang.Runnable, java.lang.Thread or java.util.concurrent.Callable. The first two are there for backward compatibility and when writing new code always use java.util.concurrent.Callable.


package se.magnuskkarlsson.example.javaee7.task.control;

import java.util.concurrent.Callable;
import java.util.logging.Logger;

public class TaskCallable implements Callable<Integer> {

    private final Logger log = Logger.getLogger(TaskCallable.class.getName());

    private final String id;

    public TaskCallable(String id) {
        this.id = id;
    }
    
    @Override
    public Integer call() throws Exception {
        log.info("[" + id + "] Starting a long running task");
        for (int i = 0; i < 3; ++i) {
            log.info("[" + id + "] Analysing task...");
            // We simulate now a long running task
            Thread.sleep(3000);
        }
        log.info("[" + id + "] Finished a long running task");
        // return some dummy data
        return (int) (Math.random() * 100);
    }

}

Then we want to test. Lets test it with a simple JAX-RS class.


package se.magnuskkarlsson.example.javaee7.task.boundary;

import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.ejb.LocalBean;
import javax.ejb.Startup;
import javax.enterprise.concurrent.ManagedExecutorService;
import javax.inject.Singleton;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import se.magnuskkarlsson.example.javaee7.task.control.TaskCallable;

@Startup
// we need to have only one and same instance, so we can clean up ManagedExecutorService
@Singleton
// Designates that a session bean exposes a no-interface view.
// This annotation is required if a session bean exposes any other client views 
// (local, remote, no-interface, 2.x Remote Home, 2.x Local Home, **Web Service**)
@LocalBean
@Path("/task")
public class TaskREST {

    @Resource
    ManagedExecutorService managedExecutorService;
    
    @PreDestroy
    public void destroy() {
        managedExecutorService.shutdownNow();
    }
    
    @POST
    public void create(@QueryParam("id") String id) {
        managedExecutorService.submit(new TaskCallable(id));
    }
    
}

And finally test with simple CURL commands.


$ curl -X POST http://localhost:8080/example-javaee7/rest/task?id=foo
$ curl -X POST http://localhost:8080/example-javaee7/rest/task?id=bar
$ curl -X POST http://localhost:8080/example-javaee7/rest/task?id=code
$ curl -X POST http://localhost:8080/example-javaee7/rest/task?id=nisse

Now we can see in the log that task are started, ran and finished.


21:45:12,129 INFO  [se.magnuskkarlsson.example.javaee7.task.control.TaskCallable] (EE-ManagedExecutorService-default-Thread-1) [foo] Starting a long running task
21:45:12,130 INFO  [se.magnuskkarlsson.example.javaee7.task.control.TaskCallable] (EE-ManagedExecutorService-default-Thread-1) [foo] Analysing task...
21:45:15,130 INFO  [se.magnuskkarlsson.example.javaee7.task.control.TaskCallable] (EE-ManagedExecutorService-default-Thread-1) [foo] Analysing task...
21:45:18,131 INFO  [se.magnuskkarlsson.example.javaee7.task.control.TaskCallable] (EE-ManagedExecutorService-default-Thread-1) [foo] Analysing task...
21:45:21,132 INFO  [se.magnuskkarlsson.example.javaee7.task.control.TaskCallable] (EE-ManagedExecutorService-default-Thread-1) [foo] Finished a long running task
21:45:33,249 INFO  [se.magnuskkarlsson.example.javaee7.task.control.TaskCallable] (EE-ManagedExecutorService-default-Thread-2) [bar] Starting a long running task
21:45:33,250 INFO  [se.magnuskkarlsson.example.javaee7.task.control.TaskCallable] (EE-ManagedExecutorService-default-Thread-2) [bar] Analysing task...
21:45:35,288 INFO  [se.magnuskkarlsson.example.javaee7.task.control.TaskCallable] (EE-ManagedExecutorService-default-Thread-3) [code] Starting a long running task
21:45:35,289 INFO  [se.magnuskkarlsson.example.javaee7.task.control.TaskCallable] (EE-ManagedExecutorService-default-Thread-3) [code] Analysing task...
21:45:36,250 INFO  [se.magnuskkarlsson.example.javaee7.task.control.TaskCallable] (EE-ManagedExecutorService-default-Thread-2) [bar] Analysing task...
21:45:38,209 INFO  [se.magnuskkarlsson.example.javaee7.task.control.TaskCallable] (EE-ManagedExecutorService-default-Thread-4) [nisse] Starting a long running task
21:45:38,210 INFO  [se.magnuskkarlsson.example.javaee7.task.control.TaskCallable] (EE-ManagedExecutorService-default-Thread-4) [nisse] Analysing task...
21:45:38,290 INFO  [se.magnuskkarlsson.example.javaee7.task.control.TaskCallable] (EE-ManagedExecutorService-default-Thread-3) [code] Analysing task...
21:45:39,251 INFO  [se.magnuskkarlsson.example.javaee7.task.control.TaskCallable] (EE-ManagedExecutorService-default-Thread-2) [bar] Analysing task...
21:45:41,210 INFO  [se.magnuskkarlsson.example.javaee7.task.control.TaskCallable] (EE-ManagedExecutorService-default-Thread-4) [nisse] Analysing task...
21:45:41,290 INFO  [se.magnuskkarlsson.example.javaee7.task.control.TaskCallable] (EE-ManagedExecutorService-default-Thread-3) [code] Analysing task...
21:45:42,251 INFO  [se.magnuskkarlsson.example.javaee7.task.control.TaskCallable] (EE-ManagedExecutorService-default-Thread-2) [bar] Finished a long running task
21:45:44,210 INFO  [se.magnuskkarlsson.example.javaee7.task.control.TaskCallable] (EE-ManagedExecutorService-default-Thread-4) [nisse] Analysing task...
21:45:44,290 INFO  [se.magnuskkarlsson.example.javaee7.task.control.TaskCallable] (EE-ManagedExecutorService-default-Thread-3) [code] Finished a long running task
21:45:47,211 INFO  [se.magnuskkarlsson.example.javaee7.task.control.TaskCallable] (EE-ManagedExecutorService-default-Thread-4) [nisse] Finished a long running task

Summary

The main class in Concurrency Utilities (JSR 236) are:

  • javax.enterprise.concurrent.ManagedExecutorService - which you can <T> Future<T> submit(Callable<T> task) task. java.util.concurrent.Future can also be preemptive shutdown by calling java.util.concurrent.Future#cancel(true).
  • The other that is just like above, but for the difference it can schedule threads for later, is javax.enterprise.concurrent.ManagedScheduledExecutorService.

November 24, 2017

The Java EE 8 specification (JSR 366) Released 18 Sep, 2017.

The Java EE 8 specification (JSR 366) was released 18 Sep 2017.

https://jcp.org/en/jsr/detail?id=366

"What's New in Java EE 8

Java EE 8 continues to improve API and programming models needed for today's applications and adds features requested by our world-wide community. This release modernizes support for many industry standards and continues simplification of enterprise ready APIs. Enhancements include:
  • Java Servlet 4.0 API with HTTP/2 support
  • Enhanced JSON support including a new JSON binding API
  • A new REST Reactive Client API
  • Asynchronous CDI Events
  • A new portable Security API
  • Server-Sent Events support (Client & Server-side)
  • Support for Java SE 8 new capabilities (e.g. Date & Time API, Streams API, annotations enhancements)"
Java EE 8 builds on Java EE 7. The following JSRs are new or updated in Java EE 8:
  • JSR 366 – Java EE 8 Platform
  • JSR 365 – Contexts and Dependency Injection (CDI) 2.0
  • JSR 367 – The Java API for JSON Binding (JSON-B) 1.0
  • JSR 369 – Java Servlet 4.0
  • JSR 370 – Java API for RESTful Web Services (JAX-RS) 2.1
  • JSR 372 – JavaServer Faces (JSF) 2.3
  • JSR 374 – Java API for JSON Processing (JSON-P)1.1
  • JSR 375 – Java EE Security API 1.0
  • JSR 380 – Bean Validation 2.0
  • JSR 250 – Common Annotations 1.3
  • JSR 338 – Java Persistence 2.2
  • JSR 356 – Java API for WebSocket 1.1
  • JSR 919 – JavaMail 1.6"
http://www.oracle.com/technetwork/java/javaee/overview/index.html

The Java EE 8 Tutorial
https://javaee.github.io/tutorial/

And the source code is now published on github
https://github.com/javaee/tutorial-examples

GlassFish 5.0 was released 21 Oct 2017 and is the reference implementation for Java EE 8
https://en.wikipedia.org/wiki/GlassFish



November 19, 2017

HTTP Keep-Alive aka Persistant Connection with Apache httpd

HTTP Keep-Alive is also known as persistent connection. In the HTTP response you have


Connection:Keep-Alive
Keep-Alive:timeout=5, max=100

And to configure this in Apache httpd



#
# KeepAlive: Whether or not to allow persistent connections (more than
# one request per connection). Set to "Off" to deactivate.
#
KeepAlive On

#
# MaxKeepAliveRequests: The maximum number of requests to allow
# during a persistent connection. Set to 0 to allow an unlimited amount.
# We recommend you leave this number high, for maximum performance.
#
MaxKeepAliveRequests 100

#
# KeepAliveTimeout: Number of seconds to wait for the next request from the
# same client on the same connection.
#
KeepAliveTimeout 5

NOTE: MaxKeepAlive is a counter that counts down for each request and after that is a new HTTP session renegotiated, that can be costly if HTTPS is used, but is necessary to clean up lingering HTTP sessions.



Java EE 7 and What is New?

Overview Java EE 7

https://image.slidesharecdn.com/javaee7inaction-150506105621-conversion-gate02/95/java-ee7-in-action-10-638.jpg?cb=1430927843

Reference https://www.slideshare.net/ankarajug/java-ee7-in-action

JMS 2.0
  • Fluent APIs
  • Unchecked exceptions
  • MDB activation properties, JMS resource definition, default
    JMS resources
Java API for WebSocket NEW

Java API for JSON Processing NEW

Bean Validation 1.1
  • Method constraints @javax.validation.Valid
JAX-RS 2.0
  • Client API
JPA 2.1
  • Schema generation javax.persistence.schema-generation.database.action
JSF 2.2
  • File upload component h:inputFile
Batch Applications for the Java Platform NEW

Concurrency Utilities for Java EE NEW





Java EE 7 Simple WebSocket Example

One of the new technologies in Java EE 7 is WebSockets. A killer use case for web sockets is when a client need push notifications from the server. Previously such a client needed to constantly ask the server if there were any updates, which was ineffective and consumed a lot of server cpu. So lets implement a simple chat application that uses web sockets.

First the server. The whole layout or architecture behind web sockets reminds us very much as with jax-rs annotations. We start with a @ServerEndpoint class annotation, just like rest @Path.

Then you have only one mandatory method @OnMessage public void onMessage(String message, Session session). The method input parameters can be different, please read the javadoc.

Then you have 3 optional methods documented here. @OnOpen, @OnClose and @OnError

I will implement all 4 methods. I will also remember all sessions and will use CDI annotation @ApplicationScoped for that, but for it to work, we need to put the data in a separate POJO and annotate that also with @ApplicationScoped and let CDI handle the lifecycle via @Inject.


package se.magnuskkarlsson.example.javaee7.chat.boundary;

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/chat")
@ApplicationScoped
public class ChatWebSocketServer {

    private final Logger log = Logger.getLogger(ChatWebSocketServer.class.getName());
    @Inject ChatSessions sessions;

    @OnOpen
    public void open(Session session) {
        sessions.getSessions().put(session.getId(), session);
        log.info("OPEN session " + session.getId());
        log.info("size " + sessions.getSessions().size());
    }

    @OnClose
    public void close(Session session) {
        sessions.getSessions().remove(session.getId());
        log.info("CLOSE session " + session.getId());
        log.info("size " + sessions.getSessions().size());
    }

    @OnError
    public void onError(Throwable error) {
        log.log(Level.SEVERE, "onError", error);
    }

    @OnMessage
    public void handleMessage(final String message, Session session) {
        log.info("size " + sessions.getSessions().size());
        sessions.getSessions().forEach((key, value) -> {
            try {
                value.getBasicRemote().sendText(message);
                log.info("SEND session " + value.getId() + ", message " + message);
            } catch (Exception e) {
                log.log(Level.SEVERE, "Failed to send text", e);
            }
        });
    }

}

package se.magnuskkarlsson.example.javaee7.chat.boundary;

import java.util.HashMap;
import java.util.Map;
import javax.enterprise.context.ApplicationScoped;
import javax.websocket.Session;

@ApplicationScoped
public class ChatSessions {
    
    private final Map<String, Session> sessions = new HashMap<>();

    public Map<String, Session> getSessions() {
        return sessions;
    }    
    
}

Now to the client. It is made up of a web page and javascript.


<!DOCTYPE html>
<html>
    <head>
        <title>Web Socket Demo</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script src="websocket.js"></script>
    </head>
    <body>
        <div>
            <form id="chatForm">
                Message: <input type="text" id="message" />
                <input type="button" value="Send" onclick="formSubmit()" />
            </form>
        </div>
        <div id="messages">
        </div>
    </body>
</html>


var socket = new WebSocket("ws://localhost:8080/example-javaee7/chat");
socket.onmessage = onMessage;

function onMessage(event) {
    var message = event.data;
    appendHtml("<--- Server : " + message);
}

function formSubmit() {
    var form = document.getElementById("chatForm");
    var message = form.elements["message"].value;
    socket.send(message);
    appendHtml("---> You : " + message);
}

function appendHtml(message) {
    var div = document.getElementById("messages")
    div.innerHTML += "<p>" + message + "</p>";
}

November 18, 2017

Java 8 Nashorn Using JavaScript on the JVM

Nashorn is the official JavaScript Engine in the Java Virtual Machine since Version 8. It supports and implements the ECMAScript 5.1 specification and competes among others directly with Google V8 (the script engine behind Node.js). Nashorn compiles JavaScript to Java Bytecode during runtime and thus provides high interoperability of Java and JavaScript.

Lets give it a try with our previous example in Java EE 7 Implementing Statistics with Interceptor, CDI Observes and LongSummaryStatistics

#!/usr/bin/jjs -fv

var uri = "http://localhost:8080/example-javaee7/rest/statistics"
var command = "curl ${uri}"
$EXEC(command)
var result = $OUT
print(result)
var resultAsArray = JSON.parse(result)
print(resultAsArray)

Now call it

$ chmod +x nashorn-demo.js
$ ./nashorn-demo.js 
nashorn full version 1.8.0_151-8u151-b12-0ubuntu0.16.04.2-b12
{"statistics":"LongSummaryStatistics{count=18, sum=111, min=0, average=6.166667, max=75}"}
[object Object]

Java EE 7 Implementing Statistics with Interceptor, CDI Observes and LongSummaryStatistics

Java EE 7 comes with many built in techniques. Lets add statistics to our application.

First we use the Interceptor to interceptor our method that we want to get performance statistics from.

package se.magnuskkarlsson.example.javaee7.monitoring.control;

import se.magnuskkarlsson.example.javaee7.monitoring.entity.PerformanceEvent;
import javax.enterprise.event.Event;
import javax.inject.Inject;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;

public class PerformanceInterceptor {

    @Inject
    Event<PerformanceEvent> events;
    
    @AroundInvoke
    public Object intercept(InvocationContext ctx) throws Exception {
        String className = ctx.getTarget().getClass().getName();
        String methodName = ctx.getMethod().getName();
        long start = System.currentTimeMillis();
        Object object = null;
        try {
            object = ctx.proceed();
        } finally {
            long duration = System.currentTimeMillis() - start;
            events.fire(new PerformanceEvent(className, methodName, duration));
        }
        return object;
    }

}

Above we only collect information and then use EE Event to fire it to a Observes. The data Object is a POJO.

package se.magnuskkarlsson.example.javaee7.monitoring.entity;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class PerformanceEvent {
   
    private final String clazz;
    private final String method;
    private final long duration;

    public PerformanceEvent(String clazz, String method, long duration) {
        this.clazz = clazz;
        this.method = method;
        this.duration = duration;
    }

    public String getClazz() {
        return clazz;
    }

    public String getMethod() {
        return method;
    }

    public long getDuration() {
        return duration;
    }

    @Override
    public String toString() {
        return "PerformanceEvent{" + "clazz=" + clazz + ", method=" + method + ", duration=" + duration + '}';
    }
    
}

And the Observes that recieves the Event.

package se.magnuskkarlsson.example.javaee7.monitoring.control;

import java.util.LongSummaryStatistics;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.ejb.ConcurrencyManagement;
import javax.ejb.ConcurrencyManagementType;
import javax.enterprise.event.Observes;
import javax.inject.Singleton;
import se.magnuskkarlsson.example.javaee7.monitoring.entity.PerformanceEvent;
import java.util.stream.Collectors;

@Singleton
// ConcurrencyManagement.Bean will make singleton bean open for concurrent calls
// see https://docs.oracle.com/javaee/7/tutorial/ejb-basicexamples002.htm
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class StatisticsObserver {

    CopyOnWriteArraySet<PerformanceEvent> events = new CopyOnWriteArraySet<>();
    
    public void log(@Observes PerformanceEvent event) {
        events.add(event);
    }

    public LongSummaryStatistics getStatistics() {
        return events.stream().collect(Collectors.summarizingLong(PerformanceEvent::getDuration));
    }
    
}

To be able to collect statistics from many concurrent methods, we first make the class a Singleton and then make it concurrent by using ConcurrencyManagement.Bean. Now we will receive concurrent incoming calls, so to make it thread safe we use CopyOnWriteArraySet.

So the now the collection is done with Interceptor and the calculation is done with LongSummaryStatistics. Now expose it via REST.

package se.magnuskkarlsson.example.javaee7.monitoring.boundary;

import javax.inject.Inject;
import javax.json.Json;
import javax.json.JsonObject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import se.magnuskkarlsson.example.javaee7.monitoring.control.StatisticsObserver;

@Path("/statistics")
@Produces({MediaType.APPLICATION_JSON})
public class StatisticsRest {
    
    @Inject
    StatisticsObserver bean;
    
    @GET
    public JsonObject getStatistics() {
        return Json.createObjectBuilder().add("statistics", bean.getStatistics().toString()).build();
    }
    
}

Then call the application a couple of times, then test the statistics via, e.g. a curl command.

$ curl -i -H "Accept: application/json" http://localhost:8080/example-javaee7/rest/statistics
HTTP/1.1 200 OK
Connection: keep-alive
X-Powered-By: Undertow/1
Server: JBoss-EAP/7
Content-Type: application/json
Content-Length: 90
Date: Sat, 18 Nov 2017 03:32:41 GMT

{"statistics":"LongSummaryStatistics{count=18, sum=111, min=0, average=6.166667, max=75}"}

To see more for Interceptor, please see my previous blog Java EE 7 Performance Monitor with Interceptors, Event and Observes .

November 17, 2017

Java EE 7 Performance Monitor with Interceptors, Event and Observes

Lets say you want to performance monitor your business class, that can be achieved with Java EE 7 Interceptors and @Observes. Lets start with the interceptor. Here will only want to encapsulate the actual aggregation and after collecting we use the Java EE 7 Event to send it to the @Observes. A clean separation of concerns.

package se.magnuskkarlsson.example.javaee7;

import javax.enterprise.event.Event;
import javax.inject.Inject;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;

public class PerformanceInterceptor {

    @Inject
    Event<PerformanceEvent> events;
    
    @AroundInvoke
    public Object intercept(InvocationContext ctx) throws Exception {
        String className = ctx.getTarget().getClass().getName();
        String methodName = ctx.getMethod().getName();
        long start = System.currentTimeMillis();
        Object object = null;
        try {
            object = ctx.proceed();
        } finally {
            long duration = System.currentTimeMillis() - start;
            events.fire(new PerformanceEvent(className, methodName, duration));
        }
        return object;
    }
}

And the class that holds the data is a POJO.

package se.magnuskkarlsson.example.javaee7;

public class PerformanceEvent {
   
    private final String clazz;
    private final String method;
    private final long duration;

    public PerformanceEvent(String clazz, String method, long duration) {
        this.clazz = clazz;
        this.method = method;
        this.duration = duration;
    }

    public String getClazz() {
        return clazz;
    }

    public String getMethod() {
        return method;
    }

    public long getDuration() {
        return duration;
    }

    @Override
    public String toString() {
        return "PerformanceEvent{" + "clazz=" + clazz + ", method=" + method + ", duration=" + duration + '}';
    }
    
}

And the @Observes.

package se.magnuskkarlsson.example.javaee7;

import java.util.logging.Logger;
import javax.enterprise.event.Observes;

public class PerformanceObserver {

    private final Logger log = Logger.getLogger(PerformanceObserver.class.getName());
    
    public void log(@Observes PerformanceEvent event) {
        log.info(event.toString());
    }

}

And to use in your class that we want to performance measure.

package se.magnuskkarlsson.example.javaee7.control;

import se.magnuskkarlsson.example.javaee7.PerformanceInterceptor;

@Stateless
@Interceptors(PerformanceInterceptor.class)
public class EmployeeBean {

// the public methods we want to measure goes here ...
}

And when we run it

00:37:38,008 INFO  [se.magnuskkarlsson.example.javaee7.PerformanceObserver] (default task-9) PerformanceEvent{clazz=se.magnuskkarlsson.example.javaee7.control.EmployeeBean, method=delete, duration=2}

Java EE 7 Cross Cutting Interceptor Example

Lets say you need a logging feature for all you business class. Instead of writing repeatable code in yours businesses classes, lets use a Java EE 7 interceptor instead.

package se.magnuskkarlsson.example.javaee7;

import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;

public class TraceLogger {

    private final Logger log = Logger.getLogger(AuditLogger.class.getName());

    @AroundInvoke
    public Object intercept(InvocationContext ctx) throws Exception {
        String className = ctx.getTarget().getClass().getName();
        String methodName = ctx.getMethod().getName();
        List<Object> paramaters = Arrays.asList(ctx.getParameters());
        log.info(className + "#" + methodName + " Starting " + paramaters + " ...");
        Object object = null;
        try {
            object = ctx.proceed();
        } finally {
            log.info(className + "#" + methodName + " Finished.");
        }
        return object;
    }

}

And to use it.

package se.magnuskkarlsson.example.javaee7.control;

import javax.interceptor.Interceptors;
import se.magnuskkarlsson.example.javaee7.AuditLogger;

@Stateless
@Interceptors(TraceLogger.class)
public class EmployeeBean {
...
// public business method goes here...
}

And to make CDI work you need WEB-INF/beans.xml. Note that in Java EE 7 you need to declare your interceptors in beans.xml, that you used to in Java EE 6.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
    bean-discovery-mode="all">
</beans>

Making Reusable JUnit Setup with TestRule and @Rule

In the below JAX-RS test we can extract the setup code to a reusable JUnit TestRule class.

public class EmployeeRestIT {
    private Client client;
    private WebTarget target;
    
    @Before
    public void setUp() throws Exception {
        client = ClientBuilder.newClient();
        target = client.target("http://localhost:8080/example-javaee7/rest/employee");
    }
...
}

And the JUnit TestRule class.

package se.magnuskkarlsson.example.javaee7;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

public class JaxRSClientTestRule implements TestRule {

    private final Client client;
    private final WebTarget target;

    private JaxRSClientTestRule(String uri) {
        client = ClientBuilder.newClient();
        target = client.target(uri);
    }

    public final static JaxRSClientTestRule buildWithUri(String uri) {
        return new JaxRSClientTestRule(uri);
    }
    
    public Client client() {
        return client;
    }
    
    public WebTarget target() {
        return target;
    }
    
    @Override
    public Statement apply(Statement base, Description description) {
        return new Statement() {

            @Override
            public void evaluate() throws Throwable {
                base.evaluate();
                client.close();
            }
        };
    }

}

And to use the JUnit TestRule class in our JUnit test class.

package se.magnuskkarlsson.example.javaee7.boundary;

import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import javax.ws.rs.client.Entity;
import static javax.ws.rs.core.MediaType.*;
import javax.ws.rs.core.Response;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Rule;
import org.junit.Test;
import se.magnuskkarlsson.example.javaee7.JaxRSClientTestRule;
import static se.magnuskkarlsson.example.javaee7.JaxRSClientTestRule.buildWithUri;

public class EmployeeRestIT {
    
    @Rule
    public JaxRSClientTestRule rule = buildWithUri("http://localhost:8080/example-javaee7/rest/employee");

    @Test
    public void crud() throws Exception {
        JsonObjectBuilder builderCreate = Json.createObjectBuilder();
        JsonObject input = builderCreate.add("name", "Foo Bar").build();
        Response respCreate = rule.target().request(APPLICATION_JSON).post(Entity.json(input));
        assertThat(respCreate.getStatus(), is(201));
        String location = respCreate.getHeaderString("Location");
        assertNotNull(location);

        Response respGet = rule.client().target(location).request(APPLICATION_JSON).get();
        assertThat(respGet.getStatus(), is(200));
        JsonObject payload = respGet.readEntity(JsonObject.class);
        System.out.println("payload='" + payload + "'");
        
        int id = payload.getInt("id");
        JsonObjectBuilder builderUpdate = Json.createObjectBuilder();
        JsonObject update = builderUpdate.add("id", id).add("name", "UPDATED!").build();
        Response respUpdate = rule.target().request(APPLICATION_JSON).put(Entity.json(update));
        assertThat(respCreate.getStatus(), is(201));
        System.out.println("respUpdate=" + respUpdate.readEntity(JsonObject.class));
        
        Response respDelete = rule.target().path("/" + id).request(APPLICATION_JSON).delete();
        assertThat(respDelete.getStatus(), is(204));
    }
}

November 16, 2017

Java EE 7 Designing JAX-RS with System Test

Whenever building a Java EE 6 or 7 application always use the javax:javaee-api maven dependency and only that. So lets start with a Java EE 7 maven pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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>se.magnuskkarlsson.examples</groupId>
    <artifactId>example-javaee7</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <failOnMissingWebXml>false</failOnMissingWebXml>
    </properties>
    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <finalName>example-javaee7</finalName>
    </build>
</project>

To make JAX-RS work we need to add an class that extends Application and annotated with @ApplicationPath.

package se.magnuskkarlsson.example.javaee7;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/rest")
public class JavaEE7Application extends Application {
}

Then we can write our Boundary JAX-RS class.

package se.magnuskkarlsson.example.javaee7.boundary;

import java.net.URI;
import java.util.List;
import javax.inject.Inject;
import javax.validation.Valid;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;

import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import se.magnuskkarlsson.example.javaee7.control.EmployeeBean;

import se.magnuskkarlsson.example.javaee7.entity.Employee;

@Path("/employee")
@Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public class EmployeeRest {

    @Inject
    EmployeeBean bean;

    // returns 200 OK if found, othereise 204 No Content
    @GET
    @Path("/{id}")
    public Employee findById(@PathParam(value = "id") long id) {
        return bean.findById(id);
    }

    // returns 200 OK, otherwise empty JsonArray
    @GET
    public List<Employee> findAll() {
        return bean.findAll();
    }

    // returns 201 Created and Location header, otherwise 400 Bad Request and reason in body
    @POST
    public Response create(@Context UriInfo uriInfo, @Valid Employee employee) {
        long id = bean.create(employee).getId();
        URI location = uriInfo.getAbsolutePathBuilder().path("/" + id).build();
        return Response.created(location).build();
    }

    // returns 200 OK, otherwise 400 Bad Request and reason in body
    @PUT
    public Employee update(@Valid Employee employee) {
        return bean.update(employee);
    }

    // returns always 204 No Content
    @DELETE
    @Path("/{id}")
    public void delete(@PathParam(value = "id") long id) {
        bean.delete(id);
    }

}

And our business logic we encapsulate in a Controller class.

package se.magnuskkarlsson.example.javaee7.control;

import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.EntityNotFoundException;
import javax.persistence.PersistenceContext;
import se.magnuskkarlsson.example.javaee7.entity.Employee;

@Stateless
public class EmployeeBean {

    @PersistenceContext
    private EntityManager em;

    public Employee findById(long id) {
        return em.find(Employee.class, id);
    }

    public List<Employee> findAll() {
        return em.createQuery("FROM Employee e").getResultList();
    }

    public Employee create(Employee employee) {
        em.persist(employee);
        return employee;
    }

    public Employee update(Employee employee) {
        em.merge(employee);
        return employee;
    }

    public void delete(long id) {
        try {
            Employee employee = em.getReference(Employee.class, id);
            em.remove(employee);
        } catch (EntityNotFoundException IGNORE) {
            // we want to delete anyway ...
        }
    }

}

And finally our Entity class.

package se.magnuskkarlsson.example.javaee7.entity;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.Size;

import javax.xml.bind.annotation.XmlRootElement;

@Entity
@XmlRootElement
public class Employee implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue
    private Long id;
    @Column(length = 255)
    @Size(min = 3, max = 255)
    private String name;

    public Employee() {
    }

    public Employee(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

We also need to a META-INF/persistence.xml file.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="javaee7" transaction-type="JTA">
        <properties>
            <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
        </properties>
    </persistence-unit>
</persistence>

And a WEB-INF/beans.xml file to make our application CDI aware.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
    bean-discovery-mode="all">
</beans>

To test this build and deploy your application to, e.g. JBoss EAP 7.

Then create a new simple java project that we suffix with -st, for system test. In that pom.xml we need to add a jax-rs and json-p implementation dependency.

<?xml version="1.0" encoding="UTF-8"?>
<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>se.magnuskkarlsson.examples</groupId>
    <artifactId>example-javaee7-st</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!-- jax-rs reference implementation -->
        <dependency>
            <groupId>org.glassfish.jersey.core</groupId>
            <artifactId>jersey-client</artifactId>
            <version>2.17</version>
            <scope>test</scope>
        </dependency>
        <!-- json-p reference implementation -->
        <dependency>
            <groupId>org.glassfish</groupId>
            <artifactId>javax.json</artifactId>
            <version>1.0.4</version>
            <scope>test</scope>
        </dependency>  
        <!-- the glue between jax-rs and json-p reference implementation -->
        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-json-processing</artifactId>
            <version>2.17</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

Now lets write our system test.

package se.magnuskkarlsson.example.javaee7.boundary;

import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import static javax.ws.rs.core.MediaType.*;
import javax.ws.rs.core.Response;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;

public class EmployeeRestIT {

    private Client client;
    private WebTarget target;
    
    @Before
    public void setUp() throws Exception {
        client = ClientBuilder.newClient();
        target = client.target("http://localhost:8080/example-javaee7/rest/employee");
    }
    
    @Test
    public void crud() throws Exception {
        JsonObjectBuilder builderCreate = Json.createObjectBuilder();
        JsonObject input = builderCreate.add("name", "Foo Bar").build();
        Response respCreate = target.request(APPLICATION_JSON).post(Entity.json(input));
        assertThat(respCreate.getStatus(), is(201));
        String location = respCreate.getHeaderString("Location");
        assertNotNull(location);

        Response respGet = client.target(location).request(APPLICATION_JSON).get();
        assertThat(respGet.getStatus(), is(200));
        JsonObject payload = respGet.readEntity(JsonObject.class);
        System.out.println("payload='" + payload + "'");
        
        int id = payload.getInt("id");
        JsonObjectBuilder builderUpdate = Json.createObjectBuilder();
        JsonObject update = builderUpdate.add("id", id).add("name", "UPDATED!").build();
        Response respUpdate = target.request(APPLICATION_JSON).put(Entity.json(update));
        assertThat(respCreate.getStatus(), is(201));
        System.out.println("respUpdate=" + respUpdate.readEntity(JsonObject.class));
        
        Response respDelete = target.path("/" + id).request(APPLICATION_JSON).delete();
        assertThat(respDelete.getStatus(), is(204));
    }
    
    @Test
    public void findNonExisting() throws Exception {
        Response response = target.path("/-999").request(APPLICATION_JSON).get();
        assertThat(response.getStatus(), is(204));
        String payload = response.readEntity(String.class);
        assertThat(payload, is(""));
    }
    
    @Test
    public void findAll() throws Exception {
        Response resp = target.request(APPLICATION_JSON).get();
        assertThat(resp.getStatus(), is(200));
        JsonArray array = resp.readEntity(JsonArray.class);
        assertThat(array.size(), is(0));
    }

    @Test
    public void updateInvalid() throws Exception {
        JsonObjectBuilder builder = Json.createObjectBuilder();
        JsonObject input = builder.add("name", "E").build();
        Response response = target.request(APPLICATION_JSON).put(Entity.json(input));
        
        assertThat(response.getStatus(), is(400));
        System.out.println("status : " + response.getStatus());
        System.out.println("entity : " + response.readEntity(String.class));
        response.getHeaders().forEach((key, value) -> System.out.println(key + ": " + value));
    }
    
    @Test
    public void delete() throws Exception {
        JsonObjectBuilder builder = Json.createObjectBuilder();
        JsonObject input = builder.add("name", "Foo Bar").build();
        Response respCreate = target.request(APPLICATION_JSON).post(Entity.json(input));
        assertThat(respCreate.getStatus(), is(201));
        String location = respCreate.getHeaderString("Location");
        assertNotNull(location);
        
        Response respDelete = client.target(location).request(APPLICATION_JSON).delete();
        assertThat(respDelete.getStatus(), is(204));
    }
    
    @Test
    public void deleteNonExisting() throws Exception {
        Response respDelete = target.path("/-999").request(APPLICATION_JSON).delete();
        assertThat(respDelete.getStatus(), is(204));
    }
    
}

November 8, 2017

Java JSSE Default Truststore

"Creating an X509TrustManager

You can either implement this interface directly yourself or obtain one from a provider-based TrustManagerFactory (such as that supplied by the SunJSSE provider). You could also implement your own interface that delegates to a factory-generated trust manager. For example, you might do this to filter the resulting trust decisions and query an end-user through a graphical user interface.

Note: If a null KeyStore parameter is passed to the SunJSSE PKIX or SunX509 TrustManagerFactory, then the factory uses the following process to try to find trust material:

  1. If the javax.net.ssl.trustStore property is defined, then the TrustManagerFactory attempts to find a file using the file name specified by that system property, and uses that file for the KeyStore parameter. If thejavax.net.ssl.trustStorePassword system property is also defined, then its value is used to check the integrity of the data in the truststore before opening it.
    If the javax.net.ssl.trustStore property is defined but the specified file does not exist, then a default TrustManager using an empty keystore is created.
  2. If the javax.net.ssl.trustStore system property was not specified, then:
    • if the file java-home/lib/security/jssecacerts exists, that file is used;
    • if the file java-home/lib/security/cacerts exists, that file is used;
    • if neither of these files exists, then the SSL cipher suite is anonymous, does not perform any authentication, and thus does not need a truststore."