2.1. fejezet, GlassFish 3.1.2.2 konfiguráció

Ez az oldal elavult

Memória kezelés beállítása

A szerver memória használatát beállíthatjuk a [glassfishRoot]/glassfish/domains/domain1/config/domain.xml fájlban. Módosítsuk a beállításokat az alábbiak szerint:

        <jvm-options>-XX:MaxPermSize=2048M</jvm-options>
        <jvm-options>-XX:PermSize=256m</jvm-options>
        <jvm-options>-Xmx1024m</jvm-options>

Adatbázis kapcsolat kezelése


DataSource: com.mysql.jdbc.jdbc2.optional.MysqlDataSource

Ha a Glassfish indítása vagy újraindítása előtt bemásoljuk a mysql-connector-java-x.y.z-bin.jar-t (ahol az x,y,z a verziószámot jelöli) a glassfish/lib ill. a glassfish/domains/domain1/lib/ext/ könyvtárba, bővebb paraméterlistát kapunk a következő nézetben. Azonban vigyázzunk, mert az adatbázis url kétszer szerepel a listában. Az egyiket le kell törölni, hogy működjön az adatbázis elérésünk. (Az URL-t javaslom letörölni.)

Ha JavaSE-ből is szeretnénk elérhetővé tenni az adatbázist, a allow-non-component-callers tulajdonságot állítsuk true értékre. Ezt az újabb (GlassFish Server Open Source Edition 3.1.2.2 (build 5)) AppServer-ben a JDBC Connection Pools > [DataPool] > Advanced oldal alján megtaláljuk külön opcióként (Allow Non Component Callers néven).


E mellett a Java SE programhoz szükségünk lesz egy jndi.properties fájlra az src könyvtárban az alábbi tartalommal:

java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory
java.naming.factory.url.pkgs=com.sun.enterprise.naming
java.naming.provider.url=jnp://localhost:1099

Persze ezt megcsinálhatjuk Java programban is:

roperties props = new Properties();
props.setProperty(“java.naming.factory.initial”, “com.sun.enterprise.naming.SerialInitContextFactory);
props.setProperty(“java.naming.factory.url.pkgs”, “com.sun.enterprise.naming);
props.setProperty(“java.naming.factory.state”, “com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl);
//Ez csak a távoli szerver eléréséhez kell.
props.setProperty(“org.omg.CORBA.ORBInitialHost”, “127.0.0.1″);
props.setProperty(“org.omg.CORBA.ORBInitialPort”, “3700);
InitialContext ctx = new InitialContext (props);

Az alkalmazásunkhoz hozzá kell adnunk az appserv-rt.jar fájlt, ami a [GlassFish]/glassfish/lib könyvtárba van, és ez az összes Glassfish könyvtárt beemeli a projektbe. Meggondolandó ez a lépés, mert emiatt túl nagyra nő az exportáláskor az újabb .jar fájl. Érdemes külön könyvtárba másoltatni a csomagokat, amik a serverből valók, és amiket kézzel adtunk hozzá a projekthez. A .jar fájl futtatásakor egy script-tel bemásolhatók a szerverről a csomagok a program indítása előtt. Ez azért is lényeges, mert a META-INF könyvtárba tárolt MANIFEST.MF tartalmazza a Class-Path mezőt, és a .jar futtatásakor ezt veszi figyelembe a Java, hiába adjuk meg -cp paraméterben a saját alkönyvtárainkat.


Ha magyar UTF-8 karakter kódolást szeretnénk beállítani, akkor a properties listába vegyük fel a következő tulajdonságokat:

useUnicode=true
characterEncoding=UTF-8

Automatikus újrakapcsolódás paraméter nem biztos hogy kell, de beállítható:

autoReconnect=true
AutoReconnectForPools=true

Ez esetben com.mysql.jdbc.exceptions.jdbc4.CommunicationsException keletkezik, ha időtúllépés miatt megszakad a kapcsolat. Néhány MySQL beállítás:

SET GLOBAL connect_timeout=30;
SHOW VARIABLES LIKE 'connect_timeout';
SET GLOBAL wait_timeout=28800;
SHOW VARIABLES LIKE 'wait_timeout';
SET NAMES utf8;

A realm és a JAAS

Konténer alapú autentikációra és adatforrás alkalmazására egy alkalmazás szervert használtam. A GlassFish adminisztrációs felületén hozzunk létre új adatforrásokat és saját jogosultság kezelőket (Realm). A konténer alapú autentikáció két részből áll: egy bejelentkezési ablakból, és több szabályból, amik az erőforrások hozzáférhetőségét szabályozzák.

web.xml:

   <security-constraint>
        <web-resource-collection>
            <web-resource-name>admin</web-resource-name>
            <url-pattern>/admin/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>ROLE_ADMIN</role-name>
        </auth-constraint>
    </security-constraint>
 
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>user</web-resource-name>
            <url-pattern>/user/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>ROLE_ADMIN</role-name>
            <role-name>ROLE_USER</role-name>
        </auth-constraint>
    </security-constraint>
 
    <login-config>
        <auth-method>FORM</auth-method>
        <realm-name>myRealm</realm-name>
        <form-login-config>
            <form-login-page>/login.xhtml</form-login-page>
            <form-error-page>/login.xhtml?auth-error=true</form-error-page>
        </form-login-config>
    </login-config>
 
    <security-role>
        <role-name>ROLE_ADMIN</role-name>
    </security-role>
 
    <security-role>
        <role-name>ROLE_USER</role-name>
    </security-role>

glassfish-web.xml:

    <security-role-mapping>
        <role-name>ROLE_ADMIN</role-name>
        <group-name>ADMIN</group-name>
    </security-role-mapping>
    <security-role-mapping>
        <role-name>ROLE_USER</role-name>
        <group-name>USER</group-name>
    </security-role-mapping>

A Glassfish admin felületén hozzunk létre jdbc pool-t (Resources > JDBC > JDBC connection pools), adatforrást (Resources > JDBC > JDBC Resources), majd egy új, adatbázis alapú jogosultság kezelőt (Configurations > Server-config > Security > Realms). A szerver naplózását a Configurations > server-config > Logger Settings menüpontban állítsuk részletesebbre. A Log Levels fülön a javax.enterprise.system.core.security értéket állítsuk ALL-ra.

A realm létrehozása előtt adatbázis táblákat is kell készítenünk:

REATE TABLE IF NOT EXISTS `postcarduser` (
  `USER_ID` bigint(20) NOT NULL AUTO_INCREMENT,
  `admin` tinyint(1) NOT NULL,
  `emailAddress` varchar(255) COLLATE utf8_hungarian_ci DEFAULT NULL,
  `enabled` tinyint(1) NOT NULL,
  `password` varchar(255) COLLATE utf8_hungarian_ci NOT NULL,
  `username` varchar(255) COLLATE utf8_hungarian_ci NOT NULL,
  PRIMARY KEY (`USER_ID`),
  UNIQUE KEY `emailAddress` (`emailAddress`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_hungarian_ci AUTO_INCREMENT=3 ;
 
INSERT INTO `postcarduser` (`USER_ID`, `admin`, `emailAddress`, `enabled`, `password`, `username`) VALUES
(1, 1, 'papp.zoltan@mx1.hu', 1, 'q', 'Papp Zoltán'),
(2, 0, 'papp.zoltan@mx2.hu', 1, 'q', 'Papp Zoltán');
 
CREATE TABLE IF NOT EXISTS `usergroup` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `description` varchar(255) COLLATE utf8_hungarian_ci NOT NULL,
  `group_name` varchar(64) COLLATE utf8_hungarian_ci NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `group_name` (`group_name`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_hungarian_ci AUTO_INCREMENT=3 ;
 
INSERT INTO `usergroup` (`id`, `description`, `group_name`) VALUES
(1, 'Adminisztrátor', 'ADMIN'),
(2, 'Felhasználó', 'USER');
 
CREATE TABLE IF NOT EXISTS `user_join_group` (
  `user_name` varchar(255) COLLATE utf8_hungarian_ci NOT NULL,
  `group_name` varchar(64) COLLATE utf8_hungarian_ci NOT NULL,
  PRIMARY KEY (`user_name`,`group_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_hungarian_ci;
 
INSERT INTO `user_join_group` (`user_name`, `group_name`) VALUES
('papp.zoltan@mx1.hu', 'ADMIN'),
('papp.zoltan@mx1.hu', 'USER'),
('papp.zoltan@mx2.hu', 'USER');
 
CREATE TABLE IF NOT EXISTS `v_active_user` (
`username` varchar(255)
,`password` varchar(255)
);
 
DROP TABLE IF EXISTS `v_active_user`;
 
CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v_active_user` AS select `u`.`emailAddress` AS `username`,`u`.`password` AS `password` from `postcarduser` `u` where (`u`.`enabled` = 1);

A v_active_user és az alapadatok kivételével az installDB.jar létrehozza az összes felsorolt táblát.


Ha esetleg nem találnánk meg a Group Table User Name Column mezőt, vegyük fel tulajdonságként group-table-user-name-column néven. Ez a hiányosság az Eclipse Helios-hoz letöltött GlassFish Tools Bundle for Eclipse csomaghoz mellékelt Glassfish 3.1.0.0 verzióban jelentkezett.

A realm Digest Algorithm és Password Encryption Algorithm mezőit állítsuk none fejlesztés alatt, de produkciós környezetbe használjuk az MD5 vagy SHA-256 értéket.

(MS-SQL 2008-nál nem kell megadni az adatbázis előtagot a kepeslap.v_active_user és a kepeslap.user_join_group esetén. Realm változtatás után mindig indítsuk újra a Glassfish-t!)

A bejelentkező form a következő:

<form action="j_security_check" class="form" method="post">
 
            <p class="email">
                    <input type="text" name="j_username" id="j_username"/>
                    <label for="j_username">E-mail / Username</label>
            </p>
 
            <p class="pwd">
                    <input type="password" name="j_password" id="j_password" />
                    <label for="j_password">Password</label>
            </p>
 
            <p class="submit">
                    <input name="login" type="submit" value="Login" /> 
                    <input name="reset_pwd" type="submit" value="Reset Password">                                                                                                                              
            </p>
    </form>

Fontos kiemelni itt a j_security_check, a j_username és a j_password szerepét. Ezek ugyanis a konténer felé indított POST eseményt és az átadott paraméterekket jelölik.

Java kódból a HTTPServletRequest osztály 3 metódusával érhető el a bejelentkezett felhasználó:

public String getRemoteUser();
 
public java.security.Principal getUserPrincipal();
 
public boolean isUserInRole(String abstractRole);

Bejelentkezett felhasználó kiléptetése:

import java.io.IOException;
 
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.context.FacesContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
 
import org.apache.log4j.Logger;
 
@ManagedBean(name="authBackingBean")
@RequestScoped
public class AuthBackingBean {
 
  private static Logger log = Logger.getLogger(AuthBackingBean.class.getName());
 
  public void logout() {
    String result="/index.xhtml";
 
    FacesContext context = FacesContext.getCurrentInstance();
    HttpServletRequest request = (HttpServletRequest)context.getExternalContext().getRequest();
 
    FacesContext facesContext = FacesContext.getCurrentInstance();
    try {
      request.logout();
      String url = facesContext.getApplication().getViewHandler().getActionURL(facesContext, result);
      facesContext.getExternalContext().redirect(url);
    } catch (ServletException e) {
      log.error("Failed to logout user!", e);
      facesContext.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Hiba a levél küldésénél", "Nincs érvényes levélkézbesítést nyújtó szerver konfigurálva."));
    } catch (IOException ex) {
      log.error("FacesContext exception: ", ex);
    }
 
  }
}

Példa a bejelentkezett felhasználó elérésére:

    public User getLoggedInUser() {
        Object request = FacesContext.getCurrentInstance().getExternalContext().getRequest();
        User result = null;
        if (request instanceof HttpServletRequest) {
            Principal user = ((HttpServletRequest) request).getUserPrincipal();
            if (user != null) {
                List<User> userList = findByEmailAddress(user.getName(), true);
                if (userList.size()==1) {
                    result = userList.get(0);
                }
            }
        }
        return result;
    }

MySQL adatbázis telepítése

install.sh

#!/bin/bash
GLASSFISH_DIR="/home/pzoli/glassfish3"
cp -r installDB_origin installDB_lib
for i in `cat dirs.txt`; do
  jar_files="$GLASSFISH_DIR/$i.jar"
  cp -n $jar_files ./installDB_lib/
done
path="$CLASSPATH:./installDB_lib/*"
$JAVA_HOME/bin/java -cp $path -jar installDB.jar
cat install-images.sql install-defaults.sql tables_mysql_innodb.sql view.sql>defaults.sql
mysql  --default-character-set=utf8 -u pzoli -p kepeslap<defaults.sql
rm defaults.sql
rm -r installDB_lib

JavaMail

JNDI-n keresztül elérhető levelező rendszerünk is. A beállítási paraméterekhez az alábbiakból válogathatunk:

mail.smtp.socketFactory.port: 465
mail.smtp.port: 465
mail.smtp.socketFactory.fallback: false
mail.smtp.auth: true
mail.smtp.password: <gmail_password>
mail.smtp.socketFactory.class: javax.net.ssl.SSLSocketFactory

Ezek után a JavaMail Session elérése Java-ban:

    public void createSession() throws NoSuchProviderException {
 
        Session session = null;
        try {
            InitialContext ctx = new InitialContext();
            session = (Session) ctx.lookup("mail/kepeslap");
        } catch (NamingException e) {
           new NoSuchProviderException(e.getLocalizedMessage());
        }  
 
 
        transport = session.getTransport();
 
        MyTransportListener myTransportListener = new MyTransportListener();
        transport.addTransportListener(myTransportListener);
        transport.addConnectionListener(myTransportListener);
    }
 
    public void connectTransporter() throws MessagingException {
        transport.connect();
    }
 
    public void closeTransporterConnection() throws MessagingException {
        transport.close();
    }
 
    public String sendMailOverSMTP(Map<String, String> params) throws AddressException, MessagingException {
        String result = params.get("defaultResult");
        // Instantiate a message
        Message msg = new MimeMessage(session);
        // Set message attributes
        msg.setFrom(new InternetAddress(params.get("fromEmail")));
 
        if (params.containsKey("isCCToMe") && params.get("isCCToMe").equalsIgnoreCase("true")) {
            msg.setRecipient(RecipientType.CC, new InternetAddress(params.get("senderEmail")));
        }
 
        String recipientMailAddress = params.get("recipientMailAddress");
        if ((recipientMailAddress != null) && !recipientMailAddress.isEmpty()) {
            msg.addRecipient(Message.RecipientType.TO, new InternetAddress(recipientMailAddress));
        }
 
        msg.setSubject(params.get("subject"));
        msg.setSentDate(new Date());
 
        // Set message content
        msg.setContent(params.get("mailContent"), "text/html; charset=UTF-8");
        // msg.setText(mailStr);
 
        Address[] address;
        if (msg.getRecipients(Message.RecipientType.CC) != null) {
            address = ArrayHelper.join(msg.getRecipients(Message.RecipientType.TO), msg.getRecipients(Message.RecipientType.CC));
        } else {
            address = msg.getRecipients(Message.RecipientType.TO);
        }
        // Send the message
        transport.sendMessage(msg, address);
 
        return result;
    }

Apache2 AJP és a Glassfish

Hogy Glassfist Apache-on keresztül elérhessük, két dolgot tehetünk. Engedélyezünk két modult:

sudo a2enmod proxy proxy_ajp

és beállítjuk az egyik site konfigurációba (pl.: 000-default) a proxy elérését:

<VirtualHost *:80>
...
ProxyRequests off
ProxyPass /kepeslap ajp://127.0.0.1:8009/kepeslap
...
</VirtualHost>

Ezek után a Glassfish admin felületén a Configurations > server-config > Network Config > Network Listeners alatt létrehozunk egy jk-listener nevű, 8009 TCP porton figyelő kapcsolatot, ami továbbítja a kéréseket és válaszokat a két szerver között.

Forrás

JNDI properties

Hibaelhárítás

PWC2785: Cannot serialize session attribute

Adminisztrációs felületen a Configurations > server-config > Web Container > Manager Properties ablakba állítsuk be a Session File Name tulajdonságot üres értékre, majd indítsuk újra a servert.

SEVERE: log4j:ERROR log4j called after unloading

Az egész hibaüzenet:

SEVERE: log4j:ERROR log4j called after unloading, see http://logging.apache.org/log4j/1.2/faq.html#unload.
SEVERE: java.lang.IllegalStateException: Class invariant violation

Megoldás:

bin/asadmin create-system-properties org.apache.catalina.loader.WebappClassLoader.ENABLE_CLEAR_REFERENCES=false

Többnyelvű paraméterek

Hibajelenség:

WARNING: PWC4011: Unable to set request character encoding to UTF-8 from context /, because request parameters have already been read, or ServletRequest.getReader() has already been called.

Használjuk a parameter-encoding tagot a glassfish-web.xml fájlban:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN"
"http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
<glassfish-web-app error-url="">
...
<parameter-encoding default-charset="UTF-8"/>
...

Arra ügyeljünk, hogy ha van property tag a fájlban (pl.: alternatedocroot_1), akkor az elé helyezzük a parameter-encoding tagot, különben hibát jelez a validátor.

Ajánlott olvasmányok:

  • Alternate document root for static files (like images)
  • Multipart mail + Glassfish JNDI
  • access glassfish datasource remotely
  • Create custom resources
  • Working with Realms, Users, Groups, and Roles
  • Understanding web security
  • Configuring Security in Glassfish-v3, creating a jdbc realm
  • JDBC security realm with Glassfish and JSF
  • Apache Shiro
  • Glassfish tuning