Uncatchable ChuckNorrisException


596

Java'da bir varsayımı java.lang.ChuckNorrisExceptionerişilemez kılacak bir kod snippet'i oluşturmak mümkün müdür?

Akla gelen düşünceler, örneğin önleyici veya en boy yönelimli programlama kullanıyor .



2
sağlanan @jschoen bağlantıdan öneri kullanarak (devre dışı bayt kod doğrulayıcı) sen yapabilirsiniz Throwable kapsamaz şey atmak! aşağıdaki cevabımda açıklanmıştır.
jtahlborn

4
Aioobe'un cevabından yapılan bu alıntı @jschoen oldukça iyi bir şekilde bağlanmış sorusunu özetler: "Yani, sorunuz 'JVM şartnameden saparsa, ilkel atma gibi garip şeyler yapabilir mi ve cevap elbette, Evet."
Dan Is Fiddling By Firelight

2
@Max - Bunun için pratik kullanımları biraz açıklayabilir misiniz?
Vineet Bhatia

3
kendini tekrar eden bir istisnaya ne dersin finalize()?
Yalan Ryan

Yanıtlar:


314

Eğer bilmiyorum bu yüzden, bu denemedim JVM böyle bir şey kısıtlayacak, ama belki atar kodunu derlemek olabilir ChuckNorrisException, ama zamanında bir sınıf tanımını sağlamak ChuckNorrisExceptionhangi Throwable kapsamaz .

GÜNCELLEME:

Çalışmıyor. Doğrulayıcı hatası oluşturur:

Exception in thread "main" java.lang.VerifyError: (class: TestThrow, method: ma\
in signature: ([Ljava/lang/String;)V) Can only throw Throwable objects
Could not find the main class: TestThrow.  Program will exit.

GÜNCELLEME 2:

Aslında, bayt kodu doğrulayıcıyı devre dışı bırakırsanız bunu çalıştırabilirsiniz! ( -Xverify:none)

GÜNCELLEME 3:

Evden takip edenler için tam komut dosyası:

Aşağıdaki sınıfları oluşturun:

public class ChuckNorrisException
    extends RuntimeException // <- Comment out this line on second compilation
{
    public ChuckNorrisException() { }
}

public class TestVillain {
    public static void main(String[] args) {
        try {
            throw new ChuckNorrisException();
        }
        catch(Throwable t) {
            System.out.println("Gotcha!");
        }
        finally {
            System.out.println("The end.");
        }
    }
}

Sınıfları derleyin:

javac -cp . TestVillain.java ChuckNorrisException.java

Çalıştırmak:

java -cp . TestVillain
Gotcha!
The end.

"RuntimeException'ı genişletir" ve yalnızca yeniden derleyinChuckNorrisException.java :

javac -cp . ChuckNorrisException.java

Çalıştırmak:

java -cp . TestVillain
Exception in thread "main" java.lang.VerifyError: (class: TestVillain, method: main signature: ([Ljava/lang/String;)V) Can only throw Throwable objects
Could not find the main class: TestVillain.  Program will exit.

Doğrulama olmadan çalıştır:

java -Xverify:none -cp . TestVillain
The end.
Exception in thread "main"

18
Tamam, ya peki Objectyerine onu yakalarsan Throwable? (Derleyici buna izin vermez, ancak doğrulayıcıyı zaten devre dışı bıraktığımız için, belki bayt kodunu bunu yapmak için hacklenebilir.)
Ilmari Karonen

11
Java'da ne atabileceğinize göre , hala atılabilirliği genişletmeyen şeyleri yakalayabilirsiniz, ancak onları atmak ve yakalamak tanımlanmamış bir davranıştır.
VolatileDream

8
@dzieciou Birlikte doğru olabilirler. İşlemci türünüzde işletim sisteminizin belirli bir sürümünde Java ortamının sürümünü kullanarak yakalayabilirsiniz. Ancak, standartta yakalanıp yakalanamayacağı belirtilmezse, tanımsız davranış olarak adlandırılır, çünkü Java'nın diğer uygulamaları onu yakalanamaz hale getirebilir.
heinrich5991

2
Hmmph. 176 upvotes için, istisnanızı (elbette ctor tarafından çağırılır) yeniden çağırmak için tüm çağrı yığınını yamanlayan bazı JNI kodu yazacağınızı umuyordum.
kdgregory

3
Tüm bunları yaparken, bir bacağın üzerinde durmak, kafanızı patlatmak ve dixie ıslık yaparken karnınızı ovalamak da harika bir fikir ...;);)
Glen Best

120

Bunu düşündükten sonra başarıyla yakalanamayan bir istisna oluşturdum. JulesWinnfieldBununla birlikte, Chuck yerine ad vermeyi seçtim , çünkü bu bir mantar-bulut-döşeme-anne-istisnası. Dahası, tam olarak aklınızdakiler olmayabilir, ama kesinlikle yakalanamaz. Gözlemek:

public static class JulesWinnfield extends Exception
{
    JulesWinnfield()
    {
        System.err.println("Say 'What' again! I dare you! I double dare you!");
        System.exit(25-17); // And you shall know I am the LORD
    }
}


public static void main(String[] args)
{       
    try
    {
        throw new JulesWinnfield();
    } 
    catch(JulesWinnfield jw)
    {
        System.out.println("There's a word for that Jules - a bum");
    }
}

Et voila! Yakalanmamış istisna.

Çıktı:

Çalıştırmak:

Tekrar 'Ne' deyin! Sana meydan okuyorum! Sana iki kez cüret ediyorum!

Java Sonucu: 8

BAŞARILI OLUŞTUR (toplam süre: 0 saniye)

Biraz daha zamanım olduğunda, başka bir şey daha bulup bulamayacağımı göreceğim.

Ayrıca, şuna da göz atın:

public static class JulesWinnfield extends Exception
{
    JulesWinnfield() throws JulesWinnfield, VincentVega
    {
        throw new VincentVega();
    }
}

public static class VincentVega extends Exception
{
    VincentVega() throws JulesWinnfield, VincentVega
    {
        throw new JulesWinnfield();
    }
}


public static void main(String[] args) throws VincentVega
{

    try
    {
        throw new JulesWinnfield();
    }
    catch(JulesWinnfield jw)
    {

    }
    catch(VincentVega vv)
    {

    }
}

Yığın taşmasına neden olur - yine istisnalar yakalanmaz.


32
Cevabınızda Yığın Taşması kullanmak için +1. Şaka yapıyorum, gerçekten iyi bir cevap.
Josiah

7
Uygun bir "yakalanamayan istisna", tüm blokları çevreleyen herhangi bir müdahalenin yakalanmadan çalışmasını sağlayacaktır. Sistemi öldürmek bir istisna oluşturmaz - sadece sistemi öldürür.
supercat

4
Nasıl "fırlatıyorsunuz" JulesWinfield? Sistem atılmadan önce çığlık atmayacak mı?
supercat

6
@mikeTheLiar: Sistem kurucu sırasında çıkar, değil mi? İfade throw new Whatever()gerçekten iki Whatever it = new Whatever(); throw it;kısımdır : ve sistem ikinci kısma ulaşmadan ölür.
supercat

5
Size @mikeTheLiar aslında olabilir bunu atmak yönetiyorsanız ... oldukça kolay Jules veya Vincent yakalamak. Atamadığınız bir istisna oluşturmak kolaydır:class cn extends exception{private cn(){}}
John Dvorak

85

Böyle bir istisna dışında, kurucudan bir kullanmak zorunludur, System.exit(Integer.MIN_VALUE);çünkü böyle bir istisna fırlatırsanız ne olur;)


32
+ 1; IMO mümkün olan tek çözümdür. Programın sonlandırılması gereken bir istisna dışında ...
ana sayfa

7
Hayır, böyle bir istisna attığınızda ne olacağı olmaz. Yakalanmayan bir istisna tek bir iş parçacığını sonlandıracak, jvm'den çıkmayacak, bazı bağlamlarda System.exit bile bir SecurityException'a neden olacak - her kod parçasının bir programı kapatmasına izin verilmiyor.
josefx

3
Bunun while(true){}yerine kullanabilirsiniz System.exit().
Piotr Praszmo

2
Aslında, sen yapabilirsiniz önlemek System.exit()onu izin vermeyen bir güvenlik yöneticisi yükleyerek çalışmaktan. yapıcıyı yakalanabilecek farklı bir istisnaya (SecurityException) dönüştürür.
jtahlborn

5
Umm, teknik olarak asla attı istisna. Henüz atmak için nesneyi bile inşa etmediniz!
Thomas Eding

46

Herhangi bir kod Throwable yakalayabilir. Yani hayır, yarattığınız istisna ne olursa olsun Throwable'ın bir alt sınıfı olacak ve yakalanmaya tabi olacak.


11
Atılabil olur hangyakalamak ChuckNorrisException amacıyla kendisi: P
PermGenError

35
public class ChuckNorrisException extends Exception {
    public ChuckNorrisException() {
        System.exit(1);
    }
}

( Teknik olarak, bu istisna asla gerçekte atılmaz, ancak uygun bir şekilde ChuckNorrisExceptionatılamaz - önce sizi atar.)


4
Bir meslektaşım, 'System.exit (1)' çağrısının bir Güvenlik İstisnası atabileceğini düşündüğü için '; (;;) {}' 'i yapıştırmayı önermişti. Bunu yaratıcılık için oy kullanıyorum!
Phil Street

Cevabınızın sonuna katılıyorum. Asla ChuckNorris ile uğraşma, İstisna ya da değil.
Benj

28

Attığınız herhangi bir istisna Throwable'ı genişletmelidir, böylece her zaman yakalanabilir. Yani cevap hayır.

Size zor işlemek için yapmak istiyorsanız, yöntemleri geçersiz kılabilir getCause(), getMessage(), getStackTrace(), toString()başka atmak java.lang.ChuckNorrisException.


2
Hmm, catch (Throwable t) herhangi bir yöntem çağırıyor mu yoksa nesneyi başka türlü mutasyona uğratıyor? Bir yakalama maddesinin imkansız hale getirilmesi için bir istisna daha atmasına neden olabilir.
Colton

1
Bence catch(Throwable t)sadece değişkene depolar, bu yüzden önerilerim sadece kullanıcı istisna ile başa çıkmak istediğinde sonraki blokta geçerlidir
mirelon

24

Cevabım @ jtahlborn'un fikrine dayanıyor, ancak bir JAR dosyasına paketlenebilen ve hatta bir web uygulamasının parçası olarak favori uygulama sunucunuza dağıtılabilen tamamen çalışan bir Java programı .

Her şeyden önce, ChuckNorrisExceptionsınıfı tanımlayalım, böylece JVM'yi en başından çökmeyecek (Chuck gerçekten JVM'lerin çökmesini seviyor BTW :)

package chuck;

import java.io.PrintStream;
import java.io.PrintWriter;

public class ChuckNorrisException extends Exception {

    public ChuckNorrisException() {
    }

    @Override
    public Throwable getCause() {
        return null;
    }

    @Override
    public String getMessage() {
        return toString();
    }

    @Override
    public void printStackTrace(PrintWriter s) {
        super.printStackTrace(s);
    }

    @Override
    public void printStackTrace(PrintStream s) {
        super.printStackTrace(s);
    }
}

Şimdi Expendablesonu inşa etmek için sınıfa gidiyor :

package chuck;

import javassist.*;

public class Expendables {

    private static Class clz;

    public static ChuckNorrisException getChuck() {
        try {
            if (clz == null) {
                ClassPool pool = ClassPool.getDefault();
                CtClass cc = pool.get("chuck.ChuckNorrisException");
                cc.setSuperclass(pool.get("java.lang.Object"));
                clz = cc.toClass();
            }
            return (ChuckNorrisException)clz.newInstance();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }
}

Ve sonunda Mainsınıf bir popoyu tekmelemek için:

package chuck;

public class Main {

    public void roundhouseKick() throws Exception {
        throw Expendables.getChuck();
    }

    public void foo() {
        try {
            roundhouseKick();
        } catch (Throwable ex) {
            System.out.println("Caught " + ex.toString());
        }
    }

    public static void main(String[] args) {
        try {
            System.out.println("before");
            new Main().foo();
            System.out.println("after");
        } finally {
            System.out.println("finally");
        }
    }
}

Derleyin ve aşağıdaki komutla çalıştırın:

java -Xverify:none -cp .:<path_to_javassist-3.9.0.GA.jar> chuck.Main

Aşağıdaki çıktıyı alacaksınız:

before
finally

Sürpriz değil - sonuçta bir cezaevi tekme :)


çok hoş! sınıf tanımı manipülasyonu ile çok fazla şey yapmadım. hala komut satırında "verify: none" gerekli mi?
jtahlborn

@jtahlborn Evet, Throwable'ın soyundan gelmeyen bir nesneyi "doğrula: hiçbiri" olmadan atmaya çalışın.
Wildfire

oh, bunun bir şekilde bu kısıtlamanın etrafında olduğu izlenimini edindim. peki bu cevabımdan nasıl farklı?
jtahlborn

2
Temel fark, derleme zamanı hacklemeden java kodu çalışmasıdır
Wildfire

15

Yapıcıda tekrar tekrar çağıran bir iş parçacığı başlatabilirsiniz originalThread.stop (ChuckNorisException.this)

İplik istisnayı tekrar tekrar yakalayabilir, ancak ölene kadar atmaya devam eder.


13

Hayır. Java'daki tüm istisnalar alt sınıf olmalıdır java.lang.Throwableve iyi bir uygulama olmamasına rağmen, aşağıdaki gibi her türlü istisnayı yakalayabilirsiniz:

try {
    //Stuff
} catch ( Throwable T ){
    //Doesn't matter what it was, I caught it.
}

Daha fazla bilgi için java.lang.Throwable belgelerine bakın .

İşaretli istisnalardan (açıkça işlenmesi gereken) kaçınmaya çalışıyorsanız, Hata veya RuntimeException alt sınıflarını kullanmak istersiniz.


9

Aslında kabul edilen cevap çok hoş değil, çünkü Java'nın doğrulama olmadan çalıştırılması gerekiyor, yani kod normal koşullar altında çalışmayacaktı.

AspectJ gerçek çözüm için kurtarmaya !

İstisna sınıfı:

package de.scrum_master.app;

public class ChuckNorrisException extends RuntimeException {
    public ChuckNorrisException(String message) {
        super(message);
    }
}

Görünüş:

package de.scrum_master.aspect;

import de.scrum_master.app.ChuckNorrisException;

public aspect ChuckNorrisAspect {
    before(ChuckNorrisException chuck) : handler(*) && args(chuck) {
        System.out.println("Somebody is trying to catch Chuck Norris - LOL!");
        throw chuck;
    }
}

Örnek uygulama:

package de.scrum_master.app;

public class Application {
    public static void main(String[] args) {
        catchAllMethod();
    }

    private static void catchAllMethod() {
        try {
            exceptionThrowingMethod();
        }
        catch (Throwable t) {
            System.out.println("Gotcha, " + t.getClass().getSimpleName() + "!");
        }
    }

    private static void exceptionThrowingMethod() {
        throw new ChuckNorrisException("Catch me if you can!");
    }
}

Çıktı:

Somebody is trying to catch Chuck Norris - LOL!
Exception in thread "main" de.scrum_master.app.ChuckNorrisException: Catch me if you can!
    at de.scrum_master.app.Application.exceptionThrowingMethod(Application.java:18)
    at de.scrum_master.app.Application.catchAllMethod(Application.java:10)
    at de.scrum_master.app.Application.main(Application.java:5)

8

Temanın bir varyasyonu, Java kodundan bildirilmemiş kontrol edilmiş istisnalar atabileceğiniz şaşırtıcı bir gerçektir. Yöntemler imzasında bildirilmediğinden, derleyici istisnayı kendisi yakalamanıza izin vermez, ancak java.lang.Exception olarak yakalayabilirsiniz.

Aşağıda, beyan edilmiş olsun veya olmasın, herhangi bir şey atmanıza izin veren bir yardımcı sınıf:

public class SneakyThrow {
  public static RuntimeException sneak(Throwable t) {
    throw SneakyThrow.<RuntimeException> throwGivenThrowable(t);
  }

  private static <T extends Throwable> RuntimeException throwGivenThrowable(Throwable t) throws T {
    throw (T) t;
  }
}

Şimdi throw SneakyThrow.sneak(new ChuckNorrisException());bir ChuckNorrisException atıyor, ancak derleyici

try {
  throw SneakyThrow.sneak(new ChuckNorrisException());
} catch (ChuckNorrisException e) {
}

ChuckNorrisException denetlenmiş bir özel durumsa atılan bir özel durumu yakalama hakkında.


6

Sadece ChuckNorrisExceptionJava s olmalıdır OutOfMemoryErrorve StackOverflowError.

catch(OutOfMemoryError ex)İstisna fırlatılması durumunda bir yürütmenin gerçekleştirileceği anlamına gelebilir, ancak bu blok istisnayı otomatik olarak arayan kişiye yeniden gönderir.

Bunun public class ChuckNorrisError extends Errorhile yaptığını sanmıyorum ama siz de deneyebilirsiniz. Uzatma hakkında hiçbir belge bulamadımError


2
Hata hala Throwable'ı genişletir, böylece onu yakalamayı önlemenin bir yolu yoktur. Java dilinin tasarımı budur.
JasonM1

1
@ JasonM1 Ben OP gerçekten "yakalanamayan" bir istisna istedi sanmıyorum ve ben onu yakalamak bile Hata yayılması anlamına geliyordu. Yani, herhangi bir
Atılabilir catchable

Zor olmak ChuckNorrisException doğrudan Throwable genişletebilir o zaman ne istisna ne de hata!
JasonM1

4
Hata yok değil bunu yakalamak eğer Bu fikre nereden emin değilim, hatta yaymak.
jtahlborn

3
Bence Erros hakkında kafanız karıştı, onlar Throwable ve hatta Throwable'ı genişleten her şey gibi normal istisnalar.
bestsss

6

Is it possible to construct a snippet of code in java that would make a hypothetical java.lang.ChuckNorrisException uncatchable?

Evet, işte cevabı: java.lang.ChuckNorrisExceptionÖyle bir örnek olmayacak şekilde tasarlajava.lang.Throwable . Neden? İzlenemeyen bir nesne tanım gereği erişilemez çünkü asla atılamayacak bir şey yakalayamazsınız.


2
Fakat o zaman bu bir istisna değildir.
dolbi

8
@dolbi: OP'nin sorusunda devletlerin java.lang.ChuckNorrisExceptionbir istisna olması gerektiği konusunda hiçbir yer bulamıyorum , yalnız atılabilir
Thomas Eding

1
Sanırım ifade edilmedi ama ima ediliyor. Bir matematikçiniz :-), değil mi?
dolbi

3

ChuckNorris'i dahili veya özel tutabilir ve onu kapsülleyebilir veya takip edebilirsiniz ...

try { doChuckAction(); } catch(ChuckNorrisException cne) { /*do something else*/ }


7
Fikrin onu yakalamak olduğuna inanmıyorum. Ben bir fikir olduğunu düşünüyorum önlemek yakalanmaktan onu.
Patrick Roberts

Eğer yanılıyorsam beni düzeltin ama eğer onu içsel yaparsanız, yansımasız ona ulaşamazsınız.
Jay

5
evet, ancak İstisna veya Fırlatılabilir yakalayabildiğiniz sürece gerçek türün görünürlüğü önemsizdir.
KeithS

3

Java'da özel durum işleme ile ilgili iki temel sorun, eylemin buna dayalı olarak gerçekleştirilip gerçekleştirilmeyeceğini belirtmek için bir istisna türü kullanması ve bir istisnayı temel alarak eylem yapan herhangi bir şeyin (yani "yakala") çözüleceği varsayılmasıdır. altta yatan durum. Bir istisna nesnesinin hangi işleyicilerin yürütmesi gerektiğine ve şimdiye kadar yürütülen işleyicilerin mevcut yöntemin çıkış koşullarını karşılaması için yeterince temizleyip temizlemediğine karar verebileceği bir araca sahip olmak faydalı olacaktır. Bu "yakalanamayan" istisnalar yapmak için kullanılabilse de, (1) yalnızca onlarla nasıl başa çıkılacağını gerçekten bilen bir kod tarafından yakalandıklarında ele alınacak istisnalar yapmak olmak için iki büyük kullanım olacaktır,finallyFooExceptionfinallya'nın çözülmesi sırasında bir blok sırasında BarException, her iki istisna da çağrı yığınını yaymalıdır; her ikisi de yakalanabilir olmalıdır, ancak her ikisi yakalanana kadar gevşemeye devam edilmelidir). Ne yazık ki, mevcut istisna işleme kodunu işleri bozmadan bu şekilde çalıştırmanın herhangi bir yolu olacağını düşünmüyorum.


ilginç bir fikir, ama ben düşük seviyeli kod arayan için belirli bir istisna "ne anlama geldiğini" bileceğini sanmıyorum, bu yüzden hiç atıcı hangi işleyicileri yürütmek karar vermek için mantıklı olacağını sanmıyorum.
jtahlborn

@jtahlborn: Şu anda, atıcı istisna tipi seçimi ile hangi istisna işleyicilerinin yürütmesi gerektiğine karar veriyor. Bu, bazı senaryoların temiz bir şekilde ele alınmasını imkansız hale getirir. Diğer şeylerin yanı sıra: (1) bir finallyblok daha önceki bir istisnayı temizlerken bir istisna oluşursa , her iki istisnanın da diğerinin yokluğunda kodun işlemesini ve devam etmesini bekleyeceği bir şey olması mümkündür, ama birini idare etmek ve diğerini görmezden gelmek kötü olur. Bununla birlikte, her iki işleyicinin işleyeceği bileşik bir istisna üretecek bir mekanizma yoktur.
supercat

@jtahlborn: Ayrıca, geri aramalarda oluşan istisnaların dış uygulama katmanı tarafından işlenmesine izin vermeyi çok zorlaştırır. Geri arama istisnası başka bir istisna türüne sarılırsa, geri çağrı istisnasının türü dış katmanda onu yakalayıp yakalamayacağınıza karar vermede kullanılamaz; kaydırılmazsa, geri aramada oluşan bir "yanlışlıkla" orta katman istisnası yanlış olabilir. Sarılmış bir istisna nesnesine dış uygulama katmanına geçirildiğinde söylendiğinde, sarılmış istisnaların türlerine cevap vermeye başlayabilir.
supercat

diğer noktalarınızı tartışmıyordum, sadece hangi işleyicilerin yürütüleceğine karar veren istisna nesnesi hakkındaki ifade. bir dereceye kadar istisna türleri bunu zaten yapıyor, ama anlaşılamadığım daha dinamik bir şey istediğiniz gibi görünüyor. Sanırım en büyük argümanınız (yanlara doğru geliyorsunuz) en altta olabildiğince fazla bilgi yakalamak ve üst katmanların tüm bu bilgileri görüp çalışmasına izin vermektir . Bu genel noktada size katılıyorum, ancak şeytan ayrıntılarda / uygulamada.
jtahlborn

@jtahlborn: Niyetim, sanal yöntemlerin özellikle "dinamik" herhangi bir şey uygulamalarını sağlamak değil, esasen "Üzerinde işlem yapılması gereken belirtilen tipte bir koşul var mı" demekti. Bununla birlikte, bahsetmeyi unuttuğum bir şey, çağrıların kodunun , kendini atmış veya kasıtlı olarak atmış gibi davranmak isteyen Foobir istisna arasında ayrım yapabileceği bir araç olması gerektiğidir , başka bir yöntem çağırıyor. "Kontrol edilen" istisnalar kavramı "hakkında" olmalıdır. FooFoo
supercat

1

Mevcut iş parçacığında yakalanmamış bir istisnayı simüle etmek kolayca mümkündür. Bu, yakalanmamış bir istisnanın düzenli davranışını tetikler ve böylece işi anlamsal olarak yapar. Ancak, gerçekte bir istisna olmadığı için geçerli iş parçacığının yürütülmesini durdurması gerekmez.

Throwable exception = /* ... */;
Thread currentThread = Thread.currentThread();
Thread.UncaughtExceptionHandler uncaughtExceptionHandler =
    currentThread.getUncaughtExceptionHandler();
uncaughtExceptionHandler.uncaughtException(currentThread, exception);
// May be reachable, depending on the uncaught exception handler.

Bu aslında (çok nadir) durumlarda, örneğin uygun Errorkullanım gerektiğinde yararlıdır , ancak yöntem herhangi bir çerçeveyi yakalayan (ve atayan) bir çerçeveden çağrılır Throwable.


0

System.exit (1) 'i çağırın finalizeve diğer tüm yöntemlerden istisnanın bir kopyasını atın, böylece program çıkacaktır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.