2013年11月9日土曜日

ArchLinux の open-vm-tools を使用して VMware Player の hgfs による共有設定

CentOS をゲスト OS にする場合、よく VMware Player の共有を行うので、ArchLinux でもやろうとして嵌った・・・

とりあえず、公式 Wiki  を参考に開始した。

まず、ArchLinux の場合、vmware-tools でなく open-vm-tools を使用するようなので、インストール。

# pacman -S open-vm-tools

  • /etc/systemd/system/mnt-hgfs.mount
  • /etc/systemd/system/mnt-hgfs.automount
をコピペで作成。

/mnt/hgfs を作成。
# mkdir -p /mnt/hgfs

起動設定後再起動。
# systemctl enable mnt-hgfs.automount
# shutdown -r now 

確認してみると、エラー・・・
# ls /mnt/hgfs
ls: cannot access /mnt/hgfs/: No such device

マウントを確認。
# mount
[snip]
systemd-1 on /mnt/hgfs type autofs (rw,relatime,fd=26,pgrp=1,timeout=300,minproto=5,maxproto=5,direct
autofs になっている。

サイトをよく読むと、
共有フォルダを動かすには vmhgfs ドライバがロードされていなければなりません。
とのこと。
# lsmod | grep vmhgfs
存在しない・・・

サイトの上のほうに、open-vm-tools-modules に vmhgfs が含まれている記述があった。
よく読むべきだ・・・

探してみると
# pacman -Ss open-vm-tools-modules
community/open-vm-tools-dkms 5:2013.09.16-3
    kernel modules for the open source implementation of VMware Tools
名前が違うものが表示されるが・・・
とりあえずインストール。
# pacman -S open-vm-tools-dkms
[snip]
>>> Enable building of open-vm-tools modules:
>>> $ dkms add open-vm-tools/2013.09.16
>>>
>>> Enabling vmxnet driver in /usr/lib/modprobe.d/open-vm-tools-modules.conf
>>> (this will disable pcnet32 driver)
>>>
>>> If vmxnet driver doesn't handle your NIC, you have to manually
>>> disable loading of pcnet32 driver
>>>

表示されたコマンドを叩いてみる。
# dkms add open-vm-tools/2013.09.16
make: *** vmxnet: No such file or directory.  Stop.
make: *** vsock: No such file or directory.  Stop.

Creating symlink /var/lib/dkms/open-vm-tools/2013.09.16/source ->
                 /usr/src/open-vm-tools-2013.09.16

DKMS: add completed.

ソースにシンボリックリンクが貼られただけ?
まさかと思い、とりあえず再起動してみる。
# shutdown -r now
確認。
# ls /mnt/hgfs
ls: cannot access /mnt/hgfs/: No such device
# mount
[snip]
systemd-1 on /mnt/hgfs type autofs (rw,relatime,fd=26,pgrp=1,timeout=300,minproto=5,maxproto=5,direct
# lsmod | grep vmhgfs
# find / -name '*vmhgfs*'
/usr/bin/mount.vmhgfs
/usr/src/open-vm-tools-2013.09.16/vmhgfs
/usr/src/open-vm-tools-2013.09.16/vmhgfs/vmhgfs_version.h
・・・ 
ドライバのインストールなんぞめったにやらんしどうすんだ・・・とりあえずコンパイルするか・・・
# cd /usr/src/open-vm-tools-2013.09.16/vmhgfs/
# make
Using standalone build system.
Makefile:146: Makefile.normal: No such file or directory
make: *** No rule to make target 'Makefile.normal'.  Stop.
エラー箇所の Makefile を見てみると、Makefile.kernel と Makefile.normal を読み込み分けているっぽい。
Makefile.kernel は存在するので、VM_KBUILD を設定。
# make VM_KBUILD=yes
Using kernel build system.
make -C /lib/modules/3.11.6-1-ARCH/build/include/.. SUBDIRS=$PWD SRCROOT=$PWD/. \
  MODULEBUILDDIR= modules
make[1]: *** /lib/modules/3.11.6-1-ARCH/build/include/..: No such file or directory.  Stop.
Makefile:120: recipe for target 'vmhgfs.ko' failed
make: *** [vmhgfs.ko] Error 2
カーネルヘッダーがなさそうなのでインストールして再度コンパイル。
# pacman -S linux-headers
# make VM_KBUILD=yes
[snip]
/usr/src/open-vm-tools-2013.09.16/vmhgfs/inode.c: In function ‘HgfsPermission’:
/usr/src/open-vm-tools-2013.09.16/vmhgfs/inode.c:1893:29: error: ‘struct dentry’ has no member named ‘d_count’
          int dcount = dentry->d_count;
                             ^
scripts/Makefile.build:308: recipe for target '/usr/src/open-vm-tools-2013.09.16/vmhgfs/inode.o' failed
make[2]: *** [/usr/src/open-vm-tools-2013.09.16/vmhgfs/inode.o] Error 1
Makefile:1225: recipe for target '_module_/usr/src/open-vm-tools-2013.09.16/vmhgfs' failed
make[1]: *** [_module_/usr/src/open-vm-tools-2013.09.16/vmhgfs] Error 2
make[1]: Leaving directory '/usr/src/linux-3.11.6-1-ARCH'
Makefile:120: recipe for target 'vmhgfs.ko' failed
make: *** [vmhgfs.ko] Error 2
ググってみるとパッチがあるようで、
# curl https://raw.github.com/rasa/vmware-tools-patches/master/patches/vmhgfs/vmhgfs-d_count-kernel-3.11-tools-9.6.0.patch | patch -p1
[snip]
patching file inode.c
# make VM_KBUILD=yes
[snip]
make[1]: Entering directory '/usr/src/open-vm-tools-2013.09.16/vmhgfs'
make[1]: 'postbuild' is up to date.
make[1]: Leaving directory '/usr/src/open-vm-tools-2013.09.16/vmhgfs'
cp -f vmhgfs.ko ./../vmhgfs.o
成功したようだ。
insmod とか modprobe だったよなと思い調べつつ適当に進めていく。
とりあえず、モジュールをコピー。
# cd /usr/lib/modules/3.11.6-1-ARCH/kernel/fs
# mkdir vmhgfs
# cat /usr/src/open-vm-tools-2013.09.16/vmhgfs/vmhgfs.ko | gzip > vmhgfs/vmhgfs.ko.gz
modules.dep に追記。
# cd ../../
echo kernel/fs/vmhgfs/vmhgfs.ko.gz: >> modules.dep
modules.dep.bin 再生成後読み込み
# depmod -a
# modprobe vmhgfs
起動時に読み込むように /etc/modules-load.d/open-vm-tools.conf 作成
# echo vmhgfs >> /etc/modules-load.d/open-vm-tools.conf

これで、/mnt/hgfs の下にマウントされた共有ディレクトリがみれるようになった。
が、ファイルを置いてみたりしているとファイルによっては途中までしか読み込まない・・・
使えないんだろうか・・・ 

2013年9月2日月曜日

Java でフルカラー画像をグレースケールに変換

Java でフルカラー Jpeg を 8bit グレースケールの PNG に変換した際に嵌った記録・・・

元となる画像(1024 x 768)は、
これから、8bit グレースケールの PNG を生成することとする。

共通処理として、

画像読み込み

public static BufferedImage loadImage(String filename) {
  try (FileInputStream in = new FileInputStream(filename)) {
    return ImageIO.read(in);
  }
}

画像出力

public static void outputImage(String filename, BufferedImage image) throws IOException {
  ImageWriter writer = ImageIO.getImageWritersByFormatName("png").next();
  writer.setOutput(ImageIO.createImageOutputStream(new File(filename)));
  writer.write(image);
  writer.dispose();
}
の 2 つを用意。

変換処理として、書き出し時に JPEGImageWriteParam の PNG 版のようなもので指定して変換できるかと、探したが見つからなかった。
com.sun.imageio.plugins.png 以下にいくつか PNG ようのクラスがあるようだ。
が、よく分からなかったので、タイプ指定で TYPE_BYTE_GRAY を指定した BufferedImage を新たに生成。
それにデータをコピーすることに。

変換処理は、2 パターン。

パターン1Graphics2D#drawImage() を使用

public static BufferedImage grayScale1(BufferedImage image) throws Exception {
  int w = image.getWidth(), h = image.getHeight();
  BufferedImage output = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY);
  Graphics2D g2d = output.createGraphics();
  g2d.drawImage(image, 0, 0, w, h, null);
  return output;
}

パターン 2BufferedImage#setData() を使用

public static BufferedImage grayScale2(BufferedImage image) throws Exception {
  int w = image.getWidth(), h = image.getHeight();
  BufferedImage output = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY);
  output.setData(image.getData());
  return output;
}

パターン1、パターン2 をそれぞれ呼び出し

try {
  BufferedImage org = loadImage("org.jpg");
  outputImage("output1.png", grayScale1(org));
  outputImage("output2.png", grayScale2(org));
} catch (Exception e) { e.printStackTrace(); } 

パターン1はいい感じ。
パターン2はデカッ
色が減った分、横に4倍引き伸ばされた感じ?画像データ配列の変換がおかしくなったようだ。

パターン1


パターン2



BufferedImage の setRGB() の説明を読むと、
デフォルトモデルがイメージ ColorModel と一致しない場合は色変換が行われます。
と書かれていたのでこちらを使うようにしてみた。


パターン 3BufferedImage#setRGB() を使用

public static BufferedImage grayScale3(BufferedImage image) throws Exception {
  int w = image.getWidth(), h = image.getHeight();
  BufferedImage output = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY);
  output.setRGB(0, 0, w, h, image.getRGB(0, 0, w, h, null, 0, w), 0, w);
  return output;
} 

パターン1にくらべかなり黒っぽくなった。

パターン3


今回は、パターン1がよさそうだ。

実は、実際嵌った時の画像では、パターン1の場合、元画像の白の部分がグレーがかってしまいパターン3で出力したほうがはっきりしたものになった。
元画像によって変えるのも大変だが・・・

Graphics2D の場合、パラメータを色々指定できるのでそのあたりをいじってみても変わるかもしれない。

2013年8月7日水曜日

Googlebot のクロール時の jsessionid

とあるシステムでアクセスログを見ていたら、Googlebot が jsessionid 付の URL でアクセスしていた・・・

初回アクセス時や、Cookie 未対応の端末の場合、jsessionid を付ける設定になっている。
セキュリティー上よろしくないのだが、ガラケー対応のサイトのため、
アプリケーションサーバ Tomcat の設定で止めることができない。

ログイン必須のページとそうでないページがあり、Googlebot がログインするわけでもないので、
無視することもできるが、SEO 的によろしくない気がする。何より気持ち悪い・・・
というわけで対応することに。

ググってみると、URL Rewrite Filter なるものがあるらしい。
mod_rewrite のフィルターバージョンとのこと。
mod_rewrite にリンク等を書き換える機能があったっけと思いつつみてみると、
<%= response.encodeURL("/world.jsp?country=usa&amp;city=nyc") %>

<c:url value="/world.jsp?country=${country}&amp;city=${city}" />

のように指定した URL が正規表現で指定した値に置換されるようだ。

SAStruts や mobylet を使用しているので、c:url はほとんど使っていないが、
フレームワークの内部的には、response.encodeURL を使っているだろうと思われるので問題はなさそう。

ただ、jsessionid を付加しておいて置換で消すというのは冗長な気がする・・・
ソースを読んだわけではないので実装がどうなっているかは分からないが・・・
最初から付加しなければいいはず。

Tomcat の HttpServletResponse.encodeURL は、
HttpServletRequest.isRequestedSessionIdFromCookie を見てた気がする、ということで、
Bot の UserAgent だった場合、HttpServletRequestWrapper を作成し、
isRequestedSessionIdFromCookie で常に true を返すことに。

public class BotFilter implements Filter {
  private static class BotRequesteWrapper extends HttpServletRequestWrapper {
    public CrawlerRequestWrapper(HttpServletRequest request) {
      super(request);
    }

    @Override
    public boolean isRequestedSessionIdFromCookie() {
      return true;
    }

    @Override
    public boolean isRequestedSessionIdFromURL() {
      return false;
    }
  }


  private static final Pattern PAT_BOT =
    Pattern.compile("Googlebot|Y!J|YahooSeeker|msnbot|bingbot");


  @Override
  public void init(FilterConfig filterConfig) throws ServletException {}

  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    throws IOException, ServletException {
    HttpServletRequest req = (HttpServletRequest)request);
    chain.doFilter(PAT_BOT.matcher(req.getHeader("User-Agent")).find() ? new BotRequestWrapper(req) : request, response);
  }

  @Override
  public void destroy() {}
}

Google, Yahoo, MSN, Bing 等の場合、isRequestedSessionIdFromCookie で常に true、
念のために、isRequestedSessionIdFromURL は 常に false を返す。
そして、フィルターを設定。
<filter>
  <filter-name>botFilter</filter-name>
  <filter-class>com.example.BotFilter</filter-class>
</filter>

<filter-mapping>
  <filter-name>botFilter</filter-name>
  <url-pattern>/*</url-pattern>
  <dispatcher>REQUEST</dispatcher>
  <dispatcher>FORWARD</dispatcher>
  <dispatcher>INCLUDE</dispatcher>
  <dispatcher>ERROR</dispatcher>
</filter-mapping> 
UserAgent を Bot のものにして、Cookie を削除後アクセスすると、ものの見事に
jsessionid が付加されている・・・

何故・・・と言う事で追ってみると、どうも ServletResponse 内に ServletRequest を保持しており、
 HttpServletRequestWrapper とは別物のよう・・・

仕方がないので、HttpServletResponseWrapper を作成し、直接 encodeURL を変更することに。
public class CrawlerFilter implements Filter {
  private static class BotResponseWrapper extends HttpServletResponseWrapper {
    public BotResponseWrapper(HttpServletResponse response) {
      super(response);
    }


    @Override
    public String encodeRedirectURL(String url) {
      return url;
    }

    @Override
    public String encodeURL(String url) {
      return url;
    }
  }


  private static final Pattern PAT_BOT =
    Pattern.compile("Googlebot|Y!J|YahooSeeker|msnbot|bingbot");


  @Override
  public void init(FilterConfig filterConfig) throws ServletException {}

  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    throws IOException, ServletException {
    chain.doFilter(request,
      PAT_BOT.matcher(((HttpServletRequest)request).getHeader("User-Agent")).matches() ?
        new BotResponseWrapper((HttpServletResponse)response) : response);
    }

    @Override
    public void destroy() {}
}
encodeUR(), encodeRedirectURL() ともに渡された URL を返すだけにする。

これで、jsessionid が付加されなくなった。

2013年1月17日木曜日

財布経過

財布購入から早一年。
購入した GANZO の財布がどうなったかとういことで。


基本、バッグか上着の内ポケットに入れて持ち歩いているが、角のほうが若干へたってきている。
落としたり、水をこぼしたりしてしまったので、内側外側共に細かい傷がいっぱいついている。内側には若干染みになっている部分も。

エイジング的にはありだが、コードバンの場合最初のきれいな状態を保ちたいという感じもしてしまう・・・

手入れは、GANZO のサイトに、「乳化性タイプ無色 成分はろう・油脂の栄養補給を目的としたクリーム」でアフターケアをと書かれてたので、M.モゥブレィシュークリームという靴用のクリーム購入。

これでたまに磨いている。
外側の折る背の部分が若干カサつく。折れる部分なので仕方ないんだろう。念入りクリームを塗っている。

硬さは 1 年前に比べて大分柔らかくなり、360度開くことも可能になった。
小銭部分はマチがないのであまり多いと取り出し難い。
カードも取り出しやすくなった。伸びてガバガバになっているわけでもなくいい感じ。
 さらに使い込んでいこうと思う。