001/*
002 * Copyright 2002-2018 the original author or authors.
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 *      https://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 org.springframework.http.client.reactive;
018
019
020import java.nio.ByteBuffer;
021import java.util.concurrent.Executor;
022
023import org.eclipse.jetty.io.ByteBufferPool;
024import org.eclipse.jetty.io.MappedByteBufferPool;
025import org.eclipse.jetty.util.ProcessorUtils;
026import org.eclipse.jetty.util.component.LifeCycle;
027import org.eclipse.jetty.util.thread.QueuedThreadPool;
028import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
029import org.eclipse.jetty.util.thread.Scheduler;
030import org.eclipse.jetty.util.thread.ThreadPool;
031
032import org.springframework.beans.factory.DisposableBean;
033import org.springframework.beans.factory.InitializingBean;
034import org.springframework.lang.Nullable;
035import org.springframework.util.Assert;
036
037/**
038 * Factory to manage Jetty resources, i.e. {@link Executor}, {@link ByteBufferPool} and
039 * {@link Scheduler}, within the lifecycle of a Spring {@code ApplicationContext}.
040 *
041 * <p>This factory implements {@link InitializingBean} and {@link DisposableBean}
042 * and is expected typically to be declared as a Spring-managed bean.
043 *
044 * @author Sebastien Deleuze
045 * @since 5.1
046 */
047public class JettyResourceFactory implements InitializingBean, DisposableBean {
048
049        @Nullable
050        private Executor executor;
051
052        @Nullable
053        private ByteBufferPool byteBufferPool;
054
055        @Nullable
056        private Scheduler scheduler;
057
058        private String threadPrefix = "jetty-http";
059
060
061        /**
062         * Configure the {@link Executor} to use.
063         * <p>By default, initialized with a {@link QueuedThreadPool}.
064         * @param executor the executor to use
065         */
066        public void setExecutor(@Nullable Executor executor) {
067                this.executor = executor;
068        }
069
070        /**
071         * Configure the {@link ByteBufferPool} to use.
072         * <p>By default, initialized with a {@link MappedByteBufferPool}.
073         * @param byteBufferPool the {@link ByteBuffer} pool to use
074         */
075        public void setByteBufferPool(@Nullable ByteBufferPool byteBufferPool) {
076                this.byteBufferPool = byteBufferPool;
077        }
078
079        /**
080         * Configure the {@link Scheduler} to use.
081         * <p>By default, initialized with a {@link ScheduledExecutorScheduler}.
082         * @param scheduler the {@link Scheduler} to use
083         */
084        public void setScheduler(@Nullable Scheduler scheduler) {
085                this.scheduler = scheduler;
086        }
087
088        /**
089         * Configure the thread prefix to initialize {@link QueuedThreadPool} executor with. This
090         * is used only when a {@link Executor} instance isn't
091         * {@link #setExecutor(Executor) provided}.
092         * <p>By default set to "jetty-http".
093         * @param threadPrefix the thread prefix to use
094         */
095        public void setThreadPrefix(String threadPrefix) {
096                Assert.notNull(threadPrefix, "Thread prefix is required");
097                this.threadPrefix = threadPrefix;
098        }
099
100        /**
101         * Return the configured {@link Executor}.
102         */
103        @Nullable
104        public Executor getExecutor() {
105                return this.executor;
106        }
107
108        /**
109         * Return the configured {@link ByteBufferPool}.
110         */
111        @Nullable
112        public ByteBufferPool getByteBufferPool() {
113                return this.byteBufferPool;
114        }
115
116        /**
117         * Return the configured {@link Scheduler}.
118         */
119        @Nullable
120        public Scheduler getScheduler() {
121                return this.scheduler;
122        }
123
124        @Override
125        public void afterPropertiesSet() throws Exception {
126                String name = this.threadPrefix + "@" + Integer.toHexString(hashCode());
127                if (this.executor == null) {
128                        QueuedThreadPool threadPool = new QueuedThreadPool();
129                        threadPool.setName(name);
130                        this.executor = threadPool;
131                }
132                if (this.byteBufferPool == null) {
133                        this.byteBufferPool = new MappedByteBufferPool(2048,
134                                        this.executor instanceof ThreadPool.SizedThreadPool
135                                                        ? ((ThreadPool.SizedThreadPool) executor).getMaxThreads() / 2
136                                                        : ProcessorUtils.availableProcessors() * 2);
137                }
138                if (this.scheduler == null) {
139                        this.scheduler = new ScheduledExecutorScheduler(name + "-scheduler", false);
140                }
141
142                if (this.executor instanceof LifeCycle) {
143                        ((LifeCycle)this.executor).start();
144                }
145                this.scheduler.start();
146        }
147
148        @Override
149        public void destroy() throws Exception {
150                try {
151                        if (this.executor instanceof LifeCycle) {
152                                ((LifeCycle)this.executor).stop();
153                        }
154                }
155                catch (Throwable ex) {
156                        // ignore
157                }
158                try {
159                        if (this.scheduler != null) {
160                                this.scheduler.stop();
161                        }
162                }
163                catch (Throwable ex) {
164                        // ignore
165                }
166        }
167
168}