情報系学部生日記

備忘録や勉強したことのまとめ

Canvas LMSのQuick Startを試す(Ubuntu/xenial)

公式ドキュメントが全然使えない

Canvas LMSのお試し環境を作ろうと思い、公式ドキュメントの手順で構築を試みた。しかしドキュメントが更新されていないのか、何度試しても途中で詰まってしまう。そもそも同じドキュメントでRubyのバージョンが異なっていたり、yarnのバージョン指定が無いなどの不備があるので、ドキュメントとしてあまり正確な内容ではないのだろう。(2017/12/24時点の話) github.com

今回の構築環境

あくまでもお試し環境を作るだけなので、VagrantUbuntu環境を構築する。 メモリ容量は4GBくらい確保しておけば良い。容量が足りないとビルド中にエラーになる。IPアドレスの設定値はご自由に。

Vagrantfile

# -*- mode: ruby -*-                                                                                               
# vi: set ft=ruby :                                                                                                
                                                                                                                   
# All Vagrant configuration is done below. The "2" in Vagrant.configure                                            
# configures the configuration version (we support older styles for                                                
# backwards compatibility). Please don't change it unless you know what                                            
# you're doing.                                                                                                    
Vagrant.configure("2") do |config|                                                                                 
  # The most common configuration options are documented and commented below.                                      
  # For a complete reference, please see the online documentation at                                               
  # https://docs.vagrantup.com.                                                                                    
                                                                                                                   
  # Every Vagrant development environment requires a box. You can search for                                       
  # boxes at https://vagrantcloud.com/search.                                                                      
  config.vm.box = "ubuntu/xenial64"                                                                                
                                                                                                                   
  # Disable automatic box update checking. If you disable this, then                                               
  # boxes will only be checked for updates when the user runs                                                      
  # `vagrant box outdated`. This is not recommended.                                                               
  # config.vm.box_check_update = false                                                                             
                                                                                                                   
  # Create a forwarded port mapping which allows access to a specific port                                         
  # within the machine from a port on the host machine. In the example below,                                      
  # accessing "localhost:8080" will access port 80 on the guest machine.                                           
  # NOTE: This will enable public access to the opened port                                                        
  # config.vm.network "forwarded_port", guest: 80, host: 8080                                                      
                                                                                                                   
  # Create a forwarded port mapping which allows access to a specific port                                         
  # within the machine from a port on the host machine and only allow access                                       
  # via 127.0.0.1 to disable public access                                                                         
  # config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1"                                
                                                                                                                   
  # Create a private network, which allows host-only access to the machine                                         
  # using a specific IP.                                                                                           
  config.vm.network "private_network", ip: "192.168.33.20"                                                         
                                                                                                                   
  # Create a public network, which generally matched to bridged network.                                           
  # Bridged networks make the machine appear as another physical device on                                         
  # your network.                                                                                                  
  # config.vm.network "public_network"                                                                             
                                                                                                                   
  # Share an additional folder to the guest VM. The first argument is                                              
  # the path on the host to the actual folder. The second argument is                                              
  # the path on the guest to mount the folder. And the optional third                                              
  # argument is a set of non-required options.                                                                     
  # config.vm.synced_folder "../data", "/vagrant_data"                                                             
                                                                                                                   
  # Provider-specific configuration so you can fine-tune various                                                   
  # backing providers for Vagrant. These expose provider-specific options.                                         
  # Example for VirtualBox:                                                                                        
  #                                                                                                                
  config.vm.provider "virtualbox" do |vb|                                                                          
  #   # Display the VirtualBox GUI when booting the machine                                                        
  #   vb.gui = true                                                                                                
  #                                                                                                                
  #   # Customize the amount of memory on the VM:                                                                  
    vb.memory = "4096"                                                                                             
  end                                                                                                              
  #                                                                                                                
  # View the documentation for the provider you are using for more                                                 
  # information on available options.                                                                              
                                                                                                                   
  # Enable provisioning with a shell script. Additional provisioners such as                                       
  # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the                                     
  # documentation for more information about their specific syntax and use.                                        
  # config.vm.provision "shell", inline: <<-SHELL                                                                  
  #   apt-get update                                                                                               
  #   apt-get install -y apache2                                                                                   
  # SHELL                                                                                                          
end                                                                                                                

ミドルウェアのインストール作業

CODES.shという構築スクリプトが用意されているが、これも何故か動かなかったので自分で一から構築する。

仮想マシンが起動したら、まずaptで必要なものを入れる。

curl -sL https://deb.nodesource.com/setup_6.x | sudo bash -

sudo add-apt-repository ppa:brightbox/ruby-ng

curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list

sudo apt-get update

sudo apt-get install ruby2.4 ruby2.4-dev postgresql nodejs libxmlsec1-dev \
zlib1g-dev libxml2-dev libsqlite3-dev libpq-dev curl make g++ redis-server

sudo apt-get install yarn=0.27.5-1

インストールしたRedisはとりあえず、裏で動かしておけば良い。

sudo redis-server &

postgresのインストール後に、postgres上にユーザを作成する。

sudo -u postgres createuser $USER
sudo -u postgres psql -c "alter user $USER with superuser" postgres

Rubyのインストール後に、Bundlerをインストールする。バージョンによっては後々面倒なことになるので、必ずバージョン指定すること。

sudo gem install bundler -v 1.14.6

Canvas LMSのソースコードを、Githubリポジトリからクローンする。

git clone https://github.com/instructure/canvas-lms.git canvas
cd canvas

Bundlerとnpmのインストール後に、Canvas LMSに必要なモジュールをインストールする。

bundle install 
npm install

npmでCoffeeScriptをインストールする。

sudo npm install -g coffee-script@1.6.2

設定ファイルのコピー

アプリケーションのサンプル設定ファイルを、使用する設定ファイルとしてコピーする。Quick Startでは特に編集しなくて良い。

for config in amazon_s3 delayed_jobs domain file_store outgoing_mail security external_migration; \
do cp -v config/$config.yml.example config/$config.yml; done

cp config/dynamic_settings.yml.example config/dynamic_settings.yml
cp config/database.yml.example config/database.yml

echo -e "development:\n  cache_store: redis_store" > config/cache_store.yml
echo -e "development:\n  servers:\n  - redis://localhost" > config/redis.yml

DBテーブルの作成

Canvas LMSで使用するテーブルを作成する。

createdb canvas_development

Canvas LMSのビルド

ようやくCanvas LMS自体のビルドを行っていく。

bundle exec rails canvas:compile_assets
bundle exec rails db:initial_setup

initial_setup後にエラーが出るが、めげずにもう一度同じコマンドを叩く。

bundle exec rails db:initial_setup

そうすると管理者のメールアドレスとパスワードを聞かれるので入力する。 この際に8500ポートに接続失敗したとか何とか言われるが、めげずにもう一度ログイン情報を入力するとインストールが完了する。

エラー内容

Problem creating administrative account, please try again: Connection refused - Connection refused - connect(2) for "localhost" port 8500 (localhost:8500)

ビルド時のエラーと管理者情報入力時のエラーが何故出力されるのかは不明で、issueも今のところ上がっていない。

Canvas LMSを起動

IPアドレスを明示的に指定しないと、ホストマシンからアクセスできないので注意すること。

bundle exec rails server -b 0.0.0.0

ControllerAdviceとResponseEntityExceptionHandlerを用いた例外ハンドラ

やること

Springで例外ハンドラを用意する方法は複数あるが、今回はResponseEntityExceptionHandlerを用いてアプリケーション内で共通の例外ハンドラを定義する。

ResponseEntityExceptionHandler (Spring Framework 5.0.0.RELEASE API)

環境

  • Spring Boot 1.5.7.RELEASE

方法

投げる例外の基底クラスを作る

Transactionalでロールバックさせるため、RuntimeExceptionを継承した例外クラスを作る。

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.HttpStatus;

abstract public class HttpStatusException extends RuntimeException {
    private String message;
    private HttpStatus httpStatus;

    HttpStatusException(HttpStatus httpStatus, String message) {
        this.httpStatus = httpStatus;
        this.message = message;
    }

    public HttpStatus getHttpStatus() {
        return this.httpStatus;
    }

    public String toResponseJson() throws JsonProcessingException {
        return new ObjectMapper().addMixIn(HttpStatusException.class, HttpStatusExceptionMixin.class).writeValueAsString(this);
    }

    @JsonAutoDetect(
        getterVisibility = JsonAutoDetect.Visibility.NONE,
        isGetterVisibility = JsonAutoDetect.Visibility.NONE,
        fieldVisibility = JsonAutoDetect.Visibility.NONE
    )
    abstract class HttpStatusExceptionMixin {
        @JsonProperty String message;
    }
}

例外エラーのフィールドには返すメッセージとHTTPステータスを用意し、返却するJSONを生成するメソッドを実装する。 JSONで返す値はmessageだけで良いので、Mixin用の抽象クラスをインナークラスとして定義しておく。

派生例外クラスを作る

とりあえずBadRequest用のクラスを作る。

import org.springframework.http.HttpStatus;

public class BadRequestException extends HttpStatusException{
    public BadRequestException(String message) {
        super(HttpStatus.BAD_REQUEST, message);
    }
}

ControllerAdviceを用いた例外ハンドラを作る

import com.fasterxml.jackson.core.JsonProcessingException;
import com.example.exceptions.HttpStatusException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

@ControllerAdvice
public class ApiAdviceController extends ResponseEntityExceptionHandler {
    @ExceptionHandler(HttpStatusException.class)
    public ResponseEntity handleHttpStatusException(HttpStatusException exception, WebRequest request) throws JsonProcessingException {
        return handleExceptionInternal(exception, exception.toResponseJson(), new HttpHeaders(), exception.getHttpStatus(), request);
    }
}

ResponseEntityExceptionHandlerを継承したクラスを作り、ControllerAdviceアノテーションを付ける。 ExceptionHandlerをメソッドに付け、HttpStatusExceptionを引っ掛けてやるようにする。 handleExceptionInternalの引数は以下のようになっている。

  1. 引っ掛けた例外オブジェクト
  2. レスポンスボディ
  3. レスポンスヘッダ
  4. レスポンスステータス
  5. カレントリクエス

まとめ

今回はBadRequestExceptionだけ作ったが、必要に応じて他のレスポンスステータスを持つ例外クラスを作ってthrowするだけで色々な例外に対応できる。 今回はコントローラ共通の例外ハンドラを作ったが、コントローラ特有のハンドラを作ればそちらが優先されるらしいので、 汎用的な例外ハンドラはControllerAdviceで実装し、特殊なレスポンスを返すなどの条件があればコントローラに個別定義すれば良い。

参考リンク

qiita.com

LaravelのHomesteadをvagrant upしようとしたらThe requested address is not valid in its context.吐いて止まった

最近、環境構築で手こずることが多いです…

環境

OS: Windows8.1
Vagrant: 1.9.3

現象

homesteadのboxを無事取り込み、vagrant upしたら以下のようなエラーを吐いて止まりました。

C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/util/is_port_open.rb:21:in `initialize': The requested address is not valid in its context. - connect(2) for "0.0.0.0" port 8000 (Errno::EADDRNOTAVAIL)
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/util/is_port_open.rb:21:in `new'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/util/is_port_open.rb:21:in `block in is_port_open?'
        from C:/HashiCorp/Vagrant/embedded/lib/ruby/2.2.0/timeout.rb:88:in `block in timeout'
        from C:/HashiCorp/Vagrant/embedded/lib/ruby/2.2.0/timeout.rb:32:in `block in catch'
        from C:/HashiCorp/Vagrant/embedded/lib/ruby/2.2.0/timeout.rb:32:in `catch'
        from C:/HashiCorp/Vagrant/embedded/lib/ruby/2.2.0/timeout.rb:32:in `catch'
        from C:/HashiCorp/Vagrant/embedded/lib/ruby/2.2.0/timeout.rb:103:in `timeout'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/util/is_port_open.rb:19:in `is_port_open?'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builtin/handle_forwarded_port_collisions.rb:248:in `port_check'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builtin/handle_forwarded_port_collisions.rb:121:in `[]'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builtin/handle_forwarded_port_collisions.rb:121:in `block in handle'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builtin/handle_forwarded_port_collisions.rb:257:in `block in with_forwarded_ports'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builtin/handle_forwarded_port_collisions.rb:253:in `each'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builtin/handle_forwarded_port_collisions.rb:253:in `with_forwarded_ports'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builtin/handle_forwarded_port_collisions.rb:98:in `handle'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builtin/handle_forwarded_port_collisions.rb:42:in `block in call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/environment.rb:567:in `lock'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builtin/handle_forwarded_port_collisions.rb:41:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/plugins/providers/virtualbox/action/prepare_forwarded_port_collision_params.rb:30:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builtin/env_set.rb:19:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builtin/provision.rb:80:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/plugins/providers/virtualbox/action/clear_forwarded_ports.rb:15:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/plugins/providers/virtualbox/action/set_name.rb:50:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/plugins/providers/virtualbox/action/clean_machine_folder.rb:17:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/plugins/providers/virtualbox/action/check_accessible.rb:18:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:95:in `block in finalize_action'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builder.rb:116:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/runner.rb:66:in `block in run'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/util/busy.rb:19:in `busy'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/runner.rb:66:in `run'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builtin/call.rb:53:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:95:in `block in finalize_action'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builder.rb:116:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/runner.rb:66:in `block in run'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/util/busy.rb:19:in `busy'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/runner.rb:66:in `run'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builtin/call.rb:53:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:95:in `block in finalize_action'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builder.rb:116:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/runner.rb:66:in `block in run'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/util/busy.rb:19:in `busy'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/runner.rb:66:in `run'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builtin/call.rb:53:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builtin/box_check_outdated.rb:78:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builtin/config_validate.rb:25:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/plugins/providers/virtualbox/action/check_virtualbox.rb:17:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:95:in `block in finalize_action'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/plugins/providers/virtualbox/action/match_mac_address.rb:19:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/plugins/providers/virtualbox/action/discard_state.rb:15:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/plugins/providers/virtualbox/action/import.rb:74:in `import'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/plugins/providers/virtualbox/action/import.rb:13:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/plugins/providers/virtualbox/action/prepare_clone_snapshot.rb:17:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builtin/prepare_clone.rb:15:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/plugins/providers/virtualbox/action/customize.rb:40:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/plugins/providers/virtualbox/action/check_accessible.rb:18:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:95:in `block in finalize_action'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builder.rb:116:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/runner.rb:66:in `block in run'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/util/busy.rb:19:in `busy'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/runner.rb:66:in `run'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builtin/call.rb:53:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builtin/config_validate.rb:25:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:95:in `block in finalize_action'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builtin/handle_box.rb:56:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:95:in `block in finalize_action'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builder.rb:116:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/runner.rb:66:in `block in run'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/util/busy.rb:19:in `busy'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/runner.rb:66:in `run'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builtin/call.rb:53:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/plugins/providers/virtualbox/action/check_virtualbox.rb:17:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/warden.rb:34:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/builder.rb:116:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/runner.rb:66:in `block in run'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/util/busy.rb:19:in `busy'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/action/runner.rb:66:in `run'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/machine.rb:225:in `action_raw'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/machine.rb:200:in `block in action'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/environment.rb:567:in `lock'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/machine.rb:186:in `call'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/machine.rb:186:in `action'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.3/lib/vagrant/batch_action.rb:82:in `block (2 levels) in run'

解決

ググるとStackOverflowにたどり着く。 stackoverflow.com vagrant 1.9.3で発生する問題らしいですね。
Homestead/scripts/homestead.rbをエディタで開き以下のように変更する。

        # 86行目くらい
        # Use Default Port Forwarding Unless Overridden
        unless settings.has_key?("default_ports") && settings["default_ports"] == false
            default_ports.each do |guest, host|
                unless settings["ports"].any? { |mapping| mapping["guest"] == guest }
            config.vm.network "forwarded_port", guest: guest, host: host, auto_correct: true, host_ip: "127.0.0.1"
                end
            end
        end

        # Add Custom Ports From Configuration
        if settings.has_key?("ports")
            settings["ports"].each do |port|
            config.vm.network "forwarded_port", guest: port["guest"], host: port["host"], protocol: port["protocol"], auto_correct: true, host_ip: "127.0.0.1"
            end
        end

VagrantでリモートのboxをaddしようとしたらSSL read: error:00000000:lib(0):func(0):reason(0)で止まる

環境

現象

boxのダウンロード中に以下のようなエラーが出て、ダウンロードが中断される。

An error occurred while downloading the remote file. The error
message, if any, is reproduced below. Please fix this error and try
again.

SSL read: error:00000000:lib(0):func(0):reason(0), errno 50

解決

errnoの部分は自分の環境では50か60だった気がする。 ググると以下のような記事が見つかった。 github.com slick.pl

とりあえずオプションを追加したら無事にダウンロードできました。

vagrant box add --insecure -c laravel/homestead

KotlinとThymeleafの組み合わせで、Thymeleaf側でisHogeフィールドが参照できない。

ThymeleafとKotlinの組み合わせで発生した問題です。 コントローラからビューにisHogeという値を渡した場合に、 ビュー側のThymeleaf内でその値を使用するときisHogeという変数名で参照できないというものです。 これはKotlinのフィールドの概念を理解していればそんなに難しい内容でないのですが、 Kotlinで書いたコードがビルド後にどうなるかを意識していない場合に結構引っかかるのではないかと思います。

KotlinでisHogeというフィールドを書きコンパイルするとHogeというメンバ変数に対し、各アクセサが自動的に生成されるので Thymeleaf側からだとisHogeという変数は存在せずHogeという変数が見えるということですね。

それにしてもキャメルケースで書いた変数がパスカルケースになってしまうのは気持ちが悪いですね。 文章だけでは分かりにくいのでサンプルコードを載せておきます。 モデルは割愛します。

package com.example

import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.servlet.ModelAndView

@Controller
@RequestMapping("/books")
@Suppress("unused")
class BooksController(
    val service: BookService
){
    @GetMapping("/add")
    fun add() = ModelAndView("books/add")
}
package com.example

import org.springframework.stereotype.Service

@Service
class BookService{
    fun getBookList() = arrayListOf<Book>().apply {
        add(Book(1, "初めてのKotlin", 3000, false))
        add(Book(2, "初めてのJava", 2800, true))
        add(Book(3, "初めてのIntellij", 4500, false))
    }
}
package com.example

class Book(
    var id: Long,
    var title: String,
    var price: Long,
    var isRemoved: Boolean = false
)
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8"/>
    <title>Book List</title>
</head>
<body>
    <table>
        <tr>
            <th>ID</th>
            <th>Title</th>
            <th>Price</th>
            <th>IsRemoved</th>
        </tr>
        <tr data-th-each="book : ${bookList}">
            <td data-th-text="${book.id}"></td>
            <td data-th-text="${book.title}"></td>
            <td data-th-text="${book.price}"></td>
            <td data-th-text="${book.Removed}"></td>
            <!--<td data-th-text="${book.isRemoved}"></td>  isRemovedが参照できずエラーになる-->
        </tr>
    </table>
</body>
</html>

Kotlinのリフレクションを使ってプロパティ情報を取得する

やってることは簡単な内容なのですが、日本語の情報があまりなかったのでメモ程度に記録しようかと。
あるクラスのKClassを取得しプロパティ名と中身を取り出してみるというものです。

import kotlin.reflect.full.memberProperties

fun main(args: Array<String>) {
    val myBook = Book(
        name = "Hello Kotlin",
        page = 120,
        isbn = "978-4865940398"
    )

    myBook::class.memberProperties.forEach {
        println("${it.name}: ${it.call(myBook)}")
    }
}

class Book(
    val name: String,
    val page: Long,
    val isbn: String
)

実行結果は以下の通り。

isbn: 978-4865940398
name: Hello Kotlin
page: 120

htpasswdでパスワードの設定を行う際に8文字までしか認識してくれない

設定したパスワードよりも短い文字列で認証が通る

 SquidでプロキシのBasic認証のパスワード設定をする際に、設定したパスワードより短い文字列でも認証が通ってしまう現象に見舞われた。自分で構築した環境ではないので、仮想マシンで再現環境を構築して色々試してみた。すると9文字以降は認証時に入力の有無に関わらず、認証が通ってしまうことに気がついた。

htpasswd -cb ./.htpasswd hoge password12345

上記のようにパスワードを設定した場合には、「password」以降の「12345」は認証時に入力してもしなくても認証が通ってしまう。

ドキュメントを読む

 そこでHtpasswdのドキュメント(http://httpd.apache.org/docs/2.2/programs/htpasswd.html)を読んでみると、dオプションのところに以下のような文章が書いてあった。

This is not supported by the httpd server on Windows and Netware and TPF. This algorithm limits the password length to 8 characters.

そもそも最大8文字までしか対応していなかった。どうやらcrypt関数というものを使う際に、8文字までしか対応していないらしい。警告くらい出してくれると嬉しいな…