Tomcat 비밀 탐지 (4): tomcat 시작 과정 상세 설명

Tomcat 에 익숙 한 엔지니어 들 이나 자바 개발 에 종사 하 는 사람들 은 Tomcat 이 어떻게 작 동 하고 멈 추 는 지 알 고 있 을 것 이다.Tomcat 소스 패키지 안에 빈 디 렉 터 리 가 있 습 니 다. 이 디 렉 터 리 에 중요 한 스 크 립 트 가 설치 되 어 있 습 니 다. Tomcat 시작 과 정지 스 크 립 트 프로그램 은 startup. bat, shutdown. bat (Windows 환경) 과 start. sh, shutdown. sh (Linux, Unix 환경) 입 니 다.모두 가 그들 을 어떻게 사용 하 는 지 알 고 있 을 것 이다. 다음은 그들 이 어떻게 서 비 스 를 시작 하고 중단 하 는 지 연구 하 는 것 이다.
실제 생산 환경 에서 절대 다수의 Tomcat 는 리 눅 스 환경 에 배 치 된 것 이다. 이 글 에서 우 리 는 startup. sh 와 shutdown. sh 를 예 로 들 어 시작 과 정지 과정 을 간단하게 말한다.(Tomcat 버 전 7.0.69)
시작 프로 세 스 분석:
Tomcat 의 bin 디 렉 터 리 에 들 어가 startup. sh 를 시작 하 는 명령 은 다음 과 같 습 니 다.
./startup.sh

이 대본 에는 뭐 가 들 어 있 을까요?시작 스 크 립 트 를 붙 입 니 다:
#!/bin/sh

# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# -----------------------------------------------------------------------------
# Start Script for the CATALINA Server
# -----------------------------------------------------------------------------

# Better OS/400 detection: see Bugzilla 31132
os400=false
case "`uname`" in
OS400*) os400=true;;
esac

# resolve links - $0 may be a softlink
PRG="$0"

while [ -h "$PRG" ] ; do
  ls=`ls -ld "$PRG"`
  link=`expr "$ls" : '.*-> \(.*\)$'`
  if expr "$link" : '/.*' > /dev/null; then
    PRG="$link"
  else
    PRG=`dirname "$PRG"`/"$link"
  fi
done

PRGDIR=`dirname "$PRG"`
EXECUTABLE=catalina.sh

# Check that target executable exists
if $os400; then
  # -x will Only work on the os400 if the files are:
  # 1. owned by the user
  # 2. owned by the PRIMARY group of the user
  # this will not work if the user belongs in secondary groups
  eval
else
  if [ ! -x "$PRGDIR"/"$EXECUTABLE" ]; then
    echo "Cannot find $PRGDIR/$EXECUTABLE"
    echo "The file is absent or does not have execute permission"
    echo "This file is needed to run this program"
    exit 1
  fi
fi

exec "$PRGDIR"/"$EXECUTABLE" start "$@"

다음은 이 스 크 립 트 를 간단하게 보 겠 습 니 다. 앞의 몇 줄:
os400=false
case "`uname`" in
OS400*) os400=true;;
esac

uname 를 사용 하여 현재 운영 체제 의 이름 을 보고 모드 OS 400 * 에 부합 되 는 지 판단 합 니 다. 만약 그렇다면 표 시 를 하고 os 400 = true 를 설정 하여 뒤의 논 리 를 제어 합 니 다.
다음 스 크 립 트 명령 에서 관련 된 두 가지 중요 한 변 수 는 PRGDIR (현재 startup. sh 스 크 립 트 의 이전 디 렉 터 리, 즉 bin 디 렉 터 리) 과 EXECUTABLE (스 크 립 트 이름 catalina. sh 설정) 입 니 다. 그리고 마지막 으로 실행 을 통 해
exec "$PRGDIR"/"$EXECUTABLE" start "$@"

catalina. sh 스 크 립 트 를 시작 하고 start 인 자 를 스 크 립 트 에 전달 하여 시작 작업 을 제어 합 니 다.catelina. sh 스 크 립 트 의 코드 가 길 기 때문에 시작 과 관련 된 일부 명령 만 붙 입 니 다.
...........
elif [ "$1" = "start" ] ; then

  if [ ! -z "$CATALINA_PID" ]; then
    if [ -f "$CATALINA_PID" ]; then
      if [ -s "$CATALINA_PID" ]; then
        echo "Existing PID file found during start."
        if [ -r "$CATALINA_PID" ]; then
          PID=`cat "$CATALINA_PID"`
          ps -p $PID >/dev/null 2>&1
          if [ $? -eq 0 ] ; then
            echo "Tomcat appears to still be running with PID $PID. Start aborted."
            echo "If the following process is not a Tomcat process, remove the PID file and try again:"
            ps -f -p $PID
            exit 1
          else
            echo "Removing/clearing stale PID file."
            rm -f "$CATALINA_PID" >/dev/null 2>&1
            if [ $? != 0 ]; then
              if [ -w "$CATALINA_PID" ]; then
                cat /dev/null > "$CATALINA_PID"
              else
                echo "Unable to remove or clear stale PID file. Start aborted."
                exit 1
              fi
            fi
          fi
        else
          echo "Unable to read PID file. Start aborted."
          exit 1
        fi
      else
        rm -f "$CATALINA_PID" >/dev/null 2>&1
        if [ $? != 0 ]; then
          if [ ! -w "$CATALINA_PID" ]; then
            echo "Unable to remove or write to empty PID file. Start aborted."
            exit 1
          fi
        fi
      fi
    fi
  fi

  shift
  touch "$CATALINA_OUT"
  if [ "$1" = "-security" ] ; then
    if [ $have_tty -eq 1 ]; then
      echo "Using Security Manager"
    fi
    shift
    eval "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \
      -Djava.endorsed.dirs="\"$JAVA_ENDORSED_DIRS\"" -classpath "\"$CLASSPATH\"" \
      -Djava.security.manager \
      -Djava.security.policy=="\"$CATALINA_BASE/conf/catalina.policy\"" \
      -Dcatalina.base="\"$CATALINA_BASE\"" \
      -Dcatalina.home="\"$CATALINA_HOME\"" \
      -Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \
      org.apache.catalina.startup.Bootstrap "$@" start \
      >> "$CATALINA_OUT" 2>&1 "&"

  else
    eval "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \
      -Djava.endorsed.dirs="\"$JAVA_ENDORSED_DIRS\"" -classpath "\"$CLASSPATH\"" \
      -Dcatalina.base="\"$CATALINA_BASE\"" \
      -Dcatalina.home="\"$CATALINA_HOME\"" \
      -Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \
      org.apache.catalina.startup.Bootstrap "$@" start \
      >> "$CATALINA_OUT" 2>&1 "&"

  fi

  if [ ! -z "$CATALINA_PID" ]; then
    echo $! > "$CATALINA_PID"
  fi

  echo "Tomcat started."

elif [ "$1" = "stop" ] ; then
.................

catalina. sh 스 크 립 트 는 모든 다른 스 크 립 트 가 최종 적 으로 실 행 된 스 크 립 트 입 니 다. 시작 하 든 스 크 립 트 를 중단 하 든 startup. sh 와 shutdown. sh 를 실행 하면 catalina. sh 로 변 환 됩 니 다. catalina. sh 를 실행 할 때 전달 하 는 매개 변 수 는 다 릅 니 다.
위의 빨간색 부분 은 서 비 스 를 시작 할 지 정지 할 지 판단 하 는 것 이 고 파란색 부분 은 일부 매개 변수 에 대한 검사 작업 입 니 다. 보라색 부분 에서 알 수 있 듯 이 최종 적 으로 시작 할 때 org. apache. catalina. startup. bootstrap 이라는 종 류 를 실 행 했 고 매개 변수 인 'start' 를 전달 했다.이전 글 에서 우 리 는 Tomcat 소스 코드 를 Eclipse 에 가 져 왔 습 니 다. 이 럴 때 우 리 는 패키지 이름 경로 에 따라 해당 하 는 종류의 Bootstrap 을 찾 고 main () 을 찾 을 수 있 습 니 다. 여기에 main 방법 을 붙 여 다음 과 같이 실현 할 수 있 습 니 다.
/**
     * Main method and entry point when starting Tomcat via the provided
     * scripts.
     *
     * @param args Command line arguments to be processed
     */
    public static void main(String args[]) {

        if (daemon == null) {
            // Don't set daemon until init() has completed
            Bootstrap bootstrap = new Bootstrap();
            try {
                bootstrap.init();
            } catch (Throwable t) {
                handleThrowable(t);
                t.printStackTrace();
                return;
            }
            daemon = bootstrap;
        } else {
            // When running as a service the call to stop will be on a new
            // thread so make sure the correct class loader is used to prevent
            // a range of class not found exceptions.
            Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
        }

        try {
            String command = "start";
            if (args.length > 0) {
                command = args[args.length - 1];
            }

            if (command.equals("startd")) {
                args[args.length - 1] = "start";
                daemon.load(args);
                daemon.start();
            } else if (command.equals("stopd")) {
                args[args.length - 1] = "stop";
                daemon.stop();
            } else if (command.equals("start")) {
                daemon.setAwait(true);
                daemon.load(args);
                daemon.start();
            } else if (command.equals("stop")) {
                daemon.stopServer(args);
            } else if (command.equals("configtest")) {
                daemon.load(args);
                if (null==daemon.getServer()) {
                    System.exit(1);
                }
                System.exit(0);
            } else {
                log.warn("Bootstrap: command \"" + command + "\" does not exist.");
            }
        } catch (Throwable t) {
            // Unwrap the Exception for clearer error reporting
            if (t instanceof InvocationTargetException &&
                    t.getCause() != null) {
                t = t.getCause();
            }
            handleThrowable(t);
            t.printStackTrace();
            System.exit(1);
        }

    }

위의 빨간색 부분 은 전 달 된 'start' 매개 변수 에 따라 확 정 된 분기 논리 입 니 다.
위의 main 방법 을 통 해 알 수 있 듯 이 전체 시작 과정 은 대략 세 단계 로 나 뉜 다. 예 를 들 어 위의 파란색 표 시 된 부분 이다.
(1) boottstrap 이 초기 화 되 었 는 지 판단 합 니까?없 으 면 초기 화 하기;
(2) Bootstrap 류 의 load () 를 호출 하여 server. xml 프로필 을 불 러 옵 니 다. (이것 은 매우 중요 한 프로필 입 니 다.)
(3) Bootstrap 류 의 start () 방법 으로 Tomcat 를 시작 합 니 다.
다음은 이 세 가지 절차 에 대해 간단 한 설명 을 하 겠 습 니 다.
첫 번 째 단계: boottstrap 이 초기 화 되 었 는 지 판단 합 니까?없 으 면 초기 화 하기;
위의 코드 를 통 해 실제 boottstrap. init () 호출 을 통 해 알 수 있 습 니 다.초기 화 를 하 러 왔 는데 구체 적 으로 뭘 했 을까요?init () 방법 이 어떻게 실현 되 는 지 봅 시다.
/**
     * Initialize daemon.
     */
    public void init()
        throws Exception
    {

        // Set Catalina path
        setCatalinaHome();
        setCatalinaBase();

        initClassLoaders();

        Thread.currentThread().setContextClassLoader(catalinaLoader);

        SecurityClassLoad.securityClassLoad(catalinaLoader);

        // Load our startup class and call its process() method
        if (log.isDebugEnabled())
            log.debug("Loading startup class");
        Class> startupClass =
            catalinaLoader.loadClass
            ("org.apache.catalina.startup.Catalina");
        Object startupInstance = startupClass.newInstance();

        // Set the shared extensions class loader
        if (log.isDebugEnabled())
            log.debug("Setting startup class properties");
        String methodName = "setParentClassLoader";
        Class> paramTypes[] = new Class[1];
        paramTypes[0] = Class.forName("java.lang.ClassLoader");
        Object paramValues[] = new Object[1];
        paramValues[0] = sharedLoader;
        Method method =
            startupInstance.getClass().getMethod(methodName, paramTypes);
        method.invoke(startupInstance, paramValues);

        catalinaDaemon = startupInstance;

    }

코드 에서 알 수 있 듯 이 주로 세 가지 일 을 했다.
(1) catalina. base 와 catalina. home 경 로 를 설정 합 니 다.
(2) 필요 한 클래스 로 더 를 초기 화 합 니 다.
(3) 자바 의 반사 체 제 를 이용 하여 setParent ClassLoader 방법 을 실행 하여 Tomcat 류 로드 시스템 의 최상 위 클래스 로 더 를 설정 합 니 다.
두 번 째 단계: Bootstrap 류 의 load () 를 호출 하여 server. xml 프로필 을 불 러 옵 니 다.
우 리 는 Bootstrap 류 의 load 방법 을 열 었 습 니 다. 다음 과 같 습 니 다.
/**
     * Load daemon.
     */
    private void load(String[] arguments)
        throws Exception {

        // Call the load() method
        String methodName = "load";
        Object param[];
        Class> paramTypes[];
        if (arguments==null || arguments.length==0) {
            paramTypes = null;
            param = null;
        } else {
            paramTypes = new Class[1];
            paramTypes[0] = arguments.getClass();
            param = new Object[1];
            param[0] = arguments;
        }
        Method method =
            catalinaDaemon.getClass().getMethod(methodName, paramTypes);
        if (log.isDebugEnabled())
            log.debug("Calling startup class " + method);
        method.invoke(catalinaDaemon, param);

    }

코드 를 통 해 알 수 있 듯 이 최종 적 으로 org. apache. catalina. startup. Catalina 류 의 load 방법 을 실행 하고 server. xml 파일 을 불 러 옵 니 다. 다음 과 같 습 니 다. 구체 적 인 해석 과정 에 대해 서 는 자세히 설명 하지 않 겠 습 니 다. 다음 에 전문 적 인 글 을 사용 하여 설명 하 겠 습 니 다. 제 블 로그 에 계속 관심 을 가지 거나 원본 을 보고 공부 하 세 요 ^ ^:
/**
     * Start a new server instance.
     */
    public void load() {

        long t1 = System.nanoTime();

        initDirs();

        // Before digester - it may be needed

        initNaming();

        // Create and execute our Digester
        Digester digester = createStartDigester();

        InputSource inputSource = null;
        InputStream inputStream = null;
        File file = null;
        try {
            try {
                file = configFile();
                inputStream = new FileInputStream(file);
                inputSource = new InputSource(file.toURI().toURL().toString());
            } catch (Exception e) {
                if (log.isDebugEnabled()) {
                    log.debug(sm.getString("catalina.configFail", file), e);
                }
            }
            if (inputStream == null) {
                try {
                    inputStream = getClass().getClassLoader()
                        .getResourceAsStream(getConfigFile());
                    inputSource = new InputSource
                        (getClass().getClassLoader()
                         .getResource(getConfigFile()).toString());
                } catch (Exception e) {
                    if (log.isDebugEnabled()) {
                        log.debug(sm.getString("catalina.configFail",
                                getConfigFile()), e);
                    }
                }
            }

            // This should be included in catalina.jar
            // Alternative: don't bother with xml, just create it manually.
            if( inputStream==null ) {
                try {
                    inputStream = getClass().getClassLoader()
                            .getResourceAsStream("server-embed.xml");
                    inputSource = new InputSource
                    (getClass().getClassLoader()
                            .getResource("server-embed.xml").toString());
                } catch (Exception e) {
                    if (log.isDebugEnabled()) {
                        log.debug(sm.getString("catalina.configFail",
                                "server-embed.xml"), e);
                    }
                }
            }


            if (inputStream == null || inputSource == null) {
                if  (file == null) {
                    log.warn(sm.getString("catalina.configFail",
                            getConfigFile() + "] or [server-embed.xml]"));
                } else {
                    log.warn(sm.getString("catalina.configFail",
                            file.getAbsolutePath()));
                    if (file.exists() && !file.canRead()) {
                        log.warn("Permissions incorrect, read permission is not allowed on the file.");
                    }
                }
                return;
            }

            try {
                inputSource.setByteStream(inputStream);
                digester.push(this);
                digester.parse(inputSource);
            } catch (SAXParseException spe) {
                log.warn("Catalina.start using " + getConfigFile() + ": " +
                        spe.getMessage());
                return;
            } catch (Exception e) {
                log.warn("Catalina.start using " + getConfigFile() + ": " , e);
                return;
            }
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    // Ignore
                }
            }
        }

        getServer().setCatalina(this);

        // Stream redirection
        initStreams();

        // Start the new server
        try {
            getServer().init();
        } catch (LifecycleException e) {
            if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
                throw new java.lang.Error(e);
            } else {
                log.error("Catalina.start", e);
            }

        }

        long t2 = System.nanoTime();
        if(log.isInfoEnabled()) {
            log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
        }

    }

세 번 째 단계: Bootstrap 류 의 start () 방법 으로 Tomcat 시작
start () 방법의 실현 을 엽 니 다. 다음 과 같 습 니 다.
/**
     * Start the Catalina daemon.
     */
    public void start()
        throws Exception {
        if( catalinaDaemon==null ) init();

        Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
        method.invoke(catalinaDaemon, (Object [])null);

    }

코드 로 볼 때, 사실 최종 적 으로 실 행 된 것 은 org. apache. catalina. startup. Catalina 류 의 start 방법 입 니 다. 그러면 org. apache. catalina. startup. Catalina 류 의 start 방법 은 어떻게 실 현 됩 니까?다음 과 같다.
/**
     * Start a new server instance.
     */
    public void start() {

        if (getServer() == null) {
            load();
        }

        if (getServer() == null) {
            log.fatal("Cannot start server. Server instance is not configured.");
            return;
        }

        long t1 = System.nanoTime();

        // Start the new server
        try {
            getServer().start();
        } catch (LifecycleException e) {
            log.fatal(sm.getString("catalina.serverStartFail"), e);
            try {
                getServer().destroy();
            } catch (LifecycleException e1) {
                log.debug("destroy() failed for failed Server ", e1);
            }
            return;
        }

        long t2 = System.nanoTime();
        if(log.isInfoEnabled()) {
            log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
        }

        // Register shutdown hook
        if (useShutdownHook) {
            if (shutdownHook == null) {
                shutdownHook = new CatalinaShutdownHook();
            }
            Runtime.getRuntime().addShutdownHook(shutdownHook);

            // If JULI is being used, disable JULI's shutdown hook since
            // shutdown hooks run in parallel and log messages may be lost
            // if JULI's hook completes before the CatalinaShutdownHook()
            LogManager logManager = LogManager.getLogManager();
            if (logManager instanceof ClassLoaderLogManager) {
                ((ClassLoaderLogManager) logManager).setUseShutdownHook(
                        false);
            }
        }

        if (await) {
            await();
            stop();
        }
    }

코드 를 보면 다음 과 같은 작업 을 했 습 니 다.
(1) 먼저 Catalina 의 서버 인 스 턴 스 가 초기 화 되 었 는 지 판단 하고 없 으 면 Catalina 류 의 load 방법 을 계속 호출 하여 초기 화 합 니 다.
(2) 초기 화 에 성공 하면 위 코드 에 표 시 된 빨간색 부분 을 호출 하여 전체 구성 요소 의 시작 작업 을 합 니 다. 여기 서 말 하고 싶 은 것 은 Tomcat 내부 디자인 의 문제 로 인해 전체 구성 요소 의 시작 이 분산 되 지 않 고 부모 구성 요소 의 시작 은 하위 요소 구성 요소 의 시작 을 이 끌 고 순서대로 유추 하면 전체 구성 요소 가 작 동 합 니 다.예 를 들 어 Server 의 시작 은 관련 Service 구성 요 소 를 모두 시작 시 키 고 Service 의 시작 은 Connector 의 시작 을 이 끌 수 있 습 니 다.여기 서 상세 하 게 설명 하지 않 고 다른 글 을 따로 설명 합 니 다. 이 글 은 전체 시작 과정 구 조 를 간단하게 묘사 합 니 다.
(3) 상기 코드 에서 파란색 부분 에서 보 듯 이 닫 는 갈 고 리 를 설정 하여 Tomcat 이 여러 가지 이유 로 종료 할 때 청 소 를 하 는 데 사용 합 니 다.
(4) Catalina 자체 클래스 의 await 방법 을 호출 하여 shutdown 명령 을 반복 적 으로 기다 립 니 다.
/**
     * Await and shutdown.
     */
    public void await() {

        getServer().await();

    }

Catalina 류 의 await () 방법 은 사실 Server 구성 요소 의 await () 방법 을 대리 한 것 입 니 다.
Eclipse 에서 org. apache. catalina. core 패키지 에 있 는 Server 구성 요소 의 표준 을 찾 아 Standard Server 를 실현 합 니 다. await 방법 이 무엇 을 했 는 지 살 펴 보 겠 습 니 다.
/**
     * Wait until a proper shutdown command is received, then return.
     * This keeps the main thread alive - the thread pool listening for http 
     * connections is daemon threads.
     */
    @Override
    public void await() {
        // Negative values - don't wait on port - tomcat is embedded or we just don't like ports
        if( port == -2 ) {
            // undocumented yet - for embedding apps that are around, alive.
            return;
        }
        if( port==-1 ) {
            try {
                awaitThread = Thread.currentThread();
                while(!stopAwait) {
                    try {
                        Thread.sleep( 10000 );
                    } catch( InterruptedException ex ) {
                        // continue and check the flag
                    }
                }
            } finally {
                awaitThread = null;
            }
            return;
        }

        // Set up a server socket to wait on
        try {
            awaitSocket = new ServerSocket(port, 1,
                    InetAddress.getByName(address));
        } catch (IOException e) {
            log.error("StandardServer.await: create[" + address
                               + ":" + port
                               + "]: ", e);
            return;
        }

        try {
            awaitThread = Thread.currentThread();

            // Loop waiting for a connection and a valid command
            while (!stopAwait) {
                ServerSocket serverSocket = awaitSocket;
                if (serverSocket == null) {
                    break;
                }
    
                // Wait for the next connection
                Socket socket = null;
                StringBuilder command = new StringBuilder();
                try {
                    InputStream stream;
                    long acceptStartTime = System.currentTimeMillis();
                    try {
                        socket = serverSocket.accept();
                        socket.setSoTimeout(10 * 1000);  // Ten seconds
                        stream = socket.getInputStream();
                    } catch (SocketTimeoutException ste) {
                        // This should never happen but bug 56684 suggests that
                        // it does.
                        log.warn(sm.getString("standardServer.accept.timeout",
                                Long.valueOf(System.currentTimeMillis() - acceptStartTime)), ste);
                        continue;
                    } catch (AccessControlException ace) {
                        log.warn("StandardServer.accept security exception: "
                                + ace.getMessage(), ace);
                        continue;
                    } catch (IOException e) {
                        if (stopAwait) {
                            // Wait was aborted with socket.close()
                            break;
                        }
                        log.error("StandardServer.await: accept: ", e);
                        break;
                    }

                    // Read a set of characters from the socket
                    int expected = 1024; // Cut off to avoid DoS attack
                    while (expected < shutdown.length()) {
                        if (random == null)
                            random = new Random();
                        expected += (random.nextInt() % 1024);
                    }
                    while (expected > 0) {
                        int ch = -1;
                        try {
                            ch = stream.read();
                        } catch (IOException e) {
                            log.warn("StandardServer.await: read: ", e);
                            ch = -1;
                        }
                        if (ch < 32)  // Control character or EOF terminates loop
                            break;
                        command.append((char) ch);
                        expected--;
                    }
                } finally {
                    // Close the socket now that we are done with it
                    try {
                        if (socket != null) {
                            socket.close();
                        }
                    } catch (IOException e) {
                        // Ignore
                    }
                }

                // Match against our command string
                boolean match = command.toString().equals(shutdown);
                if (match) {
                    log.info(sm.getString("standardServer.shutdownViaPort"));
                    break;
                } else
                    log.warn("StandardServer.await: Invalid command '"
                            + command.toString() + "' received");
            }
        } finally {
            ServerSocket serverSocket = awaitSocket;
            awaitThread = null;
            awaitSocket = null;

            // Close the server socket and return
            if (serverSocket != null) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    // Ignore
                }
            }
        }
    }

위 에 표 시 된 빨간색 부분 에서 설명 한 바 와 같이 이 방법 은 socket 연결 을 기다 리 고 shutdown 명령 을 받 습 니 다.위 코드 에서 파란색 태그 부분 은 Socket 연결 을 기다 리 고 받 은 명령 이 shutdown 변수 인지 판단 하 는 데 사 용 됩 니 다.
(5) Tomcat 이 정상적으로 실행 되 고 shutdown 명령 을 받 지 못 하면 프로그램 은 await () 방법 (4) 에 있 는 server Socket. accept () 을 기다 리 고 있 습 니 다. shutdown 명령 을 받 으 면 awaiit () 방법 은 순환 대기 에서 종료 되 고 stop 방법 을 실행 합 니 다. stop () 방법 이 무엇 을 했 는 지 살 펴 보 겠 습 니 다.
/**
     * Stop an existing server instance.
     */
    public void stop() {

        try {
            // Remove the ShutdownHook first so that server.stop()
            // doesn't get invoked twice
            if (useShutdownHook) {
                Runtime.getRuntime().removeShutdownHook(shutdownHook);

                // If JULI is being used, re-enable JULI's shutdown to ensure
                // log messages are not lost
                LogManager logManager = LogManager.getLogManager();
                if (logManager instanceof ClassLoaderLogManager) {
                    ((ClassLoaderLogManager) logManager).setUseShutdownHook(
                            true);
                }
            }
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            // This will fail on JDK 1.2. Ignoring, as Tomcat can run
            // fine without the shutdown hook.
        }

        // Shut down the server
        try {
            Server s = getServer();
            LifecycleState state = s.getState();
            if (LifecycleState.STOPPING_PREP.compareTo(state) <= 0
                    && LifecycleState.DESTROYED.compareTo(state) >= 0) {
                // Nothing to do. stop() was already called
            } else {
                s.stop();
                s.destroy();
            }
        } catch (LifecycleException e) {
            log.error("Catalina.stop", e);
        }

    }

코드 를 보면 먼저 실행 중 에 닫 힌 고 리 를 제거 한 다음 에 stop 방법 과 destroy () 방법 으로 각각 서 비 스 를 닫 고 자원 을 방출 합 니 다.
이로써 전체 Tomcat 의 시작 과정 에 대해 우 리 는 상세 하 게 설명 을 했 습 니 다. 그 중의 일부 세부 사항 에 대해 상세 하 게 설명 하지 않 고 다른 글 을 따로 설명 합 니 다. 예 를 들 어 클래스 로 더, 구성 요소 간 에 어떻게 서버 구성 요 소 를 시작 하면 모두 시작 할 수 있 는 지, 시작 에 성공 한 후에 닫 는 것 이 어떤 과정 인지 등 이 있 습 니 다. 알 고 싶 으 면 계속 닫 을 수 있 습 니 다.감사합니다.

좋은 웹페이지 즐겨찾기