アプレットのURL接続

  1. アプレットのURL接続
     アプレットは、どこでも誰でも実行できる環境が必要なため、実行には厳しいセキュリティの条件があります。アプレットからはPCのファイルの操作や、接続したサーバー以外のネットワーク接続は許されていません。
     そこで、アプレットからFORMタグに相当する情報を送り、それをサーバーのCGIで処理する方法が有効です。

  2. 目的と実行
     アプレットで何かゲームを行い、その結果をFORM形式でCGIに送ります。CGIでは、上位10名のデータをファイルに記録し、結果を送り返します。
     下のアプレットで、名前と値を入力し Sendボタンを押します。アプレットはこのデータをサーバーのCGIに送り、CGIが送り返す上位10名の結果を受け取り表示します。



     サーバーは上位10名の結果をファイルに保存していますから、アプレットを立ち上げ直しても、前に接続した結果に追加することができます。

  3. アプレットのフォーム送信とCGI
    1. HttpURLConnectionクラス
      指定したURLと接続するには、HttpURLConnectionクラスを利用します。
       <urlStr>に文字列で urlStr を指定します。これから、URLクラスの url を作成し、HttpURLConnectioクラスを作成します。

       myurl=<url>;
      URL url=new URL(myurl);
      HttpURLConnectio connection=(HttpURLConnection)url.openConnection();

      このクラスにはストリーム出力を行う、getOutputStream() メソッドが用意されており、これをPrintWriterに接続すると、cgi のデータを送ることができます。

    2. PrintWriterへの接続
      connectionから、次のようにPrintWriterを作成します。

       PrintWriter out=new PrintWriter(connection.getOutputStream());

      このoutに、FORMタグの形式でデータを送信すれば、CGIはHTMLのフォームタグでPOSTされたデータと同様にデータを受け取ることができます。この場合。値と名前を val とname のForm出力として送りますから
       val=<値>&name=<名前>
      として次のように送ることになります。
        out.print("val=");
        st1=textField1.getText();
        out.print(st1);
        out.print("&name=");
        st2=nameField.getText();
        out.print(st2);

    3. プロジェクト cgiconnect
       アプレットは、入力フィールドから ゲームの名前、プレーヤの名前と値を取り出し CGI に送ります。接続先の url は
       http://www.ccad.sccs.chukyo-u.ac.jp/cgi-bin/ranking/ranking.cgi;
      とします。実行前に、この位置に、cgi のプログラムを配置する必要があります。
       送信の返事(ベスト10)を受け取り、それを表示します。
      package jconnect;
      
      import java.awt.*;
      import java.awt.event.*;
      import java.applet.*;
      import java.io.*;
      import java.net.*;
      import java.util.*;
      
      
      /**
       * <p>タイトル: </p>
       * <p>説明: </p>
       * <p>著作権: Copyright (c) 2003</p>
       * <p>会社名: </p>
       * @author 未入力
       * @version 1.0
       */
      
      public class Applet1 extends Applet {
        private boolean isStandalone = false;
        PrintWriter out;
        private Button button1 = new Button();
        private TextField textField1 = new TextField();
        private TextField nameField = new TextField();
        private TextArea textArea1 = new TextArea();
        private Label message = new Label();
        TextField textFieldID = new TextField();
        Label label1 = new Label();
        Label label2 = new Label();
        //引数値の取得
        public String getParameter(String key, String def) {
          return isStandalone ? System.getProperty(key, def) :
            (getParameter(key) != null ? getParameter(key) : def);
        }
      
        //アプレットのビルド
        public Applet1() {
        }
        //アプレットの初期化
        public void init() {
      
          try {
            jbInit();
          }
          catch(Exception e) {
            e.printStackTrace();
          }
        }
      
        //コンポーネントの初期化
        private void jbInit() throws Exception {
          button1.setLabel("send");
          button1.setBounds(new Rectangle(28, 152, 67, 27));
          button1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(ActionEvent e) {
              button1_actionPerformed(e);
            }
          });
          this.setLayout(null);
          textField1.setText("3000");
          textField1.setBounds(new Rectangle(26, 115, 70, 24));
          nameField.setText("Akiko");
          nameField.setBounds(new Rectangle(25, 82, 65, 21));
          textArea1.setText("textArea1");
          textArea1.setBounds(new Rectangle(121, 17, 128, 194));
          message.setBackground(Color.white);
          message.setText("message");
          message.setBounds(new Rectangle(24, 189, 77, 23));
          textFieldID.setText("ga");
          textFieldID.setBounds(new Rectangle(30, 31, 56, 22));
          label1.setText("ゲーム名");
          label1.setBounds(new Rectangle(26, 9, 54, 19));
          label2.setText("名前、得点");
          label2.setBounds(new Rectangle(12, 58, 74, 20));
          this.add(textArea1, null);
          this.add(textFieldID, null);
          this.add(label1, null);
          this.add(message, null);
          this.add(button1, null);
          this.add(textField1, null);
          this.add(nameField, null);
          this.add(label2, null);
        }
        //アプレットの情報取得
        public String getAppletInfo() {
          return "アプレット情報";
        }
        //引数情報の取得
        public String[][] getParameterInfo() {
          return null;
        }
      
        void button1_actionPerformed(ActionEvent e) {
          String st0,st1,st2;
          String otxt="";
      
          try{
            String myurl="http://www.ccad.sccs.chukyo-u.ac.jp/";
            myurl+="cgi-bin/ranking/ranking.cgi";
            URL url=new URL(myurl);
            //appletと同じサーバーでないと接続できない
            //URL url=new URL("http://www.yahoo.co.jp/");
            HttpURLConnection connection=(HttpURLConnection)url.openConnection();
            //connection.setRequestMethod("PUT");
      
            connection.setDoOutput(true);
            if(connection.getDoOutput()) {
              System.out.println("connected:"+myurl);
              message.setText("connected");
            }
            else message.setText("connection failed");
      
            out=new PrintWriter(connection.getOutputStream());
            out.print("id=");
            st0=textFieldID.getText();
            out.print(st0);
      
            out.print("&val=");
            st1=textField1.getText();
            out.print(st1);
      
            out.print("&name=");
            st2=nameField.getText();
            out.print(st2);
      
            System.out.println("post:"+st0+":"+st1+":"+st2);
            out.close();
      
            // ソースの表示
            String str;
            BufferedReader urlIn;
            //次を実行しないと、cgiが実行されない!!!! cgiでprintしなくても同じ 
            //JDK1.3/1.4も無関係
            //しかし、javacでコンパイルすると、以下は無関係!!
            System.out.println("top10\n");
      
            urlIn = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            //この表示は不要
            while ((str = urlIn.readLine()) != null) {
              System.out.println(str);
              otxt += str+"\n";
            }
          }catch(IOException exception){
            exception.printStackTrace();
          }
          textArea1.setText(otxt);
        }
      
        void close_actionPerformed(ActionEvent e) {
          out.close();
        }
      }

  4. CGIによる受け取り

    1. CGI
       cgi が利用するファイルファイルには10位までの値と名前が記録されています。cgi は&get_FormData($postData) で連想配列form{ } にアプレットからの情報を受け取り ($pval,$pname) に保存します。
       cgi はファイルから上位10名のデータを読みとり、($fval,$fname)に分解して記録します。この値を出力配列 @temp にコピーしますが、$pval が $fval より大きくなったら、先に、($pval,$pname) を出力します。合計の出力数をlcountに記録し、10を超えたら、終了します。最後に、@tempの値をファイルに書き戻します。
       10位までのデータを保持するファイル名は rank.txt ファイルの前にゲームIDを付加します。ゲームIDが ga の場合、garank.txt のファイルに記録します。
       記録するファイルは予め作成しておく必要があります。ga,gb,gc のゲームIDが利用できます。

    2. ソース ranking.cgi
       
      #!/usr/bin/perl
      
      #use strict;
      use CGI::Carp qw(fatalsToBrowser);
      
      
      #定義
      
      {
      #
        #$ENV{'REQUEST_METHOD'}="GET";
        #$ENV{'QUERY_STRING'}="id=ga&name=kita&val=3000";
      
        $date=localtime;
        %form={};
        $pval="";$pname="";$id="";
       
        $mrg=1;
        $lcount=0;
        my $postData = &get_PostData;
        my $hash_ref = &get_FormData($postData);
        my %form = %$hash_ref;
      
      #FORMデータ取得
        #print msgfile "form.$postData\n";
        $id=$form{'id'};
        $pval=$form{'val'};
        $pname=$form{'name'};
        %temp={};
      
      #idのファイルを開く 
       open(msgr,$id."rank.txt");
      
       print "Content-Type: text/html\n\n";#ヘダ出力
       while(<msgr>){
          chomp;
         ($fval,$fname)=split(/:/);#名前と得点を分離
         if($fval<$pval && $mrg==1 ) {#ファイルの値と取得データを比較
           push(@temp,"$pval:$pname\n");#取得データを書き出す
           print "$pval:$pname\n";
           $mrg=0;$lcount++;
          }
          push(@temp,"$fval:$fname\n");#ファイルのデータをコピー
          print "$fval:$fname\n";
          $lcount++;
          if($lcount >= 10) {last;}#データ数10で終了
        }
      
        if (($mrg==1) && ($lcount<10)) {#ファイルのデータ数10以下の場合
          push(@temp,"$pval:$pname\n");
          print "$pval".':'."$pname\n";
        }
      
        close msgr;
        
        open(msgw,">".$id."rank.txt");#ファイルに書き戻す
        foreach $val (@temp){
          print msgw $val;
        }
        close msgw;
      
      }
        
      #データを受け取る
      sub get_PostData{
        my $data;
        # GETメソッドの場合
        if( $ENV{'REQUEST_METHOD'} eq "GET" ) {
          $data = $ENV{'QUERY_STRING'};
        # POSTメソッドの場合
        } else {
          read( STDIN, $data, $ENV{'CONTENT_LENGTH'});
              }
        return $data;
      }
      
      #FORMデータをデコードする
      sub uri_decode{
        my $str=shift;
        $str=~tr/+/ /;
        #$str=~s/%([0-9A-Fa-f][0-9A-Fa-f])/pack('H2',$1)eg;
        return $str;
      }
      
      #データを記録
      sub get_FormData{
        my $data=shift;
        my @pair=split(/&/,$data);
        my %hash;
        foreach(@pair){
          my($key,$value)=split(/=/);
         $hash{&uri_decode($key)}=&uri_decode($value);
        }
        return \%hash;
      }
      

  5. 設定
     
    1. アプレット側
       アプレット側では、cgiconnect.Applet1のクラスファイルと、このアプレットをタグで指定した、htmlファイルを用意します。接続するcgiのURLはアプレットの中で指定します。

    2. CGI側
      CGIを、アプレットと同じサーバーにおきます。アプレットのファイル ranking.cgi とデータファイル ranking.txt ファイルが必要です。ranking.cgiは第三者への実行属性、データファイルは読み書きの属性が必要です。配置するフォルダは、CGIが実行可能でなければなりません。
       また、ranking.cgi には、サーバーの Perl へのパスを設定する必要があります。