001/*
002 * Copyright 2012-2016 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 *      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 org.springframework.boot.loader;
018
019import java.util.ArrayList;
020import java.util.List;
021import java.util.jar.JarEntry;
022import java.util.jar.Manifest;
023
024import org.springframework.boot.loader.archive.Archive;
025import org.springframework.boot.loader.archive.Archive.Entry;
026import org.springframework.boot.loader.archive.Archive.EntryFilter;
027
028/**
029 * Base class for executable archive {@link Launcher}s.
030 *
031 * @author Phillip Webb
032 * @author Andy Wilkinson
033 */
034public abstract class ExecutableArchiveLauncher extends Launcher {
035
036        private final Archive archive;
037
038        public ExecutableArchiveLauncher() {
039                try {
040                        this.archive = createArchive();
041                }
042                catch (Exception ex) {
043                        throw new IllegalStateException(ex);
044                }
045        }
046
047        protected ExecutableArchiveLauncher(Archive archive) {
048                this.archive = archive;
049        }
050
051        protected final Archive getArchive() {
052                return this.archive;
053        }
054
055        @Override
056        protected String getMainClass() throws Exception {
057                Manifest manifest = this.archive.getManifest();
058                String mainClass = null;
059                if (manifest != null) {
060                        mainClass = manifest.getMainAttributes().getValue("Start-Class");
061                }
062                if (mainClass == null) {
063                        throw new IllegalStateException(
064                                        "No 'Start-Class' manifest entry specified in " + this);
065                }
066                return mainClass;
067        }
068
069        @Override
070        protected List<Archive> getClassPathArchives() throws Exception {
071                List<Archive> archives = new ArrayList<Archive>(
072                                this.archive.getNestedArchives(new EntryFilter() {
073
074                                        @Override
075                                        public boolean matches(Entry entry) {
076                                                return isNestedArchive(entry);
077                                        }
078
079                                }));
080                postProcessClassPathArchives(archives);
081                return archives;
082        }
083
084        /**
085         * Determine if the specified {@link JarEntry} is a nested item that should be added
086         * to the classpath. The method is called once for each entry.
087         * @param entry the jar entry
088         * @return {@code true} if the entry is a nested item (jar or folder)
089         */
090        protected abstract boolean isNestedArchive(Archive.Entry entry);
091
092        /**
093         * Called to post-process archive entries before they are used. Implementations can
094         * add and remove entries.
095         * @param archives the archives
096         * @throws Exception if the post processing fails
097         */
098        protected void postProcessClassPathArchives(List<Archive> archives) throws Exception {
099        }
100
101}