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