All Downloads are FREE. Search and download functionalities are using the official Maven repository.

me.magicall.biz.service.AbsRemoteRepo Maven / Gradle / Ivy

There is a newer version: 2.13.0
Show newest version
/*
 * Copyright (c) 2024 Liang Wenjian
 * magicall is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

package me.magicall.biz.service;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.google.common.collect.Lists;
import me.magicall.贵阳DearSun.Identified;
import me.magicall.贵阳DearSun.exception.UnknownException;
import me.magicall.program.lang.java.ClassKit;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.InetAddress;
import java.net.URI;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public abstract class AbsRemoteRepo<_Id, _Dto extends Identified<_Id>> implements Repo<_Dto> {
	public static final String DEFAULT_PROTOCOL = "http";
	public static final String DEFAULT_HOST = "localhost";
	public static final int DEFAULT_PORT = 80;

	private static final int DTO_LENGTH = "Dto".length();

	private final String userAgentVal;

	private final RestTemplate restTemplate;
	private final ObjectMapper objectMapper;

	private final String resourceName;
	private final Class<_Dto> dtoClass;
	private final Class<_Dto[]> dtoArrClass;

	public AbsRemoteRepo(final Class<_Dto> dtoClass) {
		this(dtoClass, toResourceName(dtoClass));
	}

	public AbsRemoteRepo(final Class<_Dto> dtoClass, final String resourceName) {
		this(dtoClass, resourceName, new ObjectMapper());
	}

	public AbsRemoteRepo(final Class<_Dto> dtoClass, final ObjectMapper objectMapper) {
		this(dtoClass, toResourceName(dtoClass), objectMapper);
	}

	@SuppressWarnings("unchecked")
	public AbsRemoteRepo(final Class<_Dto> dtoClass, final String resourceName, final ObjectMapper objectMapper) {
		this.resourceName = resourceName;
		this.dtoClass = dtoClass;
		try {
			userAgentVal = resourceName + "Client-" + getClass().getSimpleName()//
						   + '@' + InetAddress.getLocalHost().getCanonicalHostName() + "/1.0";
		} catch (final UnknownHostException e) {
			throw new UnknownException(e);
		}
		this.objectMapper = objectMapper == null ? new ObjectMapper() : objectMapper;
		initJackson();
		restTemplate = new RestTemplate(
			Collections.singletonList(new MappingJackson2HttpMessageConverter(this.objectMapper)));
		restTemplate.setInterceptors(Collections.singletonList((request, body, execution) -> {
			final var headers = request.getHeaders();
			headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON_UTF8));
			headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
			headers.set(HttpHeaders.USER_AGENT, userAgentVal);
			return execution.execute(request, body);
		}));

		dtoArrClass = (Class<_Dto[]>) ClassKit.arrClass(this.dtoClass, 1);
//		restTemplate.setErrorHandler(new ExtractingResponseErrorHandler());
	}

	private static String toResourceName(final Class dtoClass) {
		final var className = dtoClass.getSimpleName();
		return className.substring(0, className.length() - DTO_LENGTH);
	}

	private void initJackson() {
		objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
		objectMapper.registerModule(new JavaTimeModule());
	}

	@Override
	public List<_Dto> all() {
		return Lists.newArrayList(restTemplate.getForEntity(resourcesUri(), dtoArrClass).getBody());
	}

	public _Dto get(final _Id id) {
		return get(singleResourceUri(id));
	}

	public _Dto add(final _Dto entity) {
		return get(restTemplate.postForLocation(resourcesUri(), entity));
	}

	@Override
	public  List store(final Stream entities) {
		final var list = entities.collect(Collectors.toList());
		final var idsStr = joinToIds(list.stream());
		final var url = singleResourceUri(idsStr);
		restTemplate.put(url, entities);
		return list;//todo:没有再get。
	}

	@Override
	public void del(final Stream<_Dto> entities) {
		final var ids = joinToIds(entities);
		if (ids.isBlank()) {
			return;
		}
		restTemplate.delete(singleResourceUri(ids));
	}

	private String joinToIds(final Stream entities) {
		return entities.map(e -> e.id().toString()).collect(Collectors.joining(","));
	}

	private _Dto get(final URI uri) {
		if (uri == null) {
			return null;
		}
		return restTemplate.getForObject(uri, dtoClass);
	}

	private URI singleResourceUri(final Object id) {
		return singleResourceUriBuilder(id).toUri();
	}

	private UriComponents singleResourceUriBuilder(final Object id) {
		return resourcesUriBuilder().pathSegment("{id}").buildAndExpand(id);
	}

	private String resourcesUri() {
		return resourcesUriBuilder().toUriString();
	}

	private UriComponentsBuilder resourcesUriBuilder() {
		return UriComponentsBuilder.newInstance().scheme(getProtocol()).host(getHost()).port(getPort())
			.path(resourceName);
	}

	protected String getProtocol() {
		return DEFAULT_PROTOCOL;
	}

	protected String getHost() {
		return DEFAULT_HOST;
	}

	protected int getPort() {
		return DEFAULT_PORT;
	}
}