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 = getTargetBundleOrCurrent(FrameworkUtil.getBundle(getClass()), locationUrl); 047 @SuppressWarnings({"unchecked"}) 048 Enumeration<URL> entries = bundle.findEntries(locationUrl.getPath(), "*", true); 049 050 if (entries != null) { 051 while (entries.hasMoreElements()) { 052 URL entry = entries.nextElement(); 053 String resourceName = getPathWithoutLeadingSlash(entry); 054 055 resourceNames.add(resourceName); 056 } 057 } 058 059 return resourceNames; 060 } 061 062 private Bundle getTargetBundleOrCurrent(Bundle currentBundle, URL locationUrl) { 063 try { 064 Bundle targetBundle = currentBundle.getBundleContext().getBundle(getBundleId(locationUrl.getHost())); 065 return targetBundle != null ? targetBundle : currentBundle; 066 } catch (Exception e) { 067 return currentBundle; 068 } 069 } 070 071 private long getBundleId(String host) { 072 final Matcher matcher = bundleIdPattern.matcher(host); 073 if (matcher.find()) { 074 return Double.valueOf(matcher.group()).longValue(); 075 } 076 throw new IllegalArgumentException("There's no bundleId in passed URL"); 077 } 078 079 private String getPathWithoutLeadingSlash(URL entry) { 080 final String path = entry.getPath(); 081 082 return path.startsWith("/") ? path.substring(1) : path; 083 } 084}