001/*
002 * Copyright 2022-2026 Revetware LLC.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.soklet;
018
019import org.jspecify.annotations.NonNull;
020import org.jspecify.annotations.Nullable;
021
022import javax.annotation.concurrent.ThreadSafe;
023import java.lang.reflect.Method;
024import java.util.Objects;
025
026import static java.lang.String.format;
027import static java.util.Objects.requireNonNull;
028
029/**
030 * Represents a <em>Resource Method</em>, which is a Java {@link Method} invoked by Soklet to handle an HTTP request.
031 * <p>
032 * Instances can be acquired via the {@link #fromComponents(HttpMethod, ResourcePathDeclaration, Method, Boolean)} factory method.
033 * <p>
034 * Detailed documentation available at <a href="https://www.soklet.com/docs/request-handling">https://www.soklet.com/docs/request-handling</a>.
035 *
036 * @author <a href="https://www.revetkn.com">Mark Allen</a>
037 */
038@ThreadSafe
039public final class ResourceMethod {
040        @NonNull
041        private final HttpMethod httpMethod;
042        @NonNull
043        private final ResourcePathDeclaration resourcePathDeclaration;
044        @NonNull
045        private final Method method;
046        @NonNull
047        private final Boolean serverSentEventSource;
048
049        /**
050         * Vends a <em>Resource Method</em> given its unique components.
051         *
052         * @param httpMethod              an HTTP method
053         * @param resourcePathDeclaration an HTTP path which might contain placeholders, e.g. {@code /example/{exampleId}}
054         * @param method                  a Java method to invoke for the combination of HTTP method and resource path
055         * @param serverSentEventSource   is this <em>Resource Method</em> configured as a Server-Sent Event source?
056         * @return a <em>Resource Method</em> for the supplied components
057         */
058        @NonNull
059public static ResourceMethod fromComponents(@NonNull HttpMethod httpMethod,
060                                                                                                                                                                                        @NonNull ResourcePathDeclaration resourcePathDeclaration,
061                                                                                                                                                                                        @NonNull Method method,
062                                                                                                                                                                                        @NonNull Boolean serverSentEventSource) {
063                requireNonNull(httpMethod);
064                requireNonNull(resourcePathDeclaration);
065                requireNonNull(method);
066                requireNonNull(serverSentEventSource);
067
068                return new ResourceMethod(httpMethod, resourcePathDeclaration, method, serverSentEventSource);
069        }
070
071        private ResourceMethod(@NonNull HttpMethod httpMethod,
072                                                                                                 @NonNull ResourcePathDeclaration resourcePathDeclaration,
073                                                                                                 @NonNull Method method,
074                                                                                                 @NonNull Boolean serverSentEventSource) {
075                requireNonNull(httpMethod);
076                requireNonNull(resourcePathDeclaration);
077                requireNonNull(method);
078                requireNonNull(serverSentEventSource);
079
080                this.httpMethod = httpMethod;
081                this.resourcePathDeclaration = resourcePathDeclaration;
082                this.method = method;
083                this.serverSentEventSource = serverSentEventSource;
084        }
085
086        @Override
087        public String toString() {
088                return format("%s{httpMethod=%s, resourcePathDeclaration=%s, method=%s, serverSentEventSource=%s}", getClass().getSimpleName(),
089                                getHttpMethod(), getResourcePathDeclaration(), getMethod(), isServerSentEventSource());
090        }
091
092        @Override
093        public boolean equals(@Nullable Object object) {
094                if (this == object)
095                        return true;
096
097                if (!(object instanceof ResourceMethod resourceMethod))
098                        return false;
099
100                return Objects.equals(getHttpMethod(), resourceMethod.getHttpMethod())
101                                && Objects.equals(getResourcePathDeclaration(), resourceMethod.getResourcePathDeclaration())
102                                && Objects.equals(getMethod(), resourceMethod.getMethod())
103                                && Objects.equals(isServerSentEventSource(), resourceMethod.isServerSentEventSource());
104        }
105
106        @Override
107        public int hashCode() {
108                return Objects.hash(getHttpMethod(), getResourcePathDeclaration(), getMethod(), isServerSentEventSource());
109        }
110
111        /**
112         * Returns the HTTP method for this <em>Resource Method</em>.
113         *
114         * @return the HTTP method
115         */
116        @NonNull
117        public HttpMethod getHttpMethod() {
118                return this.httpMethod;
119        }
120
121        /**
122         * Returns the HTTP path for this <em>Resource Method</em>, which might contain placeholders - for example, {@code /example/{exampleId}}.
123         *
124         * @return the HTTP path
125         */
126        @NonNull
127        public ResourcePathDeclaration getResourcePathDeclaration() {
128                return this.resourcePathDeclaration;
129        }
130
131        /**
132         * Returns the Java method to invoke for the combination of HTTP method and resource path.
133         *
134         * @return the Java method to invoke
135         */
136        @NonNull
137        public Method getMethod() {
138                return this.method;
139        }
140
141        /**
142         * Returns whether this <em>Resource Method</em> functions as a Server-Sent Event Source.
143         *
144         * @return {@code true} if this <em>Resource Method</em> functions as a Server-Sent Event Source, {@code false} otherwise
145         */
146        @NonNull
147        public Boolean isServerSentEventSource() {
148                return this.serverSentEventSource;
149        }
150}