explicit JavaLog(jobject log) : log_(log) {
   const Context::Handle ctx = Context::getContext();
   LocalRef<jclass> logClass(ctx->env()->GetObjectClass(log));
   appendId_ = ctx->env()->GetMethodID(
       logClass.get(), "append",
       "(Ljava/time/LocalDateTime;Lcom/yandex/contest/invoker/ILog$Level;"
       "Ljava/lang/String;Ljava/lang/String;)V");
   ctx->throwIfOccured();
 }
LocalRef<jobject> newPrimitiveWrapper(const T obj) {
  using jinfo = traits::jinfo<T>;
  static_assert(jinfo::is_primitive, "Should be primitive.");

  const Context::Handle ctx = Context::getContext();
  LocalRef<jclass> clazz(ctx->env()->FindClass(jinfo::jwrapperclass().c_str()));
  const jmethodID valueOfId = ctx->env()->GetStaticMethodID(
      clazz.get(), "valueOf", jinfo::valueOfSig().c_str());
  LocalRef<jobject> wrapper(
      ctx->env()->CallStaticObjectMethod(clazz.get(), valueOfId, obj));
  ctx->throwIfOccured();
  return wrapper;
}
void getIterable(jobject jobj, const std::function<void(jobject)> &cb) {
  const Context::Handle ctx = Context::getContext();
  LocalRef<jclass> clazz(ctx->env()->GetObjectClass(jobj));
  const jmethodID iteratorId = ctx->env()->GetMethodID(
      clazz.get(), "iterator", "()Ljava/util/Iterator;");
  LocalRef<jobject> iter(ctx->env()->CallObjectMethod(jobj, iteratorId));
  LocalRef<jclass> iterClass(ctx->env()->GetObjectClass(iter.get()));
  const jmethodID hasNextId =
      ctx->env()->GetMethodID(iterClass.get(), "hasNext", "()Z");
  const jmethodID nextId =
      ctx->env()->GetMethodID(iterClass.get(), "next", "()Ljava/lang/Object;");
  while (ctx->env()->CallBooleanMethod(iter.get(), hasNextId)) {
    LocalRef<jobject> jobj(ctx->env()->CallObjectMethod(iter.get(), nextId));
    ctx->throwIfOccured();
    cb(jobj.get());
  }
}
void getMap(jobject jobj, const std::function<void(jobject, jobject)> &cb) {
  const Context::Handle ctx = Context::getContext();
  LocalRef<jclass> clazz(ctx->env()->GetObjectClass(jobj));
  const jmethodID entrySetId =
      ctx->env()->GetMethodID(clazz.get(), "entrySet", "()Ljava/util/Set;");
  LocalRef<jobject> entrySet(ctx->env()->CallObjectMethod(jobj, entrySetId));
  LocalRef<jclass> mapEntryClass(ctx->env()->FindClass("java/util/Map$Entry"));
  const jmethodID getKeyId = ctx->env()->GetMethodID(
      mapEntryClass.get(), "getKey", "()Ljava/lang/Object;");
  const jmethodID getValueId = ctx->env()->GetMethodID(
      mapEntryClass.get(), "getValue", "()Ljava/lang/Object;");
  ctx->throwIfOccured();
  getIterable(entrySet.get(), [&ctx, &cb, getKeyId, getValueId](jobject jobj) {
    LocalRef<jobject> jkey(ctx->env()->CallObjectMethod(jobj, getKeyId));
    LocalRef<jobject> jvalue(ctx->env()->CallObjectMethod(jobj, getValueId));
    cb(jkey.get(), jvalue.get());
  });
}