/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.pulsar.client.impl.schema;

import static java.nio.charset.StandardCharsets.UTF_8;
import java.lang.reflect.Field;
import org.apache.avro.Schema;
import org.apache.avro.reflect.ReflectData;
import org.apache.commons.lang3.StringUtils;
import org.apache.pulsar.client.api.schema.SchemaDefinition;
import org.apache.pulsar.client.impl.schema.generic.GenericAvroSchema;
import org.apache.pulsar.common.schema.SchemaInfo;
import org.apache.pulsar.common.schema.SchemaType;

/**
 * This is a base schema implementation for Avro Based `Struct` types.
 * A struct type is used for presenting records (objects) which
 * have multiple fields.
 *
 * <p>Currently Pulsar supports 3 `Struct` types -
 * {@link org.apache.pulsar.common.schema.SchemaType#AVRO},
 * {@link org.apache.pulsar.common.schema.SchemaType#JSON},
 * and {@link org.apache.pulsar.common.schema.SchemaType#PROTOBUF}.
 */
@Deprecated
public abstract class StructSchema<T> extends AbstractStructSchema<T> {

    protected final Schema schema;

    protected StructSchema(SchemaInfo schemaInfo) {
        super(schemaInfo);
        this.schema = parseAvroSchema(new String(schemaInfo.getSchema(), UTF_8));
        if (schemaInfo.getProperties().containsKey(GenericAvroSchema.OFFSET_PROP)) {
            this.schema.addProp(GenericAvroSchema.OFFSET_PROP,
                    schemaInfo.getProperties().get(GenericAvroSchema.OFFSET_PROP));
        }
    }

    public Schema getAvroSchema() {
        return schema;
    }


    protected static Schema createAvroSchema(SchemaDefinition schemaDefinition) {
        Class pojo = schemaDefinition.getPojo();

        if (StringUtils.isNotBlank(schemaDefinition.getJsonDef())) {
            return parseAvroSchema(schemaDefinition.getJsonDef());
        } else if (pojo != null) {
            ThreadLocal<Boolean> validateDefaults = null;

            try {
                Field validateDefaultsField = Schema.class.getDeclaredField("VALIDATE_DEFAULTS");
                validateDefaultsField.setAccessible(true);
                validateDefaults = (ThreadLocal<Boolean>) validateDefaultsField.get(null);
            } catch (NoSuchFieldException | IllegalAccessException e) {
                throw new RuntimeException("Cannot disable validation of default values", e);
            }

            final boolean savedValidateDefaults = validateDefaults.get();

            try {
                // Disable validation of default values for compatibility
                validateDefaults.set(false);
                return extractAvroSchema(schemaDefinition, pojo);
            } finally {
                validateDefaults.set(savedValidateDefaults);
            }
        } else {
            throw new RuntimeException("Schema definition must specify pojo class or schema json definition");
        }
    }

    protected static Schema extractAvroSchema(SchemaDefinition schemaDefinition, Class pojo) {
        try {
            return parseAvroSchema(pojo.getDeclaredField("SCHEMA$").get(null).toString());
        } catch (NoSuchFieldException | IllegalAccessException | IllegalArgumentException ignored) {
            return schemaDefinition.getAlwaysAllowNull() ? ReflectData.AllowNull.get().getSchema(pojo)
                    : ReflectData.get().getSchema(pojo);
        }
    }

    protected static Schema parseAvroSchema(String schemaJson) {
        final Schema.Parser parser = new Schema.Parser();
        parser.setValidateDefaults(false);
        return parser.parse(schemaJson);
    }

    public static <T> SchemaInfo parseSchemaInfo(SchemaDefinition<T> schemaDefinition, SchemaType schemaType) {
        return SchemaInfoImpl.builder()
                .schema(createAvroSchema(schemaDefinition).toString().getBytes(UTF_8))
                .properties(schemaDefinition.getProperties())
                .name("")
                .type(schemaType).build();
    }

}

@@ -915,212 +915,114 @@
 ort 
-java.lang.reflect.Field;%0Aimport org.apache.avro.Schema;%0Aimport org.apache.avro.reflect.ReflectData;%0Aimport org.apache.commons.lang3.StringUtils;%0Aimport org.apache.pulsar.client.api.schema.SchemaDefinition
+static org.apache.pulsar.client.impl.schema.util.SchemaUtil.parseAvroSchema;%0Aimport org.apache.avro.Schema
 ;%0Aim
@@ -1144,59 +1144,8 @@
 nfo;
-%0Aimport org.apache.pulsar.common.schema.SchemaType;
 %0A%0A/*
@@ -2152,2408 +2152,6 @@
   %7D%0A
-%0A%0A    protected static Schema createAvroSchema(SchemaDefinition schemaDefinition) %7B%0A        Class pojo = schemaDefinition.getPojo();%0A%0A        if (StringUtils.isNotBlank(schemaDefinition.getJsonDef())) %7B%0A            return parseAvroSchema(schemaDefinition.getJsonDef());%0A        %7D else if (pojo != null) %7B%0A            ThreadLocal%3CBoolean%3E validateDefaults = null;%0A%0A            try %7B%0A                Field validateDefaultsField = Schema.class.getDeclaredField(%22VALIDATE_DEFAULTS%22);%0A                validateDefaultsField.setAccessible(true);%0A                validateDefaults = (ThreadLocal%3CBoolean%3E) validateDefaultsField.get(null);%0A            %7D catch (NoSuchFieldException %7C IllegalAccessException e) %7B%0A                throw new RuntimeException(%22Cannot disable validation of default values%22, e);%0A            %7D%0A%0A            final boolean savedValidateDefaults = validateDefaults.get();%0A%0A            try %7B%0A                // Disable validation of default values for compatibility%0A                validateDefaults.set(false);%0A                return extractAvroSchema(schemaDefinition, pojo);%0A            %7D finally %7B%0A                validateDefaults.set(savedValidateDefaults);%0A            %7D%0A        %7D else %7B%0A            throw new RuntimeException(%22Schema definition must specify pojo class or schema json definition%22);%0A        %7D%0A    %7D%0A%0A    protected static Schema extractAvroSchema(SchemaDefinition schemaDefinition, Class pojo) %7B%0A        try %7B%0A            return parseAvroSchema(pojo.getDeclaredField(%22SCHEMA$%22).get(null).toString());%0A        %7D catch (NoSuchFieldException %7C IllegalAccessException %7C IllegalArgumentException ignored) %7B%0A            return schemaDefinition.getAlwaysAllowNull() ? ReflectData.AllowNull.get().getSchema(pojo)%0A                    : ReflectData.get().getSchema(pojo);%0A        %7D%0A    %7D%0A%0A    protected static Schema parseAvroSchema(String schemaJson) %7B%0A        final Schema.Parser parser = new Schema.Parser();%0A        parser.setValidateDefaults(false);%0A        return parser.parse(schemaJson);%0A    %7D%0A%0A    public static %3CT%3E SchemaInfo parseSchemaInfo(SchemaDefinition%3CT%3E schemaDefinition, SchemaType schemaType) %7B%0A        return SchemaInfoImpl.builder()%0A                .schema(createAvroSchema(schemaDefinition).toString().getBytes(UTF_8))%0A                .properties(schemaDefinition.getProperties())%0A                .name(%22%22)%0A                .type(schemaType).build();%0A    %7D%0A%0A
 %7D%0A

Remove useless code of class StructSchema (#15949)