最近は、LAN接続の外付けハードディスクやNAS機能が付いている個人用ルータが販売されています。こういう製品は誰にでも使いやすくできるようにWebベースの設定画面が用意されていてとても便利です。Windows の場合は、自動バックアップの環境を整えるのも比較的簡単にできます。Mac OSX やLinux から自動バックアップする場合は、smbclient コマンドを使うことでコマンドでの自動化が可能です。
smbclient コマンドは、ftpコマンドと似たコマンドなのでftpを知っている方は簡単に使うことができるはずです。
#!/bin/sh
#
# Sambaクライアントコマンド(smbclient)で、対象のSambaサーバにバックアップを行うスクリプト.
#
#
# Author: Satoshi Okita
# Create: 2009年 11月 18日 水曜日 19:06:40 JST
# Update: 2009年 11月 19日 木曜日 13:44:54 JST
# 初期ディレクトリの設定をできるようにしました。
#####
# 接続先の設定の仕方
# このスクリプトに「接続先の設定」というユーザが設定する場所があります。
#
#
#####
# targetlist.txtの書き方
#
# /tmp/bkup_targetdir/aaa.txt
# /tmp/bkup_targetdir/bbb.txt
# /etc/.
# 上記のように、1行単位に、バックアップしたいファイル、またはディレクトリを記述します。
# ディレクトリを指定する場合は、/etc/.のようにドットを記述してください。
# 現在はドットがあるかないかでsmbclientのコマンドを切り替えているためです。
# また、ファイル名はtargetlist.txtである必要はありません。
#
#####
# 使い方:
# ルート権限で以下のように実行します。
# #chmod 744 bkup2nas.sh
# #./bkup2nas.sh targetlist.txt
# ユーザが設定するパラメータはここから、
# 接続先の設定
# Sambaサーバの接続先(共有フォルダのルートを設定します。)
# //NetBiosName/や \\\\NetBiosName\\ShareRootのように記述します。
#
#service=//NetBiosName
#service=//NetBiosName/ShareRoot
#service=\\\\NetBiosName\\ShareRoot
service=//AAHost/Share1
#
# 保存したいディレクトリがある場合はdirectory変数に設定します。
# このディレクトリはこのスクリプトで自動作成しませんので、
# あらかじめ作っておく必要があります。
#
#directory=
directory=bkupdir
workgroup=Workgroup
username=Tarohoge
password=passhoge
# ここまで
# コマンドリスト
BASENAME=`which basename`
DIRNAME=`which dirname`
SMBCLIENT=`which smbclient`
input_file=$1
# バックアップ対象が書かれているファイルが引数に渡されたか
# 確認します。
if [ -z $input_file ]; then
echo "引数がありません."
exit 1
fi
# 引数で渡されたファイルを1行単位で処理する
while read buff;do
if [ ! -f "$buff" -a ! -d "$buff" ]; then
echo "$buffはファイル、またはディレクトリではありません"
exit 2
fi
# ディレクトリ名取得
dir_name=`$DIRNAME "$buff"`
filename=`$BASENAME "$buff"`
echo "ディレクトリは$dir_name ファイル名は $filename"
local_file=$buff;
smb_client_put_cmd=''
# ファイルバックアップの場合は、putコマンドを設定し、
# ディレクトリバックアップの場合は、mputコマンドを指定します。
if [ '.' = $filename ];then
smb_client_put_cmd='mput *'
else
smb_client_put_cmd="put $filename"
fi
# 初期ディレクトリが設定されている場合は、smbclientコマンドの
# -Dオプションが有効になります。
dir_opt=''
if [ ! -z $directory ];then
dir_opt="-D $directory"
fi
# smbclientコマンドを確認プロンプトなし、再帰モードで実行します。
$SMBCLIENT $service $password $dir_opt -W $workgroup -U $username \
-c "prompt;
recurse;
mkdir $dir_name;
cd .$dir_name;
lcd $dir_name;
$smb_client_put_cmd;"
done < $input_file
ブラウザの標準機能では、縦書きが難しいのでFlashで日本語の縦書きを試してみました。
package {
/**
* [Flex3.4 SDK コンパイル方法]
* mxmlc -target-player=10.0.0 Hello.as
* -target-playerオプションで、Flash Playerのバージョンを指定する必要がある。
*/
import flash.display.Sprite;
import flash.display.StageAlign
import flash.display.StageScaleMode;
import flash.text.engine.*;
/**
* Flex3.4 SDKでコンパイルして、Flash 10上で日本語の縦書きを表示させるサンプル.
* @link http://level0.kayac.com/2008/12/textbox.php#more
*/
public class Hello extends Sprite {
// コンストラクタ?.
public function Hello() {
// Flashの表示領域の設定.
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
stage.frameRate = 30;
// フォントとフォーマット指定.
var fontDesc:FontDescription = new FontDescription("メイリオ");
var format:ElementFormat = new ElementFormat(fontDesc);
format.locale = "ja";
format.fontSize = 50;
// 文字列と上記の情報から、コンテントを作る。
var text:String = "日本語縦書き";
var content:TextElement = new TextElement(text, format);
// 段落を作る.
var textBlock:TextBlock = new TextBlock(content)
// 縦書きの指定.
textBlock.lineRotation = TextRotation.ROTATE_90;
textBlock.textJustifier = new EastAsianJustifier(
"ja", LineJustification.ALL_BUT_LAST);
// 描写の設定
var height:uint = format.fontSize * text.length;
var textLine:TextLine = textBlock.createTextLine(null, height);
// 描写.
// このクラスはSpriteクラスを継承しているので、addChildで描写。
addChild(textLine);
}
}
}
cakePHP1.2 では、bootstrap.php が起動後、必要なPHPファイルが読み込まれフレームワークの初期設定が完了します。その後、cake/dispatcher.php がコントローラの呼び出し処理(Dispatcher#_invoke)を行います。この時に、以下のようなif文でscaffold変数が判定されることで、scaffold機能が呼び出されます。
if ($controller->scaffold !== false) {
App::import('Core', 'Scaffold');
return new Scaffold($controller, $params);
}
PHP には、!==演算子があり、$a !=== $b の場合、$a が $b と等しくないか、同じ型でない場合に TRUE を返します。
cakePHP のコントローラは、AppController クラスを継承します。$scaffoldは、AppController クラスの親であるControllerクラスでbool型として定義されています。そのため、アプリケーション開発者が各自作成するコントローラで$scaffoldを再定義時すると、var $scaffold = NULL;を記述した事と同じになり、上記if文の分岐判定を通るようになります。
始めてcakePHPを学んだときは、変数定義をしただけで、何故scaffold機能が読み込まれるのか疑問でしたが理解できました。
<?php
class ParentController {
var $scaffold = false;
}
class MyController extends ParentController {
var $scaffold; // NULLが入る.
}
$obj = new MyController();
if ($obj->scaffold !== false) {
echo "scaffold is "; var_dump($obj->scaffold);
} else {
echo "scaffold is "; var_dump($obj->scaffold);
}
?>
Java 言語だと型の定義などが含まれるのであまりきれいではありませんね。
public class MyController extends ParentController {
public Boolean scaffold;
public static void main(String [] args) {
MyController mc = new MyController();
System.out.println(mc.scaffold);
}
}
class ParentController {
public Boolean scaffold = Boolean.FALSE;
}
MySQL が無償で提供しているER 図作成ツールである MySQL Workbench でRBACを拡張したテーブル定義を書いてみました。通常のRBACだと役割とユーザを結びつけますが、「どこのドメイン」の「どのアプリケーション」が役割を持つのかという構造に拡張しました。

RBACを拡張したのテーブル定義
元々、認証・承認の一元管理についていろいろ調べていたのですが、LDAP で承認も含めて一元管理するのはいろいろ大変そうなので断念しました。RBAC についていろいろ調べてみて分かったことですが、RBACd という日本で作られたRBACデーモンがあるようです。RBAC の ER 図に関しては、Google で画像検索をすると参考になる図がいくつか見つかりました。(参考1, 参考2)
作成したWebアプリケーションの状態
開発時は、Tomcatにポート番号8080で接続して開発を行いました。アプリケーションの機能は簡単で、ゲストユーザは閲覧権限のみ持っていて、ログインを行うと管理者になったりできます。これはどこでも使われるWebアプリケーションの仕組みだと思います。Tomcat上ではセッションの保持が問題なく行われました。つまりログイン処理を行ってHttpSessionによるセッションオブジェクトを生成した後だと、ブラウザからのHTTPリクエストが行われても、sessionid で一意性を保持できた状態です。
Apache と Tomcat の連携と問題
その後、ある程度動作するようになったので、Apache 2.2 と Tomcat 5.5 を mod_proxy_ajp で連携しました。以下は、リバースプロキシの設定になります。1行目では、インターネットからhttp://www.oklab.org/devlink/ にリクエストが来たら、該当のリクエスト先の ajp://localhost:8009/OKLabDevLink/ にHTTPリクエストを送信します。2行目は、リダイレクトの対応のために記述しています。この設定だけだと、Apache 2.2 と Tomcat 5.5 は連携できるのですが、ブラウザのCookie に保存された JSESSIONID が常に変更される状態でした。
ProxyPass /devlink/ ajp://localhost:8009/OKLabDevLink/
ProxyPassReverse /devlink/ ajp://localhost:8009/OKLabDevLink/
ProxyPassReverseCookiePath による問題解決
いろいろ調べてみると Apache 2.2 の設定の ProxyPassReverseCookiePath ディレクティブで、Cookie の Path を変更できるので、元々の Path である /OKLabDevLink から /devlink に変更し対応できました。
ProxyPass /devlink/ ajp://localhost:8009/OKLabDevLink/
ProxyPassReverse /devlink/ ajp://localhost:8009/OKLabDevLink/
ProxyPassReverseCookiePath /OKLabDevLink /devlink
何故こうなるかは、もう少し調べてみないと分からないのが現状ですが、次の機会にしたいと思います。
その他
Firebug の拡張プラグインの、 Firecookie を使うと、ブラウザで cookie の状態を確認できるので便利でした。 あと Live HTTP Headers アドオンを追加すると 横取り丸などのプロキシも不要になりそうです。
HTMLには、数値文字参照 (Numeric character reference) と文字実体参照 (Character entity reference) があります。文字実体参照は、< ©などです。数値文字参照は日本語片仮名のの’ア’をアのように表現する方法です。例えばHTMLの文字コードをISO-8859-1にして、数値文字参照で文字を表記すれば、複数言語の混在が容易に行えます。
プログラムからHTTPリクエストでHTMLを取得した場合、以下の条件の場合は注意しなければいけない場合があります。つまり、文字コードとしてはISO-8859-1なのに、実際にはUnicodeの日本語という場合があるということです。
- HTTPのContent-Type には、charset オプションが付かない。
- HTML の meta要素のContent-TypeがISO-8859-1
- ただし、HTML内は、数値文字参照で記述されている。
変換方法は、それほど難しいことではなく、&#;が含まれるの場合は、10進法と判断し、&#x;が含まれる場合は、16進数と判断します。そしてその値をInteger.parseInt(数値文字参照, 進数)という形int型にします。そして結果のint型をchar型にキャストして文字として認識させれば、Java プログラム上で扱うことが可能になります。Java の char型は、2バイトです。それを踏まえると、Unicode表現の変換方式で実装する方法や、C言語のように、パフォーマンスを求める実装も可能なはずです。
今回、自作のプログラムで、DocBookで記述され日本語に翻訳されたHTMLを解析した際に、この問題を発見しました。最近は様々な文字コード変換ライブラリがあり、(juniversalchardet)などは優秀なようです。自らSocketでHTTP通信をしようとしない限りこういう問題には出会わないのかもしれませんが、よい勉強になりました。
package org.oklab.sandbox;
/**
* HTML の表記方法の数値文字参照(Numeric character reference)をJavaのjava.lang.Stringに変換します.
*
* @author satoshiokita
*
*/
public class ConvertNumericCharacterReference2String {
/**
* テストメイン.
* @param args
*/
public static void main(String[] args) {
/*
* テスト1. 数値文字参照で10進法
*/
// 「オープンソースソフトウェア」という文字列を数値文字参照で10進法記述.
String testNCRString10 = "オープンソースソフトウェア";
// 変換
String resultJavaString10 = convert(testNCRString10);
// 結果出力
System.out.println(resultJavaString10);
/*
* テスト2. 数値文字参照で16進法
*/
// 「あいうえお」という文字列を数値文字参照で16進法記述.
String testNCRString16 = "あいうえお";
// 変換
String resultJavaString16 = convert(testNCRString16);
// 結果出力
System.out.println(resultJavaString16);
}
/**
* HTML の表記方法の数値文字参照(Numeric character reference)をJavaのjava.lang.Stringに変換します.
* @param testNCRString 変換前の数値文字参照
* @return java.lang.String
*/
private static String convert(String srcNCRString) {
String [] ncrStringSplit = srcNCRString.split(";");
char []resultBuffer = new char[ncrStringSplit.length];
for (int i = 0; i < ncrStringSplit.length; i++) {
if (ncrStringSplit[i].toLowerCase().indexOf("&#x") != -1) {
resultBuffer[i] = (char) Integer.parseInt(ncrStringSplit[i].toLowerCase().replace("&#x", ""), 16);
} else {
resultBuffer[i] = (char) Integer.parseInt(ncrStringSplit[i].replace("&#", ""));
}
}
return new String(resultBuffer);
}
}
Javaの正規表現で POSIX 文字クラスを使い、句読文字、制御文字、空白文字を変換し無効化(サニタイジング・サニタイズ)するサンプルクラスです。 POSIX 文字クラスというのは、正規表現の定数です。
例えば、半角空白、タブ、改行コードなどを一つにまとめて、\p{Space} のように記述できます。これを使えば、空白文字はAに変換したいとした場合に簡単に記述できます。詳しくはサンプルソースコードを見ていただければ分かると思います。
また、POSIX 文字クラスだけでは、複雑な表現に対応できないため、Perl は独自拡張しているため非常にテキストの解析が得意な言語となっています。Java では、Jakarta ORO が強力な正規表現機能を持っていて、デファクト・スタンダードに近い存在になっています。
package org.oklab.sandbox;
import java.util.regex.Pattern;
/**
* Javaの正規表現で POSIX文字クラスを使い、
* 句読文字、制御文字、空白文字を変換し無効化(サニタイジング・サニタイズ)するサンプルクラスです。
*
* @author satoshiokita
* @since 2009/09/15
*
*/
public class SampleSanitize {
/**
* テストを実行する。
* @param args
*/
public static void main(String[] args) {
// 変換したい文字列.
String src = "h1llo .#s/ggg test.2test \t\n\f\r AAAAA!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ZZZ";
// 正規表現でサニタイジング
String buff = sanitize(src);
// テスト時にデバッグしやすいように、目盛りを表示.
System.out.println("12345678901234567890123456789012345678901234567890");
System.out.println("---------+---------+---------+---------+---------+");
// 結果出力。
System.out.println(buff);
}
/**
* 正規表現で、句読文字、制御文字、空白文字を無効化する。
* @param src 変換したい文字列
* @return 変換後の文字列
*/
private static String sanitize(String src) {
String replacedStr = null;
// 句読文字 !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ を空文字("")にして無効化.
Pattern pattern = Pattern.compile("\\p{Punct}");
replacedStr = pattern.matcher(src).replaceAll("");
// 制御文字: [\x00-\x1F\x7F]を空文字("")にして無効化.
pattern = Pattern.compile("\\p{Punct}");
replacedStr = pattern.matcher(replacedStr).replaceAll("");
// 空白文字: [ \t\n\x0B\f\r]を半角空白にする。
pattern = Pattern.compile("\\p{Space}");
replacedStr = pattern.matcher(replacedStr).replaceAll(" ");
return replacedStr;
}
}
Java で HTML を解析する場合はオープンソースでいろいろなライブラリがあります。 これらは強力で便利なのですが、件名のように特定の用途のみだけに使う場合は、解析ツールの学習コスト、運用後のライブラリ管理などでコストが高くなります。そこでちょっとしたHTMLの解析コードを実装してみました。解析部分は甘いところが多々ありますが、解析のとっかかりソースコードにはなると思います。
javaでは、HTTP接続用のライブラリとして、java.net.HTTPURLConnection クラスが存在するのでこれを使えば簡単にできます。しかし、以下コードでは、細かい制御や、プログラミング言語を変更した場合のコード変換を考慮してSocket接続をしています。
execute メソッド内の while ループが大元の繰り返し処理です。一般的な繰り返し処理と違うところは、title解析の parseTitle メソッドや、keyword解析の parseKeywords メソッド内に、ループするオブジェクトを渡していて、メソッド内で繰り返し処理を行っているところです。HTMLでは、title要素が1行とは限りません。そのためタグの終端まで読み込む必要があります。
また、あまり複雑にならない程度に正規表現を利用して、HTMLの要素解析を行っています。たとえば、HTMLでは以下を許容します。そのためif文で評価していません。
- <meta name=keyword …
- <meta name=’keyword’ …
- <meta name=”keyword” …
追記:2009/09/22 titleタグや、keyword属性が日本語の場合を考慮して、HTTPヘッダのContent-Typeをで、文字コードを取得し変換するようにしました。WordPressにコピーペーストした際に、<>などが変換されずに、ソースコードがおかしくなっていたのを修正しました。
/**
*
*/
package org.oklab.socket;
import static java.net.HttpURLConnection.HTTP_OK;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* SocketでHTTP接続を行い、HTTPステータス、HTMLのtitle要素、HTMLのmeta要素のkeyword属性を解析します。
*
* @author satoshiokita
*
*/
public class SocketConnectionSample {
/**
* @param args
*/
public static void main(String[] args) {
SocketConnectionSample scs = new SocketConnectionSample();
scs.execute();
}
/**
*
*/
private void execute() {
String host = "www.oklab.org";
int port = 80;
try {
Socket socket = new Socket(host, port);
BufferedReader sin = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
BufferedWriter sout = new BufferedWriter(new OutputStreamWriter(
socket.getOutputStream()));
// ソケットに書き込む。つまりHTTPリクエストを作る。flashで送信。
sout.write("GET / HTTP/1.1\r\n"
+ "Host: " + host + "\r\n"
+ "Connection: close\r\n"
+ "\r\n");
sout.flush();
// ソケットから読み出す。つまり応答を解析する。
long lineCount = 0;
String buff;
int httpStatus = HTTP_OK;
String title = "";
boolean alreadyParseTitle = false;
String keywords = "";
boolean alreadyParseKeywords = false;
String charset = "";
boolean alreadyParseContentType = false;
while ((buff = sin.readLine()) != null) {
lineCount++;
// HTTPヘッダー HTTPステータスの解析.
if (lineCount == 1) {
httpStatus = Integer.parseInt(buff.split(" ")[1]);
}
// HTTPヘッダー Content-Typeの解析.
if (!alreadyParseContentType && (buff.indexOf("Content-Type: text/html; charset=") != -1)) {
if (buff.indexOf("=") != -1) {
charset = buff.substring(buff.indexOf("=") + 1, buff.length()).trim();
}
alreadyParseContentType = true;
}
// HTML meta要素 keyword属性の解析.
if (!alreadyParseKeywords
&& ((buff.toLowerCase().indexOf("name=\"keyword\"") != -1)
|| (buff.toLowerCase().indexOf("name=keyword") != -1)
|| (buff.toLowerCase().indexOf("name='keyword'") != -1))) {
keywords = parseKeywords(sin, buff);
alreadyParseKeywords = true;
}
// HTML title要素の解析.
if (!alreadyParseTitle && (buff.toLowerCase().indexOf("<title>") != -1)) {
title = parseTitle(sin, buff);
alreadyParseTitle = true;
}
}
sout.close();
sin.close();
socket.close();
System.out.println("HTTP Status =" + httpStatus);
System.out.println("HTML title =" + new String(title.getBytes(), charset));
System.out.println("HTML meta keywords =" + new String(keywords.getBytes(), charset));
System.out.println("HTML meta charset =" + charset);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private String parseTitle(BufferedReader sin, String buff) {
String title = "";
StringBuilder titleBuff = new StringBuilder();
try {
boolean isParsingTitleNow = true;
do {
if (isParsingTitleNow) {
titleBuff.append(buff);
}
if (buff.toLowerCase().indexOf("</title>") != -1) {
isParsingTitleNow = false;
break;
}
} while ((buff = sin.readLine()) != null);
Pattern p = Pattern.compile("</?title>", Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(titleBuff);
title = m.replaceAll("").trim();
} catch (IOException e) {
title = "";
e.printStackTrace();
}
return title;
}
private String parseKeywords(BufferedReader sin, String buff) {
String keywords = "";
StringBuilder keywordsBuff = new StringBuilder();
try {
boolean isParsingKeywordsNow = true;
do {
if (isParsingKeywordsNow) {
keywordsBuff.append(buff);
}
if (isParsingKeywordsNow) {
if (buff.toLowerCase().indexOf(">") != -1) {
isParsingKeywordsNow = false;
break;
}
}
} while ((buff = sin.readLine()) != null);
// \p{Punct} は、句読文字を判定。
// \\p{Punct} の、先頭\は、文字列内なのでエスケープしている。
// \\p{Punct}?の、?は、数量が0以上現れるかどうか判断.
// 上記により、 name=keyword, name="keyword" name='keyword'を解析できる.
Pattern p = Pattern.compile(
"<meta.+name=\\p{Punct}?keyword\\p{Punct}?.+content=\\p{Punct}?",
Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(keywordsBuff);
keywords = m.replaceAll("");
// "/> や '>など閉じ括弧を削除.
p = Pattern.compile("\\p{Punct}?\\p{Space}*/>", Pattern.CASE_INSENSITIVE);
keywords = p.matcher(keywords).replaceAll("").trim();
} catch (IOException e) {
keywords = "";
e.printStackTrace();
}
return keywords;
}
}
個人でサーバを運営する場合、費用を考慮すると、グローバルIPアドレスを1つ取得して、名前ベースのバーチャルホストで複数ドメインの運用をするという場合が多いと思います。
ルータにヘアピンNAT (英語圏ではhairpin routing やhairpin NAT という言葉で会話がされていますが、まだ単語として定まっていない感じです。) の機能が無いと、名前ベースのバーチャルホストの運用時に「ルータの設定画面が表示されてしまう」問題が発生します。最近、BUFFALO の WZR-HP-G300NH を購入してテストしているのですが、この問題が発生しました。
対応方法として一番手っ取り早いのは、C:\WINDOWS\system32\drivers\etc\hosts ファイルにバーチャルホスト運用のホスト名を記述することです。
127.0.0.1 localhost
192.168.11.26 blog.oklab.org # バーチャルホスト
Eclipse 3.5 でJPAプロジェクトを新規に作成して、OracleXE に接続した時のpersistence.xml です。データソースを取得して設定したい場合は、<non-jta-data-source>、<jta-data-source>で設定が可能です。JavaEE アプリケーションサーバを使わない場合やJPAを試してみる場合は、persistence.xml にデータベース接続先を記述してしまったほうが効率が良さそうです。
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="OKLabJPASandbox" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>org.oklab.devlink.jpa.Operator</class>
<properties>
<property name="eclipselink.target-database" value="Oracle" />
<property name="eclipselink.logging.level" value="INFO" />
<property name="eclipselink.jdbc.driver" value="oracle.jdbc.OracleDriver" />
<property name="eclipselink.jdbc.url" value="jdbc:oracle:thin:@192.168.11.2:1521:XE" />
<property name="eclipselink.jdbc.password" value="xxPASSxx" />
<property name="eclipselink.jdbc.user" value="xxUSERxx" />
</properties>
</persistence-unit>
</persistence>