tokuhirom's blog

fluentd を手元で動かしてデバッグしたいとき

以下のようにして fluentd をインストール。

gem install fluentd --no-ri --no-rdoc

/etc/fluent/fluent.conf に以下のようにかく。

<source>
    type forward
</source>
<match *.**>
    type stdout
</match>

んで、以下のようにして起動。

fluentd --no-supervisor --verbose

起動後は、メッセージ確認可能。

Created: 2016-05-02 05:24:54 +0000
Updated: 2016-05-02 05:24:54 +0000

【解決済み】jackson 2.7 以後は @ConstructorProperties を参照するようになっているので、lombok を使っている場合に何も考えずにアップグレードすると死ぬ

Jackson 2.7 がリリースされていますが、このバージョンから @ConstructorProperties を参照するようになっています。

See

@java.beans.ConstructorProperties は、Java Beans の標準にある annotation です。 これにより、引数を持つコンストラクタを Jackson で利用できるようになります。

たとえば、以下のように、 immutable なオブジェクトを Jackson で扱えるようになるのです!

public class Point {
    @ConstructorProperties({"x", "y"})
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    private final int x, y;
}

そういうわけで、便利なんですが、ここに一つ落とし穴があります。

lombok の @AllArgsConstructor または @Value を利用していて、かつ @JsonProperty でフィールド名を変更している場合です。 つまり、以下のようなケース。

@AllArgsConstructor
@Value
public class Foo {
    @JsonProperty("y")
    private String x;
}

この場合、生成コードは以下のようなものになります。

public class Foo {
    @JsonProperty("y")
    private String x;

    public String getX() { return this.x; }

    @ConstructorProperties({"x"})
    public Foo(String x) { return this.x; }
}

で、こうなった時に、Jackson 氏は @ConstructorProperties@JsonProperty で割り当てられてる名前が違うやんけ! と文句を言ってくるわけです。

再現コードは以下のようになります。

import java.util.List;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;

import lombok.AllArgsConstructor;
import lombok.Data;

public class Boo {
    @Data
    @AllArgsConstructor
    public static class Foo {
        @JsonProperty("foo_id")
        private String fooId;
    }

    public static void main(String[] args) {
        ObjectMapper objectMapper = new ObjectMapper();
        JavaType javaType = objectMapper.getTypeFactory().constructType(Foo.class);
        BeanDescription desc = objectMapper.getSerializationConfig().introspect(javaType);
        List<BeanPropertyDefinition> properties = desc.findProperties();
        System.out.println(properties);
    }
}

で、この挙動を抑制するには、@ConstructorProperties の付与を停止すればよいです。

プロジェクトのルート・ディレクトリに lombok.config というファイルを配置します。 build.gradle と同じディレクトリにおけばOK.

このファイルに以下のように記述すれば、@ConstructorProperties の生成が抑制されるので問題なくなる。

lombok.anyConstructor.suppressConstructorProperties = true

そんな感じです。

【2016.09.08 追記】 最新版では問題発生しなくなっています。

Created: 2016-05-02 05:24:54 +0000
Updated: 2016-05-02 05:24:54 +0000

Spring の ConfigurationProperties を kotlin で使う

以下のように、open class にすれば良いらしい。kotlin のクラスはデフォルトで final 指定がつくので、open 指定しないとダメな様子。 @Configuration @ConfigurationProperties(prefix = "koblog") open class KoblogSettings { var filePath: String = "" }

var にして default 値入れないといけないので、負けた感じがすごい。

そのうち、immutable なクラスにも対応する日がくるのでしょう。たぶん。

Created: 2016-05-02 05:24:54 +0000
Updated: 2016-05-02 05:24:54 +0000

gradle で maven central にリリースできた。

http://tagomoris.hatenablog.com/entries/2016/02/16

こちらを参考にしてやったら簡単にできた。 tagomoirs++

// releng: gradle clean && gradle uploadArchives -Prelease

group 'me.geso'
version '1.0.0'

apply plugin: 'java'
apply plugin: "maven"
apply plugin: "signing"

sourceCompatibility = 1.8

//set build variables based on build type (release, continuous integration, development)
def isDevBuild
def isCiBuild
def isReleaseBuild
def sonatypeRepositoryUrl
if (hasProperty("release")) {
    isReleaseBuild = true
    sonatypeRepositoryUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
} else if (hasProperty("ci")) {
    isCiBuild = true
    version += "-SNAPSHOT"
    sonatypeRepositoryUrl = "https://oss.sonatype.org/content/repositories/snapshots/"
} else {
    isDevBuild = true
    version += "-SNAPSHOT"
}

repositories {
    mavenCentral()
}

task javadocJar(type: Jar) {
    classifier = 'javadoc'
    from "${buildDir}/javadoc"
}

task sourcesJar(type: Jar) {
    classifier = 'sources'
    from sourceSets.main.allSource
}

artifacts {
    archives javadocJar, sourcesJar
}

signing {
    required { isReleaseBuild }
    sign configurations.archives
}

uploadArchives {
    repositories {
        if (isDevBuild) {
            mavenLocal()
        }
        else {
            mavenDeployer {
                if(isReleaseBuild) {
                    beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
                }

                repository(url: sonatypeRepositoryUrl) {
                    authentication(userName: sonatypeUsername, password: sonatypePassword)
                }

                pom.project {
                    name 'paranoid_dns_resolver'
                    packaging 'jar'
                    description 'Paranoid DNS Resolver'
                    url 'https://github.com/tokuhirom/paranoid_dns_resolver'

                    scm {
                        url "scm:git@github.com:tokuhirom/paranoid_dns_resolver.git"
                        connection "scm:git@github.com:tokuhirom/paranoid_dns_resolver.git"
                        developerConnection "scm:git@github.com:tokuhirom/paranoid_dns_resolver.git"
                    }
                    licenses {
                        license {
                            name 'MIT'
                            url 'https://opensource.org/licenses/MIT'
                        }
                    }
                    developers {
                        developer {
                            id 'tokuhirom'
                            name 'Tokuhiro Matsuno'
                            email 'tokuhirom@gmail.com'
                        }
                    }
                }
            }
        }
    }
}

dependencies {
    compile 'org.apache.httpcomponents:httpclient:4.5.2'
    compile 'com.github.tomakehurst:wiremock:1.58'
    compile 'org.assertj:assertj-core:3.3.0'
    testCompile group: 'junit', name: 'junit', version: '4.11'
}

task wrapper(type: Wrapper) {
    gradleVersion = '2.11'
}
Created: 2016-05-02 05:24:54 +0000
Updated: 2016-05-02 05:24:54 +0000

Spring Boot とファイルアップロード

File upload のサンプルがこれ。 https://spring.io/guides/gs/uploading-files/

http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/autoconfigure/web/MultipartProperties.html このへんによると、

multipart.location specifies the directory where files will be stored. The default is "". A common value is to use the system's temporary directory, which can be obtained.
multipart.maxFileSize specifies the maximum size permitted for uploaded files. The default is 1Mb.
multipart.maxRequestSize specifies the maximum size allowed for multipart/form-data requests. The default is 10Mb
multipart.fileSizeThreshold specifies the size threshold after which files will be written to disk. Default is 0, which means that the file will be written to disk immediately.

とのこと。multipart.maxFileSize が 1Mb なのは、最近のでっかい画像を考えるとちょっと足りない。

なので、通常は、ファイルをアップロードさせるアプリケーションを開発するときには、明示する必要がある。

multipart.fileSizeThreshold は、なんか大きめの値を設定したくなりがちだが、通常はデフォルトの0で問題ないと思う。小さいファイルをたくさんアップさせるようなサービスの場合は別だが、そんなサービスそうそうない。

Created: 2016-05-02 05:24:54 +0000
Updated: 2016-05-02 05:24:54 +0000

Freemarker でテンプレート書くときに、Freemarker tag っぽいのを出力したい

Created: 2016-05-02 05:24:54 +0000
Updated: 2016-05-02 05:24:54 +0000

gradle が混乱したときにとにかく念入りにキャッシュ消したい時にうつコマンド

gradle を使っている時に、変なライブラリを依存に加えた結果、依存関係が混乱し、どうにもならなくなるときがある。 そんな時は設定を戻してから以下のように打てば、とにかくキャッシュは消えて、とりあえずなんとかなる気がする。

pkill -f gradle; rm -rf ~/.m2/repository/ ~/.gradle/caches/ .gradle; ./gradlew --recompile-scripts --refresh-dependencies --rerun-tasks clean check
Created: 2016-05-02 05:24:54 +0000
Updated: 2016-05-02 05:24:54 +0000

Java で method_missing したい。

Java で method_missing したいというのは、テストを書いているときによくあるケースである。 Mockito など利用すればいいのだが、その中身が気になってしまうのが、エンジニアの性であろう。

そういうわけで、method_missing のやり方を調べていると、Proxy を利用する例が多々みつかる。 しかしこの方法では満足な結果は得られない。なぜならば Proxy では interface に対するモッキングはできるがクラスにたいするモッキングが出来ないからだ。悲しい。

参考: http://www.javaworld.com/article/2076233/java-se/explore-the-dynamic-proxy-api.html

そういうわけで、mocking には cglib or javassist などのバイトコード生成系ライブラリを利用するのが良い。 http://stackoverflow.com/questions/3291637/alternatives-to-java-lang-reflect-proxy-for-creating-proxies-of-abstract-classes


なお mockito の

when(body).thenReturn()

のような記法で、body がキャプチャできるわけないしキモいなあと思っていたのだが、これは単に DSL としてかっこ良く見せてるだけで、実際には引数としてなにかに利用しているわけではないようだ。つまり以下のように書いても同じなのだが、読みやすくするためにああなっている、ということのようだ。

body; when().thenReturn()
Created: 2016-02-06 01:05:08 +0000
Updated: 2016-02-06 01:05:08 +0000

spring boot で、テスト時だけ読ませたい @Configuration があるとき

なんかよくわからんけど @ContextConfiguration 指定すると @SpringApplicationConfiguration が処理されなくなるのかな。よくわからんけど。

なので、@SpringApplicationConfiguration(classes = { MyApplication.class, MyConfig1.class, MyConfig2.class }) みたいにしておいて、MyConfig1.class を test.MyConfig1 など、component scan の対象外に追い出せば良い。

以上。

Created: 2016-02-06 01:05:08 +0000
Updated: 2016-02-06 01:05:08 +0000

spring の MockMVC で通信結果をダンプしたいとき

mockmvc.perform(get("/"))
    .andDo(log());

とかしとけばいい。

    .andDo(print());

のほうが、確実に出力されるのでいいかも(ロガーの設定によらず出力される)

Created: 2016-02-06 01:05:08 +0000
Updated: 2016-02-06 01:05:08 +0000

spring boot でテスト書いてたら 403 になって、csrf filter に引っかかってるぽくていらっとしたときの対策

@Before
public void setUp() throws Exception {
    mockMvc = MockMvcBuilders
            .webAppContextSetup(webApplicationContext)
            .apply(springSecurity())
            .build();
}

みたいな設定してるときに csrf filter かかってきてテストコードが 403 で弾かれてうざいわーって時

mockMvc.perform(post("/register")
              .with(csrf())
              .param("action", "signup"))
     .andExpect(status().isOk());

とかすりゃ、とりあえず csrf token 渡すようになるっぽい。

って stackoverflow に書いてあった。

http://stackoverflow.com/questions/21749781/why-i-received-an-error-403-with-mockmvc-and-junit

Created: 2016-02-06 01:05:08 +0000
Updated: 2016-02-06 01:05:08 +0000

Spring Boot で redirect: とかなってるやつを自分で拡張したい

通常の Spring Boot では redirect:/ とか書くと、Moved Temporaly になるけど Moved Permanently 返したいときがある。

そんなとき、以下のような答えがインターネッツを検索していると出てくる。

  • @ResponseStatus(value=HttpStatus.MOVED_PERMANENTLY) ってコントローラメソッドに付与するって方法がある
    • メソッド全体にかかってくるのがダサい。
  • httpServletReqeust.setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, 302) でやる
    • わかりづらい

そういうわけで、redirect301:/hoge/ とかでできるようにしたらいいんじゃないかと思いつく。 この似非 URL みたいなやつは ViewResolver ってやつがハンドリングしている。

ViewResolver は https://github.com/spring-projects/spring-framework/blob/554bf49/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java#L700 のへんで集められるが、要するに DI で ViewResolver をねじ込めば良い。

Spring Boot の場合、以下のようにすればよい。ViewResolver は複数指定できて、順番に呼び出される感じになっている。apache module のような呼びだされ方を想定しておけば良い。

@Slf4j
@Configuration
public class WebConfig implements ServletContextAware {
    @Bean
    public ViewResolver myViewResolver() {
        return (viewName, locale) -> {
            final String header = "redirect301:";
            if (viewName.startsWith(header)) {
                String redirectUrl = viewName.substring(header.length());
                RedirectView view = new RedirectView(redirectUrl, true, true);
                view.setStatusCode(HttpStatus.MOVED_PERMANENTLY);
                return view;
            } else {
                return null; // DECLINED
            }
        };
    }
}
Created: 2016-02-06 01:05:08 +0000
Updated: 2016-02-06 01:05:08 +0000

Harriet 0.05 released.

shipped.

Created: 2016-02-06 01:05:08 +0000
Updated: 2016-02-06 01:05:08 +0000

spring mvc の interceptor のパスマッチャのやつ

Spring mvc の interceptor ってやつ。hook みたいなやつ。以下のようなパスの設定ができる。 excludePathPatterns の設定だけだと動かないとかいう説もあって、注意が必要。

registry.addInterceptor(new ThemeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**");

http://docs.spring.io/spring/docs/4.2.4.RELEASE/spring-framework-reference/html/mvc.html#mvc-config-interceptors

ここのパターンは PathMatcher でマッチするのだが、AntPathMatcher が事実上唯一の実装。 https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/AntPathMatcher.html このマッチャで利用可能なメタキャラクタは以下のもののみ。

? matches one character
* matches zero or more characters
** matches zero or more directories in a path

インターセプターで、特定のパスのみ認証スキップとかやってる場合、気をつけないとエラー表示ページとかがそのまま interceptor かかってて謎のリダイレクトかかったりしてだるいです。

以上。

Created: 2016-02-06 01:05:08 +0000
Updated: 2016-02-06 01:05:08 +0000

spring boot で、freemarker のテンプレートの読み込みがサーバー再起動しないと読まれなくてだるいって時の対処方法

spring boot はデフォルトでは classpath から読んでるので、まあなんか再読み込みさせるのが難しい。eclipse なら自動でコンパイルされるのでまだマシだが、intellij だとなんか遅いしイライラする。

そこで、build.gradleに以下のように記述し、template の読み込みパスを変更した。

bootRun {
    systemProperty "spring.freemarker.templateLoaderPath", "file:src/main/resources/templates/,classpath:/templates/"
}

freemarker が適当に file:// から直接読んでくれるので、ファイル変更したらすぐ読み込まれて便利。なぜ application.properties or application.yml に書かないのかというと、application.yml 的なものは common module に入れておいて、web module, admin module などがそれに依存するみたいな構成にしたいからで、そのような構成になっている場合、common module に sub project のパスを直ガキできないからです。

まあ、そんな感じ。

Created: 2015-12-19 01:14:59 +0000
Updated: 2015-12-19 01:14:59 +0000

xfce4で、スタートメニューを開くコマンド

xfce4-popup-whiskermenu

Created: 2015-12-19 01:14:59 +0000
Updated: 2015-12-19 01:14:59 +0000

AssertJ 用の IntelliJ postfix completion plugin を書いた

AssertJ の assertThat() をpostfix completion で入力したいと常々思っていて、誰か書いてくれないかなと思っていたけど誰も書いてくれないし、IntelliJ のプラグインというものを書いてみたかったので書いてみた。

https://github.com/tokuhirom/assertj-postfix-plugin

↑レポジトリはこちら。

IntelliJ のプラグインの情報は少なくて、どうもどこが本家なのかよくわからなかった。 http://www.jetbrains.org/intellij/sdk/docs/index.html たぶんこの辺。だと思う。

とりあえず検索してみたところ IntelliJ IDEAのPostfix補完プラグインを作るという記事が見つかったので、「こりゃあ、真似すれば簡単に実装できそうだなー」と思ったのが運の尽き。

どーも、ここに書いてある情報だけでは実装は無理っぽい。。 なんか実行時例外出るし。。そもそも俺 intellij plugin の書き方が全くわかってないし。。

で、いろいろ調べてみたところ、IntelliJ のプラグインは gradle つかって開発するのが楽っぽい。

gradle-intellij-plugin ってので、簡単にビルドできる。./gradlew runIdea ってすると、gradle が intellij idea のコミュニティエディションをダウンロードしてきてそれを実行してくれる。

https://github.com/JetBrains/gradle-intellij-plugin

build.gradle は以下の様な感じ。


plugins {
    id "org.jetbrains.intellij" version "0.0.32"
}

apply plugin: 'org.jetbrains.intellij'
apply plugin: 'java'


sourceCompatibility = 1.8

intellij {
    pluginName 'AssertJ Postfix Plugin'
}

group 'me.geso.assertj_postfix_plugin'
version '0.0.1'

task wrapper(type: Wrapper) {
    gradleVersion = '2.6'
}

repositories {
    mavenCentral()
}

ディレクトリ構成は以下のようになっている。

src
└── main
    ├── java
    │   └── me
    │       └── geso
    │           └── assertj_postfix_plugin
    │               ├── AssertJPostfixTemplateProvider.java
    │               └── AssertJTemplate.java
    └── resources
        ├── META-INF
        │   └── plugin.xml
        └── postfixTemplates
            └── AssertJTemplate
                ├── after.java.template
                ├── before.java.template
                └── description.html

src/main/resources/postfixTemplates/ 以下にテンプレートのデータが入っている。これは実際になにかの役に立つわけではないが、入れておかないと例外があがって死ぬ。各テンプレートごとに after.java.template, before.java.template, description.html の3つのファイルが必要だ。 これらのテンプレートは、以下のような場所に表示される。表示されるだけなので、動作には関係ない。

META-INF/plugin.xml がプラグインのメタデータ。プラグインのIDやらなんやらの基本的な設定がここに書いてある。

codeInsight.template.postfixTemplateProvider のあたりが重要で、テンプレートプロバイダを設定している。あとは他のプラグインでも一緒。

<idea-plugin version="2">
    <id>me.geso.assertj_postfix_plugin</id>
    <name>AssertJ Postfix Completion Plugin</name>
    <version>0.0.1</version>
    <vendor email="tokuhirom+intellij@gmail.com" url="http://64p.org">tokuhirom</vendor>

    <description><![CDATA[
        This plugin adds postfix completion template for assertj.
    ]]></description>

    <change-notes><![CDATA[
        <ul>
        <li>0.0.1 - The first release.
        </ul>
    ]]>
    </change-notes>

    <!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html for description -->
    <idea-version since-build="141.0"/>

    <extensions defaultExtensionNs="com.intellij">
        <codeInsight.template.postfixTemplateProvider language="JAVA"
                                                      implementationClass="me.geso.assertj_postfix_plugin.AssertJPostfixTemplateProvider"/>
    </extensions>

    <actions>
    </actions>

</idea-plugin>

で、テンプレートの入り口は以下のとおり。テンプレートのセットを作って返しているだけ。

package me.geso.assertj_postfix_plugin;

import java.util.HashSet;
import java.util.Set;

import org.jetbrains.annotations.NotNull;

import com.intellij.codeInsight.template.postfix.templates.JavaPostfixTemplateProvider;
import com.intellij.codeInsight.template.postfix.templates.PostfixTemplate;

public class AssertJPostfixTemplateProvider extends JavaPostfixTemplateProvider {
    private final HashSet<PostfixTemplate> templates;

    public AssertJPostfixTemplateProvider() {
        templates = new HashSet<>();
        templates.add(new AssertJTemplate());
    }

    @NotNull
    @Override
    public Set<PostfixTemplate> getTemplates() {
        return templates;
    }
}

テンプレートの実装、以下の様な感じになる。StringBasedPostfixTemplate ってやつを継承することにより、なんかそれっぽいテンプレートを書くだけでよいという感じになっている。

package me.geso.assertj_postfix_plugin;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import com.intellij.codeInsight.template.Template;
import com.intellij.codeInsight.template.Template.Property;
import com.intellij.codeInsight.template.TemplateManager;
import com.intellij.codeInsight.template.postfix.templates.StringBasedPostfixTemplate;
import com.intellij.codeInsight.template.postfix.util.JavaPostfixTemplatesUtils;
import com.intellij.psi.PsiElement;

public class AssertJTemplate extends StringBasedPostfixTemplate {

    public AssertJTemplate() {
        super("assertThat", "assertThat(expr);",
             JavaPostfixTemplatesUtils.selectorAllExpressionsWithCurrentOffset(JavaPostfixTemplatesUtils.IS_NON_VOID));
    }

    @Override
    public Template createTemplate(TemplateManager templateManager, String s) {
        Template template = super.createTemplate(templateManager, s);
        template.setValue(Property.USE_STATIC_IMPORT_IF_POSSIBLE, true);
        return template;
    }

    @Nullable
    @Override
    public String getTemplateString(@NotNull PsiElement psiElement) {
        return  "org.assertj.core.api.Assertions.assertThat($expr$);$END$";
    }
}

デバッガで実行すれば、プラグインが読み込まれた状態の IDEA が起動するので、動作を確認すればOK。普通の java アプリと同様に、デバッガでステップ実行とかもできる。便利。

出来上がったら、 https://plugins.jetbrains.com/ で publish すれば OK.

./gradlew publishPlugin やらでアップロードできるが、とりあえずは普通に ./gradlew buildPlugin して、form からアップロードした。plugin.xml が含まれてないと言われてアップロード失敗したので、./gradlew jar した jar をアップした。二回目からは publishPlugin でやれる。と思う。

そういうわけで、アップしてみてある。ご利用ください。と言いたいところだけど、まだ承認待なのでした。 https://plugins.jetbrains.com/plugin/8093?pr=

Created: 2015-12-19 01:14:59 +0000
Updated: 2015-12-19 01:14:59 +0000

C 言語で qsort_r がポータブルじゃなくて困る時の対策

https://github.com/noporpoise/sort_r

qsort は、ソート対象の変数以外を渡すことができないため、マルチスレッドのプログラムでは取り扱いづらい。tls 使えばいいが、それもなあ、という感じな時は、qsort_r を使いたい。

しかし qsort_r は標準化されていないため、ポータビリティが必要なケースでは使いづらい。

そこで sortr だ。sortr は、glibc 2.8 or later または BSD 環境下では qsortr を使い、linux 環境下では qsorts を利用し、何もなければ自前のソートルーチンでソートしてくれるという便利な奴である。

License も public domain なので使いやすくて良い。

Created: 2015-12-19 01:14:59 +0000
Updated: 2015-12-19 01:14:59 +0000

Perl5 での Plack に当たる Crust をみんなで作った話

この記事は Perl 6 Advent Calendar 2015 の1日目です。

Perl 6 は、2015年のクリスマスにリリースされることになっており、我々としてはそれを待ち受ける必要があると考えました。

Perl6 がリリースされた暁には、いろいろ遊ぼうかなという気持ちをみなさんお持ちだと思います。 Perl6 には夢が詰まっており、様々な機能が含まれている夢の言語です。

そういった、夢の機能については今後、クリスマスまでの間に、語られていきますが、本稿では、現実的な話をします。


今年のクリスマスに Perl6 がリリースされるぞ、という宣言がでたわけですが、そうなってくると、Perl6 をクリスマスから早速遊びたいなと思うわけですよね。

そして、私は web engineer なので、 Perl6 が出たら早速 web application を書きたいと思うわけです。

しかし、そこで気づいてしまう。Perl6 には Plack のようなものが無いと!

Perl6 に Plack のようなものがないし、当時まともな httpd の実装が存在していなかったのです! (実際には存在はしていたのですが、multi thread 対応していないし、なぜか当時の MoarVM は listen の backlog が 1 にハードコードされているためにまったく実用的ではなかったのです。この listen のバックログがハードコードされている問題は現在では修正されています)

これではいけない。と気づくわけです。まともな httpd が必要だ。 とりあえず fork(2) があれば、listen backlog が 1 にハードコードされているというハードな環境下でもなんとかなるだろうと思って、まず NativeCall ( Perl6 から FFI で C 関数をコールする機能) を使って fork を呼ぶようにしてみました。これはなかなかうまく動作したのですが、Mac で動作しないという問題が発見されました。

なぜか。

MoarVM は内部で libuv を利用しており、あらゆるソケット処理に libuv を利用しています。 libuv は fork をサポートしていません。より具体的に言うと mac の kqueue は fork をサポートしてないので、完全に詰みました。

対策として、ソケットライブラリを全部 NativeCall で実装することも考えたのですが(実装もしたのですが)、完全に本質を見失っている感じがしてきたので、この方針は諦めることにしました。


そうこうしているうちに、IO::Socket::Async ベースならわりと動くっぽいのではないかという話になり、IO::Socket::Async ベースで実装する方針に転換しました。

何度か書き直したのですが、まあそこそこ動くようになりました。

そのそこそこ動く実装が HTTP::Server::Tiny です。これは Perl5 の有名な httpd 実装である kazuhooku 氏による Starlet を参考に実装されており、HTTP/1.1 に対応した高速なサーバーです。 (Perl6 でできる範囲としてはかなり高速になるように実装しています。期待したパフォーマンスが出ていなければ、それは、、)

https://github.com/tokuhirom/p6-HTTP-Server-Tiny/


そういうわけで、HTTP::Server::Tiny ができたので、あとは Plack 相当のレイヤーを実装しようということになりました。

そこで、下記12名の Perl Monger が集まり、Plack を Perl6 に移植するプロジェクトに乗り出しました。

  • fayland
  • hiroraba
  • kentaro
  • lestrrat
  • mattn
  • moznion
  • retupmoca
  • softmoth
  • sugyan
  • syohex
  • syoichikaji
  • tokuhirom

https://github.com/tokuhirom/p6-Crust

結果として、2015年12月1日時点でほぼ Plack の機能を実装しております。 例えばイカのように実行すれば、さくっと httpd が立ち上がります。めっちゃ便利。

crustup -e 'sub { 200, [], ["OK"] }'

具体的にはイカのものが完備されています。

  • Crust::App::Directory
  • Crust::App::File
  • Crust::App::URLMap
  • Crust::Builder
  • Crust::Handler::FastCGI
  • Crust::Handler::HTTP::Easy
  • Crust::Handler::HTTP::Server::Tiny
  • Crust::Headers
  • Crust::Middleware::AccessLog
  • Crust::Middleware::Auth::Basic
  • Crust::Middleware::Conditional
  • Crust::Middleware::ContentLength
  • Crust::Middleware::ErrorDocument
  • Crust::Middleware::Lint
  • Crust::Middleware
  • Crust::Middleware::ReverseProxy
  • Crust::Middleware::Runtime
  • Crust::Middleware::StackTrace
  • Crust::Middleware::Static
  • Crust::Middleware::XFramework
  • Crust::MIME
  • Crust
  • Crust::Request
  • Crust::Request::Upload
  • Crust::Response
  • Crust::Runner
  • Crust::Test::MockHTTP
  • Crust::Test
  • HTTP::Message::PSGI


そんなわけで、ウェブアプリケーションをかく準備は整っています。

あとは Perl6 のリリースを待つだけや!!!!

明日は karupanerura さんの 無限リストであそぶ です。

http://qiita.com/advent-calendar/2015/perl6

Created: 2015-12-19 01:14:59 +0000
Updated: 2015-12-19 01:14:59 +0000

MoarVM における DESTROY メソッドの呼び出し手順

refcnt 型のインタープリタの場合デストラクタの呼び出しは極めて容易だが、GC の場合はちょっと面倒だ。

Rakudo の場合は、src/Perl6/Metamodel/Finalization.nqp にコードがある。

以下のように、DESTROY メソッドを持っているクラスを MoarVM に settypefinalize している。 ``` role Perl6::Metamodel::Finalization { has @!destroyers;

method setup_finalization($obj) {
    my @mro   := self.mro($obj);
    my int $i := nqp::elems(@mro);
    my @destroyers;
    while --$i >= 0 {
        my $class   := @mro[$i];
        my $destroy := $class.HOW.find_method($class, 'DESTROY', :no_fallback(1));
        if !nqp::isnull($destroy) && $destroy {
            nqp::push(@destroyers, $destroy);
        }
    }
    @!destroyers := @destroyers;
    if @destroyers {
        nqp::settypefinalize($obj, 1);
    }
}

method destroyers($obj) {
    @!destroyers
}

} ```

settypefinalize の処理は src/core/interp.c の以下の部分で処理される。

            OP(settypefinalize):
                MVM_gc_finalize_set(tc, GET_REG(cur_op, 0).o, GET_REG(cur_op, 2).i64);
                cur_op += 4;
                goto NEXT;

MVMgcfinalizeset により、クラスに MVMFINALIZETYPE フラグが付与される。 このフラグが付与されているとオブジェクトの新規作成を行う MVMgcallocateobject の中で、MVM_gc_finalize_add_to_queue(tc, obj); が呼ばれ、ファイナライズキューに詰められる。

というわけで、ファイナライズ処理が必要になっているものは別枠で処理される、ということでした。

Created: 2015-12-19 01:14:59 +0000
Updated: 2015-12-19 01:14:59 +0000

cmake で変数を全部ダンプする

cmake で現在利用可能な変数の名前とその値をすべてダンプしたいということがあると思う。そんな時にはこうしたらいい。

get_cmake_property(_variableNames VARIABLES)
foreach (_variableName ${_variableNames})
    message(STATUS "${_variableName}=${${_variableName}}")
endforeach()

http://stackoverflow.com/questions/9298278/cmake-print-out-all-accessible-variables-in-a-script

Created: 2015-12-19 01:14:59 +0000
Updated: 2015-12-19 01:14:59 +0000
Next page