001/* 002 * Copyright 2012-2017 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.devtools.restart.classloader; 018 019import java.io.Serializable; 020import java.util.Collection; 021import java.util.Collections; 022import java.util.LinkedHashMap; 023import java.util.Map; 024import java.util.Map.Entry; 025import java.util.Set; 026 027import javax.management.loading.ClassLoaderRepository; 028 029import org.springframework.util.Assert; 030 031/** 032 * {@link ClassLoaderFileRepository} that maintains a collection of 033 * {@link ClassLoaderFile} items grouped by source folders. 034 * 035 * @author Phillip Webb 036 * @since 1.3.0 037 * @see ClassLoaderFile 038 * @see ClassLoaderRepository 039 */ 040public class ClassLoaderFiles implements ClassLoaderFileRepository, Serializable { 041 042 private static final long serialVersionUID = 1; 043 044 private final Map<String, SourceFolder> sourceFolders; 045 046 /** 047 * Create a new {@link ClassLoaderFiles} instance. 048 */ 049 public ClassLoaderFiles() { 050 this.sourceFolders = new LinkedHashMap<>(); 051 } 052 053 /** 054 * Create a new {@link ClassLoaderFiles} instance. 055 * @param classLoaderFiles the source classloader files. 056 */ 057 public ClassLoaderFiles(ClassLoaderFiles classLoaderFiles) { 058 Assert.notNull(classLoaderFiles, "ClassLoaderFiles must not be null"); 059 this.sourceFolders = new LinkedHashMap<>(classLoaderFiles.sourceFolders); 060 } 061 062 /** 063 * Add all elements items from the specified {@link ClassLoaderFiles} to this 064 * instance. 065 * @param files the files to add 066 */ 067 public void addAll(ClassLoaderFiles files) { 068 Assert.notNull(files, "Files must not be null"); 069 for (SourceFolder folder : files.getSourceFolders()) { 070 for (Map.Entry<String, ClassLoaderFile> entry : folder.getFilesEntrySet()) { 071 addFile(folder.getName(), entry.getKey(), entry.getValue()); 072 } 073 } 074 } 075 076 /** 077 * Add a single {@link ClassLoaderFile} to the collection. 078 * @param name the name of the file 079 * @param file the file to add 080 */ 081 public void addFile(String name, ClassLoaderFile file) { 082 addFile("", name, file); 083 } 084 085 /** 086 * Add a single {@link ClassLoaderFile} to the collection. 087 * @param sourceFolder the source folder of the file 088 * @param name the name of the file 089 * @param file the file to add 090 */ 091 public void addFile(String sourceFolder, String name, ClassLoaderFile file) { 092 Assert.notNull(sourceFolder, "SourceFolder must not be null"); 093 Assert.notNull(name, "Name must not be null"); 094 Assert.notNull(file, "File must not be null"); 095 removeAll(name); 096 getOrCreateSourceFolder(sourceFolder).add(name, file); 097 } 098 099 private void removeAll(String name) { 100 for (SourceFolder sourceFolder : this.sourceFolders.values()) { 101 sourceFolder.remove(name); 102 } 103 } 104 105 /** 106 * Get or create a {@link SourceFolder} with the given name. 107 * @param name the name of the folder 108 * @return an existing or newly added {@link SourceFolder} 109 */ 110 protected final SourceFolder getOrCreateSourceFolder(String name) { 111 SourceFolder sourceFolder = this.sourceFolders.get(name); 112 if (sourceFolder == null) { 113 sourceFolder = new SourceFolder(name); 114 this.sourceFolders.put(name, sourceFolder); 115 } 116 return sourceFolder; 117 } 118 119 /** 120 * Return all {@link SourceFolder SourceFolders} that have been added to the 121 * collection. 122 * @return a collection of {@link SourceFolder} items 123 */ 124 public Collection<SourceFolder> getSourceFolders() { 125 return Collections.unmodifiableCollection(this.sourceFolders.values()); 126 } 127 128 /** 129 * Return the size of the collection. 130 * @return the size of the collection 131 */ 132 public int size() { 133 int size = 0; 134 for (SourceFolder sourceFolder : this.sourceFolders.values()) { 135 size += sourceFolder.getFiles().size(); 136 } 137 return size; 138 } 139 140 @Override 141 public ClassLoaderFile getFile(String name) { 142 for (SourceFolder sourceFolder : this.sourceFolders.values()) { 143 ClassLoaderFile file = sourceFolder.get(name); 144 if (file != null) { 145 return file; 146 } 147 } 148 return null; 149 } 150 151 /** 152 * An individual source folder that is being managed by the collection. 153 */ 154 public static class SourceFolder implements Serializable { 155 156 private static final long serialVersionUID = 1; 157 158 private final String name; 159 160 private final Map<String, ClassLoaderFile> files = new LinkedHashMap<>(); 161 162 SourceFolder(String name) { 163 this.name = name; 164 } 165 166 public Set<Entry<String, ClassLoaderFile>> getFilesEntrySet() { 167 return this.files.entrySet(); 168 } 169 170 protected final void add(String name, ClassLoaderFile file) { 171 this.files.put(name, file); 172 } 173 174 protected final void remove(String name) { 175 this.files.remove(name); 176 } 177 178 protected final ClassLoaderFile get(String name) { 179 return this.files.get(name); 180 } 181 182 /** 183 * Return the name of the source folder. 184 * @return the name of the source folder 185 */ 186 public String getName() { 187 return this.name; 188 } 189 190 /** 191 * Return all {@link ClassLoaderFile ClassLoaderFiles} in the collection that are 192 * contained in this source folder. 193 * @return the files contained in the source folder 194 */ 195 public Collection<ClassLoaderFile> getFiles() { 196 return Collections.unmodifiableCollection(this.files.values()); 197 } 198 199 } 200 201}