잡을 수없는 척


596

가설을 포착 할 수 없게하는 코드 스 니펫을 Java 로 구성 할 수 java.lang.ChuckNorrisException있습니까?

염두에 둔 생각은 예를 들어 인터셉터 또는 화면 지향 프로그래밍을 사용 합니다.



2
@jschoen이 제공 한 링크 (바이트 코드 검증기 비활성화)의 제안을 사용하면 Throwable을 확장하지 않는 것을 던질 수 있습니다 ! 아래 답변에 설명되어 있습니다.
jtahlborn

4
aioobe의 답변에서 발췌 한이 글은 @jschoen이 아주 잘 연결된 질문을 요약합니다. "즉, 귀하의 질문은 'JVM이 사양에서 벗어나면 원시성을 던지는 것과 같은 이상한 일을 할 수 있습니까?' 예."
Dan은 Firelight에 의해 방황

2
@Max-실용화에 대해 자세히 설명해 주시겠습니까?
Vineet Bhatia

3
에 다시 던지는 예외는 finalize()어떻습니까?
Lie Ryan

답변:


314

경우 나도 몰라 그래서 나는이 시도하지 않은 JVM은 이 같은 제한,하지만 어쩌면 당신은 발생 코드를 컴파일 할 수 ChuckNorrisException있지만, 실행시의 클래스 정의를 제공 ChuckNorrisException하는 Throwable를 확장하지 않습니다 .

최신 정보:

작동하지 않습니다. 검증 자 오류를 생성합니다.

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.

업데이트 2 :

실제로 바이트 코드 검증기를 비활성화하면이 기능을 사용할 수 있습니다! ( -Xverify:none)

업데이트 3 :

집에서 다음과 같은 사람들을 위해 다음은 전체 스크립트입니다.

다음 클래스를 작성하십시오.

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.");
        }
    }
}

컴파일 클래스 :

javac -cp . TestVillain.java ChuckNorrisException.java

운영:

java -cp . TestVillain
Gotcha!
The end.

"RuntimeException을 확장"과 주석 재 컴파일 ChuckNorrisException.java :

javac -cp . ChuckNorrisException.java

운영:

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.

확인없이 실행 :

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

18
그래, Object대신에 잡으면 Throwable어떨까? (컴파일러는 허용하지 않지만, 이미 검증기를 비활성화했기 때문에 바이트 코드를 해킹하여이를 수행 할 수도 있습니다.)
Ilmari Karonen

11
Java로 던질 수있는 것에 따르면 여전히 던질 수없는 것들을 잡을 수는 있지만 던지고 잡는 것은 정의되지 않은 동작입니다.
VolatileDream

8
@dzieciou 그들은 함께 사실 일 수 있습니다. 프로세서 유형의 특정 운영 체제 버전에서 사용중인 Java 환경 버전을 사용하여이를 파악할 수 있습니다. 그러나 표준에서 지정 될 수 있는지 여부를 파악할 수없는 경우 정의되지 않은 동작이라고합니다. Java의 다른 구현에서는이를 캐치 할 수 없도록 선택할 수 있기 때문입니다.
heinrich5991

2
흠. 나는 176 개의 공짜에 대해 전체 호출 스택을 원숭이 패치하여 예외를 다시 던지기 (물론 ctor에 의해 호출 됨) JNI 코드를 작성하기를 바랐습니다.
kdgregory

3
이 모든 것을 할 때, 한쪽 다리에 서서 머리를 가볍게 두드리고 딕시를 휘파람으로 배를 문지르는 것도 좋은 생각입니다 ...;);)
Glen Best

120

이것을 숙고 한 후, 나는 잡을 수없는 예외를 성공적으로 만들었습니다. JulesWinnfield그러나 그것은 하나의 버섯 구름을 낳는 어머니 예외이기 때문에 척 대신 이름을 지정했습니다 . 또한, 당신이 생각했던 것과 정확히 일치하지는 않지만 확실히 잡을 수는 없습니다. 관찰 :

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");
    }
}

짜잔! 잡히지 않은 예외.

산출:

운영:

'무엇을 다시 말해봐! 감히! 나는 당신을 두 번 감히!

자바 결과 : 8

빌드 성공 (총 시간 : 0 초)

시간이 좀 더 있으면 다른 것을 생각 해낼 수 없는지 알게 될 것입니다.

또한 이것을 확인하십시오 :

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)
    {

    }
}

스택 오버플로가 발생합니다. 다시 예외가 포착되지 않습니다.


32
답변에 스택 오버플로를 사용하면 +1입니다. 농담, 정말 좋은 대답.
요시야

7
적절한 "잡을 수없는 예외"는 모든 둘러싸는 finally 블록이 중간에 잡히지 않고 실행되도록합니다. 시스템을 죽이는 것은 예외를 던지는 것이 아니라 단지 시스템을 죽이는 것입니다.
supercat December

4
당신은 어떻게 "투척" JulesWinfield합니까? 던지기 전에 시스템이 멈춰 서지 않습니까?
supercat

6
@ mikeTheLiar : 생성자 중에 시스템이 종료됩니다. 이 문장 throw new Whatever()은 실제로 두 부분으로 구성 Whatever it = new Whatever(); throw it;되며 시스템은 두 번째 부분에 도달하기 전에 죽습니다.
supercat

5
@mikeTheLiar 실제로 Jules 또는 Vincent를 쉽게 잡을 있습니다 ... 던져 버린다면 . 던질 수없는 예외를 쉽게 만들 수 있습니다.class cn extends exception{private cn(){}}
John Dvorak

85

이러한 예외가있는 경우 분명히 System.exit(Integer.MIN_VALUE);생성자 를 사용하는 것이 필수적입니다. 왜냐하면 이러한 예외를 던지면 발생할 수 있기 때문입니다.)


32
+1; IMO는 이것이 유일한 해결책입니다. 잡을 수없는 예외는 프로그램을 종료해야합니다 ...

7
아니요, 그러한 예외를 던질 때 일어나는 일은 아닙니다. 잡히지 않은 예외는 단일 스레드를 종료하고 jvm을 종료하지 않습니다. 일부 상황에서는 System.exit 자체로 인해 SecurityException이 발생할 수도 있습니다. 모든 코드가 프로그램을 종료 할 수있는 것은 아닙니다.
josefx

3
while(true){}대신 사용할 수 있습니다 System.exit().
Piotr Praszmo

2
실제로, 당신은 할 수 있습니다 방지 System.exit()를 허용하지 보안 관리자를 설치하여 작동. 생성자를 다른 예외 (SecurityException)로 바꾸면 잡힐 수 있습니다.
jtahlborn

5
음, 기술적으로 당신 은 예외를 던지지 않았습니다 . 아직 던질 객체를 만들지 않았습니다!
Thomas Eding

46

모든 코드는 Throwable을 잡을 수 있습니다. 따라서, 당신이 만든 예외는 Throwable의 서브 클래스가되고 잡힐 수 있습니다.


11
hangChuckNorrisException을 포착하려는 시도에서 Throwable 자체가 발생합니다. : P
PermGenError

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

( 기술적 으로이 예외는 실제로 발생하지 않지만 적절한 ChuckNorrisException것을 던질 수는 없습니다. 먼저 먼저 던집니다.)


4
내 동료가 'System.exit (1)'호출로 보안 예외가 발생할 수 있다고 생각하면서 'for (;;) {}'를 고수 할 것을 제안했습니다. 나는 창의성을 위해 이것을 투표하고 있습니다!
Phil Street

나는 당신의 대답의 끝에 동의합니다. ChuckNorris를 엉망으로 만들지 마십시오.
Benj

28

던지는 예외는 Throwable을 확장해야하므로 항상 잡을 수 있습니다. 따라서 대답은 '아니요'입니다.

당신이 그것을 어렵게 처리 할 수 있도록하려는 경우, 당신은 방법을 대체 할 수 있습니다 getCause(), getMessage(), getStackTrace(), toString()서로를 던져 java.lang.ChuckNorrisException.


2
흠, catch (Throwable t) 어떤 메소드를 호출하거나 그렇지 않으면 객체를 변경합니까? catch 절이 예외를 더 발생시켜 불가능하게 할 수도 있습니다.
Colton

1
내가 생각하는 catch(Throwable t)사용자가 예외에 대처하고자 할 때 나의 제안은 다음 블록에서 적용 할 수 있도록 변수로 만 저장합니다
mirelon

24

내 대답은 @jtahlborn의 아이디어를 기반으로하지만 완전히 작동하는 Java 프로그램이며 JAR 파일 로 패키지화되고 웹 응용 프로그램 의 일부로 즐겨 사용하는 응용 프로그램 서버에 배포 할 수 있습니다 .

우선 ChuckNorrisException, 처음부터 JVM이 충돌하지 않도록 클래스를 정의합시다 (Chuck은 충돌하는 JVM 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);
    }
}

이제 Expendables클래스를 구성하여 구성합니다.

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);
        }
    }
}

그리고 마지막으로 Main수업에서 엉덩이를 걷어차십시오.

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");
        }
    }
}

다음 명령으로 컴파일하고 실행하십시오.

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

다음과 같은 출력이 나타납니다.

before
finally

놀랍지 않습니다-결국 라운드 하우스 킥입니다 :)


아주 좋아요! 클래스 정의 조작으로 많은 일을하지 않았습니다. 커맨드 라인에 여전히 "verify : none"이 필요합니까?
jtahlborn

@jtahlborn 예, Throwable의 자손이 아닌 객체를 던지려고하면 "verify : none"없이 실패합니다.
Wildfire

오, 나는 이것이 어떻게 든 그 제약 조건을 극복했다는 인상을 받았습니다. 그래서 이것은 내 대답과 어떻게 다릅니 까?
jtahlborn

2
가장 큰 차이점은 컴파일 타임 해킹없이 자바 코드가 작동한다는 것입니다.
Wildfire

15

생성자에서 반복적으로 호출하는 스레드를 시작할 수 있습니다. originalThread.stop (ChuckNorisException.this)

스레드는 예외를 반복적으로 잡아낼 수 있지만 죽을 때까지 계속 throw합니다.


13

아니요. Java의 모든 예외는 서브 클래스 여야하며 java.lang.Throwable, 모범 사례는 아니지만 모든 유형의 예외를 다음과 같이 잡을 수 있습니다.

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

자세한 정보는 java.lang.Throwable 문서를 참조하십시오.

확인 된 예외 (명시 적으로 처리 해야하는 예외) 를 피하려는 경우 Error 또는 RuntimeException을 서브 클래스 화하려고합니다.


9

실제로 승인 된 답변은 Java가 검증없이 실행되어야하기 때문에 좋지 않습니다. 즉, 정상적인 상황에서는 코드가 작동하지 않습니다.

실제 솔루션을 구하기위한 AspectJ !

예외 클래스 :

package de.scrum_master.app;

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

양상:

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;
    }
}

샘플 애플리케이션 :

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!");
    }
}

산출:

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

테마의 변형은 Java 코드에서 선언되지 않은 검사 예외를 던질 수 있다는 놀라운 사실입니다. 메소드 서명에 선언되어 있지 않기 때문에 컴파일러는 예외 자체를 잡을 수는 없지만 java.lang.Exception으로 잡을 수는 있습니다.

다음은 선언 여부에 관계없이 무언가를 던질 수있는 도우미 클래스입니다.

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;
  }
}

이제 throw SneakyThrow.sneak(new ChuckNorrisException());ChuckNorrisException이 발생하지만 컴파일러는

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

ChuckNorrisException이 확인 된 예외 인 경우 발생하지 않는 예외를 잡는 방법


6

ChuckNorrisExceptionJava에서 유일한 것은 OutOfMemoryErrorand 이어야합니다 StackOverflowError.

실제로 catch(OutOfMemoryError ex)예외가 발생하는 경우 a 가 실행 된다는 의미에서 "캐치"할 수 있지만 해당 블록은 자동으로 예외를 호출자에게 다시 던집니다.

나는 그것이 public class ChuckNorrisError extends Error트릭을 한다고 생각하지 않지만 시도해 볼 수 있습니다. 확장에 대한 문서를 찾지 못했습니다Error


2
오류는 여전히 Throwable을 확장하므로 오류 를 방지 할 수 없습니다. Java 언어의 설계에 의한 것입니다.
JasonM1

1
@ JasonM1 OP가 실제로 "일치 할 수없는"예외를 요청한 것으로 생각하지 않으며, 오류가 발생하더라도 오류가 전파됨을 의미했습니다. 그래서, 어떤의 Throwable은 잡을 수 있지만이 둘은 결국에 상관없이 당신이 무엇을 전달하지 않습니다
USR-로컬 ΕΨΗΕΛΩΝ

까다롭기 위해 ChuckNorrisException은 Throwable을 직접 확장 할 수 있습니다.
JasonM1

4
잡은 경우에도 오류가 전파 되지 않습니다 . 어디에서 그 아이디어를 얻었는지 잘 모르겠습니다.
jtahlborn

3
Erros에 대해 혼란 스럽습니다. Throwable 또는 Throwable 자체를 확장하는 모든 것과 같은 일반적인 예외입니다.
bestsss

6

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

예, 그리고 여기에 대한 답변이 있습니다 : java.lang.ChuckNorrisException의 인스턴스가 아닌 것을 디자인하십시오 java.lang.Throwable. 왜? 던질 수없는 물건은 결코 던질 수없는 것을 잡을 수 없기 때문에 정의에 의해 잡을 수 없습니다.


2
그러나 예외는 아닙니다.
dolbi

8
@dolbi : 나는 국가가 그 영업의 질문에는 장소를 찾을 수없는 java.lang.ChuckNorrisException예외가 될 혼자 throw 가능 객체를 할 수 있어야합니다
토마스 대한 수정 사항을

1
나는 그것이 언급되지 않았다고 생각하지만 암시 적입니다. 당신은 수학자입니다 :-), 그렇지 않습니까?
dolbi

3

ChuckNorris를 내부 또는 개인으로 유지하고 캡슐화하거나 부어 넣을 수 있습니다 ...

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


7
나는 그 아이디어가 그것을 잡는 것이라고 생각하지 않습니다. 아이디어가 발각되는 것을 막는 것이라고 생각합니다 .
패트릭 로버츠

내가 틀렸다면 바로 잡으십시오. 그러나 당신이 그것을 내면으로 만들면 성찰 없이는 갈 수 없습니다.
Jay

5
예, 그러나 예외 또는 Throwable을 잡을 수있는 한 실제 유형의 가시성은 관련이 없습니다.
KeithS

3

Java에서 예외 처리의 두 가지 근본적인 문제점은 예외 유형을 사용하여 조치를 수행해야하는지 여부를 표시하고 예외를 기반으로 조치를 취하는 항목 (예 : "캐치")이 해결 될 것으로 추정된다는 것입니다. 기본 조건. 예외 객체가 어떤 핸들러를 실행해야하는지, 지금까지 실행 한 핸들러가 현재 메소드가 종료 조건을 만족시킬만큼 충분히 정리했는지 여부를 결정할 수있는 수단을 갖는 것이 유용 할 것입니다. 이것은 "잡을 수없는"예외를 만드는 데 사용될 수 있지만, 두 가지 더 큰 용도는 (1) 예외를 처리하는 방법을 실제로 알고있는 코드에 의해 포착 될 때만 처리되는 예외를 만드는 것입니다.finallyFooExceptionfinally를 해제하는 동안 차단 BarException하면 두 예외가 모두 호출 스택을 전파해야합니다. 둘 다 잡을 수 있어야하지만 둘 다 잡힐 때까지 풀어야합니다. 불행히도 기존의 예외 처리 코드를 방해하지 않고 작동시키는 방법이 없다고 생각합니다.


흥미로운 생각이지만, 저수준 코드가 특정 예외가 호출자에게 "무엇을 의미하는지"알지 못한다고 생각하므로, 던지는 사람이 어떤 핸들러를 실행해야하는지 결정하는 것이 의미가 없다고 생각합니다.
jtahlborn

@jtahlborn : 현재, 던지는 사람은 예외 유형을 선택하여 어떤 예외 핸들러를 실행할지 결정합니다. 이로 인해 일부 시나리오를 깔끔하게 처리 할 수는 없습니다. (1) finally이전 예외에서 블록을 정리하는 동안 예외가 발생하면 다른 예외가 없으면 예외가 코드가 처리하고 계속할 것으로 예상되는 것일 수 있습니다. 그러나 하나를 처리하고 다른 하나를 무시하는 것은 좋지 않습니다. 그러나 두 핸들러가 처리 할 복합 예외를 생성하는 메커니즘은 없습니다.
supercat

@jtahlborn : 또한 콜백 내에서 발생하는 예외를 외부 응용 프로그램 계층에서 처리하기가 매우 어렵습니다. 콜백 예외가 다른 예외 유형으로 래핑 된 경우 콜백 예외 유형은 외부 계층에서이를 포착할지 여부를 결정할 때 사용할 수 없습니다. 래핑되지 않은 경우 "우발적 인"중간 계층 예외는 콜백에서 발생하는 예외로 오인 될 수 있습니다. 랩핑 된 예외 오브젝트가 외부 애플리케이션 계층으로 전달 될 때 알려지면 랩핑 된 예외 유형에 응답하기 시작할 수 있습니다.
supercat December

나는 당신의 다른 요점을 주장하지 않았으며, 어떤 핸들러가 실행될지를 결정하는 예외 객체에 대한 진술 만했다. 어느 정도 예외 유형은 이미 그렇게하지만, 당신이 더 역동적 인 것을 원한 것처럼 들립니다. 나는 당신의 주요 논쟁 (당신이 옆으로 오는 종류)은 맨 아래에서 가능한 많은 정보를 포착하고 상위 계층이 모든 정보를 보고 작업하게 하는 것이라고 생각합니다. 이 일반적인 점에서 나는 당신에게 동의하지만, 악마는 세부 사항 / 구현에 있습니다.
jtahlborn

@jtahlborn : 제 의도는 가상 메소드가 특히 "동적"을 구현하는 것이 아니라 본질적으로 "동작해야하는 표시된 유형의 조건이 있습니까"라고 말합니다. 그러나 내가 언급 한 것을 잊어 버린 한 가지는 호출하는 코드 가 자신을 던지거나 의도적으로 자신을 던지는 척 Foo하는 예외와 구별 하는 호출 코드를 구별 할 수 있는 수단이 있어야한다는 것 Foo입니다.Foo 이 때 발생하는 기대되지 않았습니다 다른 방법을 호출합니다. 이것이 "체크 된"예외의 개념이 "약"이어야하는 것입니다.
supercat December

1

현재 스레드에서 포착되지 않은 예외를 쉽게 시뮬레이션 할 수 있습니다. 이것은 잡히지 않은 예외의 규칙적인 동작을 유발하여 작업을 의미 적으로 수행합니다. 그러나 실제로 예외가 발생하지 않으므로 현재 스레드의 실행을 반드시 중지하지는 않습니다.

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

예를 들어 적절한 Error처리가 필요한 경우와 같이 (매우 드물게) 상황에서 실제로 유용 하지만, 메소드는 any를 포착 (및 삭제)하는 프레임 워크에서 호출됩니다 Throwable.


0

에서 System.exit (1)을 호출 finalize하고 다른 모든 메소드에서 예외 사본을 던져 프로그램이 종료되도록하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.