728x90
반응형

1. Mosquitto Docker compose 설정

  mosquitto:
    container_name: lpms-mosquitto
    restart: always
    image: eclipse-mosquitto
    ports:
      - "9000:1883"
      - "9001:9001"
    volumes:
      - ${MOSQUITTO_DIR}/config/mosquitto.conf:/mosquitto/config/mosquitto.conf
      - ${MOSQUITTO_DIR}/data:/mosquitto/data
      - ${MOSQUITTO_DIR}/log:/mosquitto/log

- mosquitto.conf

allow_anonymous true
connection_messages true
log_type all
listener 1883

 

2. Spring boot build.gradle 추가

    implementation 'org.springframework.boot:spring-boot-starter-integration'
    implementation 'org.springframework.integration:spring-integration-mqtt'

 

3. MqttProperties 클래스 생성

@ConfigurationProperties(prefix = "mqtt")
@Component
@Data
@Validated
public class MqttProperties {

    private String name;
    private String password;
    private String url;
    private Integer qos;
    private String topic;

}

 

4. MqttConfig 클래스 생성

본 프로젝트에서는 outBound만 사용 할 예정이다.

@Configuration
@RequiredArgsConstructor
public class MqttConfig {

    private static final String MQTT_CLIENT_ID = MqttAsyncClient.generateClientId();
    private final MqttProperties properties;

    /**
     * DefaultMqttPahoClientFactory를 통해 MQTT 클라이언트를 등록
     */
    @Bean
    public DefaultMqttPahoClientFactory defaultMqttPahoClientFactory() {
        DefaultMqttPahoClientFactory clientFactory = new DefaultMqttPahoClientFactory();
        MqttConnectOptions options = new MqttConnectOptions();
        options.setCleanSession(true);
        options.setServerURIs(new String[]{properties.getUrl()});
        options.setUserName(properties.getName());
        options.setPassword(properties.getPassword().toCharArray());
        clientFactory.setConnectionOptions(options);
        return clientFactory;
    }

    /**
     * MQTT 클라이언트를 통해 메시지를 구독하기 위하여 MqttPahoMessageDrivenChannelAdapter를 통해 메시지 수신을 위한 채널을 구성
     */
//    @Bean
//    public MessageChannel mqttInputChannel() {
//        return new DirectChannel();
//    }
//
//    @Bean
//    public MessageProducer inboundChannel() {
//        MqttPahoMessageDrivenChannelAdapter adapter =
//            new MqttPahoMessageDrivenChannelAdapter(
//                properties.getUrl(),
//                MQTT_CLIENT_ID,
//                properties.getTopic());
//        adapter.setCompletionTimeout(5000);
//        adapter.setConverter(new DefaultPahoMessageConverter());
//        adapter.setQos(1);
//        adapter.setOutputChannel(mqttInputChannel());
//        return adapter;
//    }
//
//    @Bean
//    @ServiceActivator(inputChannel = "mqttInputChannel")
//    public MessageHandler inboundMessageHandler() {
//        return message -> {
//            String topic = (String) message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC);
//            System.out.println("Topic:" + topic);
//            System.out.println("Payload" + message.getPayload());
//        };
//    }

    /**
     * Message outbound를 위한 채널 구성
     */

    @Bean
    public MessageChannel mqttOutboundChannel() {
        return new DirectChannel();
    }

    @Bean
    @ServiceActivator(inputChannel = "mqttOutboundChannel")
    public MessageHandler mqttOutbound(DefaultMqttPahoClientFactory clientFactory) {
        MqttPahoMessageHandler messageHandler =
            new MqttPahoMessageHandler(MQTT_CLIENT_ID, clientFactory);
        messageHandler.setAsync(true);
        messageHandler.setDefaultQos(1);
        return messageHandler;
    }

    @MessagingGateway(defaultRequestChannel = "mqttOutboundChannel")
    public interface MyGateway {

        void sendToMqtt(String data, @Header(MqttHeaders.TOPIC) String topic);

    }

}

 

5. MqttService 클래스 생성

@Service
@RequiredArgsConstructor
public class MqttService {

    private final MyGateway myGateway;

    public void send() {
        myGateway.sendToMqtt("12345", "/a/b/q");
    }
}

 

6. 도커 컨테이너 만들고, Spring 서버 킨 다음 필요한 곳에서 사용

728x90
반응형
728x90
반응형

Spring Security를 사용할 때 UserDetail 객체를 사용하는데, 예를들어

@AllArgsConstructor
public class UserDetailsImpl implements UserDetails {

@Getter
private Long id;

위와 같은 애들을 Request로 받아올 수 있다.

 

아래와 같이 사용하면 된다.

 

@PostMapping("/logout")
public CommonResponse<Boolean> logout(
@AuthenticationPrincipal UserDetailsImpl userDetails,
HttpServletResponse response
) {
728x90
반응형
728x90
반응형

기본적으로 LocalDateTime에는 timezone이 없기 때문에 따로 세팅을 해주어야한다.

먼저 now를 생성할 때 다음과 같이 UTC 시간을 준다.

LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);

 

그 다음 timezone을 세팅할 때 다음과 같이 해준다.

now..atZone(TimeZone.getDefault().toZoneId())
                                .format(DateTimeFormatter.RFC_1123_DATE_TIME)

 

어떤 형식으로 보여줄 지는 다음 사이트를 참고하면 된다.

DateTimeFormatter (Java Platform SE 8 ) (oracle.com)

 

DateTimeFormatter (Java Platform SE 8 )

Parses the text using this formatter, without resolving the result, intended for advanced use cases. Parsing is implemented as a two-phase operation. First, the text is parsed using the layout defined by the formatter, producing a Map of field to value, a

docs.oracle.com

 

728x90
반응형
728x90
반응형

Microsoft Teams로 알림메세지를 보낼 경우 다음과 같이 구현할 수 있다.

 

1. TeamsWebhookService

@Service
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Slf4j
@RequiredArgsConstructor
public class TeamsWebhookService {

    public void send(TeamsWebhookMessageDto dto) {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setConnectTimeout(10000);
        factory.setReadTimeout(10000);
        HttpHeaders httpHeaders = new HttpHeaders();
        RestTemplate restTemplate = new RestTemplate(factory);
        httpHeaders.setContentType(new MediaType("application", "json", StandardCharsets.UTF_8));

        HttpEntity<TeamsWebhookMessageDto> request = new HttpEntity<>(dto, httpHeaders);

        try {
            restTemplate.postForLocation(new URI(dto.getUrl()), request);
        } catch (URISyntaxException e) {
            log.error(e.getMessage());
            throw new RuntimeException(e);
        }
    }

    public List<Map<String, Object>> makeAttachments(
        String title
    ) {
        List<Map<String, Object>> attachments = List.of(
            Map.of(
                "contentType", "application/vnd.microsoft.card.adaptive",
                "content", new Content(
                    List.of(
                        Map.of(
                            "type", "Container",
                            "items", List.of(
                                Map.of(
                                    "type", "TextBlock",
                                    "text", title,
                                    "weight","bolder",
                                    "size","medium"
                                )
                            )
                        ),
                        Map.of(
                            "type", "Container",
                            "items", List.of(
                                Map.of(
                                    "type", "FactSet",
                                    "facts", List.of(
                                        Map.of(
                                            "title","title: ",
                                            "value","value"
                                        )
                                    )
                                )
                            )
                        )
                    )
                )
            )
        );
        return attachments;
    }
}

 

2. MessageDto

@Data
public class TeamsWebhookMessageDto {

    private String url;
    private String type = "message";
    private List<Map<String, Object>> attachments;

    @Data
    public static class Content {

        @JsonProperty("$schema")
        private String schema;
        private String type;
        private String version ;
        private List<Map<String, Object>> body;

        public Content(List<Map<String, Object>> body) {
            this.schema = "http://adaptivecards.io/schemas/adaptive-card.json";
            this.type = "AdaptiveCard";
            this.version = "1.0";
            this.body = body;
        }
    }

    public TeamsWebhookMessageDto(List<Map<String, Object>> attachments) {
        this.attachments = attachments;
    }

}

 

3. send 부분

TeamsWebhookMessageDto messageDto = new TeamsWebhookMessageDto(
                    teamsWebhookService.makeAttachments("title")
                );
                messageDto.setUrl("URL 들어가는 부분");

                teamsWebhookService.send(messageDto);

 

- Adaptive Card Example code

Schema Explorer | Adaptive Cards

 

Schema Explorer | Adaptive Cards

Schema Explorer Choose element: Important note about accessibility: In version 1.3 of the schema we introduced a label property on Inputs to improve accessibility. If the Host app you are targeting supports v1.3 you should use label instead of a TextBlock

adaptivecards.io

 

728x90
반응형
728x90
반응형

자바스크립트에서 참 편했던 점이 따로 객체를 만들지 않고 중괄호나 대괄호로 바로 리스트 맵을 만들 수 있었던 점이였다.

자바에서도 똑같진 않지만 아래와 같이 편하게 초깃값이 주어진 채로 선언할 수 있다.

 

public static List<Map<String, Integer>> eventList = Arrays.asList(
        new HashMap<>() {{
            put(value.name(), i);
        }},
        new HashMap<>() {{
            put("NO_ERROR", 0x00000000);
        }}
    );
728x90
반응형
728x90
반응형

삭제 명령을 내릴 때 데이터가 삭제되는 것이 아니라 다른 액션을 주고 싶을 때 

@SQLDelete 어노테이션을 쓰면 간단하게 해결할 수 있다.

@SQLDelete(sql = "UPDATE my_table SET deleted_at = current_timestamp WHERE id = ?")
public class MyTable {

...

@Column
private LocalDateTime deletedAt;


}

 

위의 예시는 삭제된 시점을 deleted_at 컬럼으로 지정한 것이다.

 

데이터를 조회할 땐 deleted_at이 null인지 여부를 따져서 조회하면 된다.

728x90
반응형
728x90
반응형

순회를 하는 도중에 무언가 작업을 한다면 ConcurrentModificationException 이 뜰 수 있다.

다음과 같이 써보자.

 

 

 

List<String> list = new ArrayList<>();

list.add("str1");
list.add("str2");
list.add("str3");

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String str = iterator.next();
    if ("str1".equals(str)) {
        iterator.remove();
    }
}

 

아래와 같이 removeIf 메소드로 간편하게 사용할 수도 있다.

 

list.removeIf(event -> !otherList.contains(event));
728x90
반응형
728x90
반응형

일반 리스트에서 내가 원하는 Key를 가진 Map List로 만들 경우 다음과 같이 Collectors를 사용해 편하게 만들 수 있다.

public Map<Integer, Animal> convertListAfterJava8(List<Animal> list) {
    Map<Integer, Animal> map = list.stream()
      .collect(Collectors.toMap(Animal::getId, Function.identity()));
    return map;
}

 

728x90
반응형
728x90
반응형

Enum 의 name 들만 따로 list로 만들고 싶을 경우 다음과 같이 만들 수 있다.

public enum State {
    NEW,
    RUNNABLE,
    BLOCKED,
    WAITING,
    TIMED_WAITING,
    TERMINATED;

    public static String[] names() {
        // ...
    }
}

 

public static String[] getNames(Class<? extends Enum<?>> e) {
    return Arrays.toString(e.getEnumConstants()).replaceAll("^.|.$", "").split(", ");
}

 

아래와 같이 파라미터를 자유자재로 쓸 수도 있다.

public String[] getNames() {
    return Arrays.stream(MyEnum.class.getEnumConstants()).map(Enum::name)
        .toArray(String[]::new);
}
        
public String[] getNames(Class<? extends Enum<?>> e) {
    return Arrays.stream(e.getEnumConstants()).map(Enum::name)
        .toArray(String[]::new);
}

public List<String> getNames(Class<? extends Enum<?>> e) {
        return Arrays.stream(e.getEnumConstants()).map(Enum::name).toList();
    }
728x90
반응형
728x90
반응형

1. build.gradle 추가

    implementation 'org.springframework.boot:spring-boot-starter-mail'

 

2. Service 작성

JavaMailSenderImpl 을 불러와 설정을 적용시키고 보내는 방식이다.

@Service
@Slf4j
@RequiredArgsConstructor
public class EmailService {

    public void sendMail(String subject, String text) {
        try {

            String mailServer = "메일서버";
            int port = "포트";
            String from = "보내는사람";
            String to = "받는사람";
            String username = "메일 아이디";
            String password = "메일 비밀번호";
            Boolean useTls = TLS를 쓸 경우 true;
            TlsVersion tlsVersion = TLS 버전(ex. TLSv1.2);

            JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
            javaMailSender.setHost(mailServer);
            javaMailSender.setPort(port);
            javaMailSender.setDefaultEncoding("UTF-8");

            if (!Objects.equals(username, "")) {
                javaMailSender.setUsername(username);
            }

            if (!Objects.equals(password, "")) {
                javaMailSender.setPassword(password);
            }

            Properties pt = new Properties();

            if (Objects.equals(username, "") && Objects.equals(password, "")) {
                pt.put("mail.smtp.auth", false);
            } else {
                pt.put("mail.smtp.auth", true);
            }

            if (useTls) {
                pt.put("mail.smtp.socketFactory.port", port);
                pt.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
                pt.put("mail.smtp.starttls.enable", true);
                pt.put("mail.smtp.starttls.required", true);
                pt.put("mail.smtp.ssl.protocols", tlsVersion);
            }

            javaMailSender.setJavaMailProperties(pt);

            MimeMessage message = javaMailSender.createMimeMessage();
            MimeMessageHelper helper = new MimeMessageHelper(message, true);

            helper.setFrom(from);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(text);
            javaMailSender.send(message);

        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }
}

 

3. 구글에서 보안 설정을 해준다.

설정-> 계정 -> 2단계 인증을 설정하고 앱 비밀번호를 사용하면 된다.

참고 Spring Mail AuthenticationFailedException 해결하기 | Be an Overachiever (ivvve.github.io) 

 

728x90
반응형

+ Recent posts