001/*
002 * Copyright 2022-2025 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 javax.annotation.Nonnull;
020import javax.annotation.Nullable;
021import javax.annotation.concurrent.ThreadSafe;
022import java.lang.reflect.Method;
023import java.util.Objects;
024
025import static java.lang.String.format;
026import static java.util.Objects.requireNonNull;
027
028/**
029 * Represents a <em>Resource Method</em>, which is a Java {@link Method} invoked by Soklet to handle an HTTP request.
030 * <p>
031 * Instances can be acquired via the {@link #withComponents(HttpMethod, ResourcePathDeclaration, Method, Boolean)} factory method.
032 * <p>
033 * Detailed documentation available at <a href="https://www.soklet.com/docs/request-handling">https://www.soklet.com/docs/request-handling</a>.
034 *
035 * @author <a href="https://www.revetkn.com">Mark Allen</a>
036 */
037@ThreadSafe
038public final class ResourceMethod {
039        @Nonnull
040        private final HttpMethod httpMethod;
041        @Nonnull
042        private final ResourcePathDeclaration resourcePathDeclaration;
043        @Nonnull
044        private final Method method;
045        @Nonnull
046        private final Boolean serverSentEventSource;
047
048        /**
049         * Vends a <em>Resource Method</em> given its unique components.
050         *
051         * @param httpMethod              an HTTP method
052         * @param resourcePathDeclaration an HTTP path which might contain placeholders, e.g. {@code /example/{exampleId}}
053         * @param method                  a Java method to invoke for the combination of HTTP method and resource path
054         * @param serverSentEventSource   is this <em>Resource Method</em> configured as a Server-Sent Event source?
055         * @return a <em>Resource Method</em> for the supplied components
056         */
057        @Nonnull
058        public static ResourceMethod withComponents(@Nonnull HttpMethod httpMethod,
059                                                                                                                                                                                        @Nonnull ResourcePathDeclaration resourcePathDeclaration,
060                                                                                                                                                                                        @Nonnull Method method,
061                                                                                                                                                                                        @Nonnull Boolean serverSentEventSource) {
062                requireNonNull(httpMethod);
063                requireNonNull(resourcePathDeclaration);
064                requireNonNull(method);
065                requireNonNull(serverSentEventSource);
066
067                return new ResourceMethod(httpMethod, resourcePathDeclaration, method, serverSentEventSource);
068        }
069
070        private ResourceMethod(@Nonnull HttpMethod httpMethod,
071                                                                                                 @Nonnull ResourcePathDeclaration resourcePathDeclaration,
072                                                                                                 @Nonnull Method method,
073                                                                                                 @Nonnull Boolean serverSentEventSource) {
074                requireNonNull(httpMethod);
075                requireNonNull(resourcePathDeclaration);
076                requireNonNull(method);
077                requireNonNull(serverSentEventSource);
078
079                this.httpMethod = httpMethod;
080                this.resourcePathDeclaration = resourcePathDeclaration;
081                this.method = method;
082                this.serverSentEventSource = serverSentEventSource;
083        }
084
085        @Override
086        public String toString() {
087                return format("%s{httpMethod=%s, resourcePathDeclaration=%s, method=%s, serverSentEventSource=%s}", getClass().getSimpleName(),
088                                getHttpMethod(), getResourcePathDeclaration(), getMethod(), isServerSentEventSource());
089        }
090
091        @Override
092        public boolean equals(@Nullable Object object) {
093                if (this == object)
094                        return true;
095
096                if (!(object instanceof ResourceMethod resourceMethod))
097                        return false;
098
099                return Objects.equals(getHttpMethod(), resourceMethod.getHttpMethod())
100                                && Objects.equals(getResourcePathDeclaration(), resourceMethod.getResourcePathDeclaration())
101                                && Objects.equals(getMethod(), resourceMethod.getMethod())
102                                && Objects.equals(isServerSentEventSource(), resourceMethod.isServerSentEventSource());
103        }
104
105        @Override
106        public int hashCode() {
107                return Objects.hash(getHttpMethod(), getResourcePathDeclaration(), getMethod(), isServerSentEventSource());
108        }
109
110        /**
111         * Returns the HTTP method for this <em>Resource Method</em>.
112         *
113         * @return the HTTP method
114         */
115        @Nonnull
116        public HttpMethod getHttpMethod() {
117                return this.httpMethod;
118        }
119
120        /**
121         * Returns the HTTP path for this <em>Resource Method</em>, which might contain placeholders - for example, {@code /example/{exampleId}}.
122         *
123         * @return the HTTP path
124         */
125        @Nonnull
126        public ResourcePathDeclaration getResourcePathDeclaration() {
127                return this.resourcePathDeclaration;
128        }
129
130        /**
131         * Returns the Java method to invoke for the combination of HTTP method and resource path.
132         *
133         * @return the Java method to invoke
134         */
135        @Nonnull
136        public Method getMethod() {
137                return this.method;
138        }
139
140        /**
141         * Returns whether this <em>Resource Method</em> functions as a Server-Sent Event Source.
142         *
143         * @return {@code true} if this <em>Resource Method</em> functions as a Server-Sent Event Source, {@code false} otherwise
144         */
145        @Nonnull
146        public Boolean isServerSentEventSource() {
147                return this.serverSentEventSource;
148        }
149}