001/* 002 Copyright 2010-2016 Boxfuse GmbH 003 <p/> 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 <p/> 008 http://www.apache.org/licenses/LICENSE-2.0 009 <p/> 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 */ 016package io.avaje.classpath.scanner.internal.scanner.classpath; 017 018import org.osgi.framework.Bundle; 019import org.osgi.framework.FrameworkUtil; 020 021import java.net.URL; 022import java.util.Enumeration; 023import java.util.Set; 024import java.util.TreeSet; 025import java.util.regex.Matcher; 026import java.util.regex.Pattern; 027 028/** 029 * OSGi specific scanner that performs the migration search in 030 * the current bundle's classpath. 031 * 032 * <p> 033 * The resources that this scanner returns can only be loaded if 034 * Flyway's ClassLoader has access to the bundle that contains the migrations. 035 * </p> 036 */ 037public class OsgiClassPathLocationScanner implements ClassPathLocationScanner { 038 039 //Felix and Equinox "host" resource url pattern starts with bundleId, which is 040 // long according osgi core specification 041 private static final Pattern bundleIdPattern = Pattern.compile("^\\d+"); 042 043 public Set<String> findResourceNames(String location, URL locationUrl) { 044 Set<String> resourceNames = new TreeSet<>(); 045 046 Bundle bundle = targetBundleOrCurrent(FrameworkUtil.getBundle(getClass()), locationUrl); 047 @SuppressWarnings({"unchecked"}) 048 Enumeration<URL> entries = bundle.findEntries(locationUrl.getPath(), "*", true); 049 if (entries != null) { 050 while (entries.hasMoreElements()) { 051 resourceNames.add(pathWithoutLeadingSlash(entries.nextElement())); 052 } 053 } 054 return resourceNames; 055 } 056 057 private Bundle targetBundleOrCurrent(Bundle currentBundle, URL locationUrl) { 058 try { 059 Bundle targetBundle = currentBundle.getBundleContext().getBundle(bundleId(locationUrl.getHost())); 060 return targetBundle != null ? targetBundle : currentBundle; 061 } catch (Exception e) { 062 return currentBundle; 063 } 064 } 065 066 private long bundleId(String host) { 067 final Matcher matcher = bundleIdPattern.matcher(host); 068 if (matcher.find()) { 069 return Double.valueOf(matcher.group()).longValue(); 070 } 071 throw new IllegalArgumentException("There's no bundleId in passed URL"); 072 } 073 074 private String pathWithoutLeadingSlash(URL entry) { 075 final String path = entry.getPath(); 076 return path.startsWith("/") ? path.substring(1) : path; 077 } 078}