Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.karaf.cave.server.storage; import org.apache.commons.io.FileUtils; import org.apache.felix.bundlerepository.Resource; import org.apache.felix.bundlerepository.impl.DataModelHelperImpl; import org.apache.felix.bundlerepository.impl.RepositoryImpl; import org.apache.felix.bundlerepository.impl.ResourceImpl; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.karaf.cave.server.api.CaveRepository; import org.jsoup.Jsoup; import org.jsoup.UnsupportedMimeTypeException; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java.net.URL; /** * Default implementation of a Cave repository. */ public class CaveRepositoryImpl extends CaveRepository { private final static Logger LOGGER = LoggerFactory.getLogger(CaveRepositoryImpl.class); private RepositoryImpl obrRepository; public CaveRepositoryImpl(String name, String location, boolean scan) throws Exception { super(); this.setName(name); this.setLocation(location); this.createRepositoryDirectory(); if (scan) { this.scan(); } } /** * Check if the repository folder exists and create it if not. */ private void createRepositoryDirectory() throws Exception { LOGGER.debug("Create Cave repository {} folder.", this.getName()); File locationFile = new File(this.getLocation()); if (!locationFile.exists()) { locationFile.mkdirs(); LOGGER.debug("Cave repository {} location has been created.", this.getName()); LOGGER.debug(locationFile.getAbsolutePath()); } obrRepository = new RepositoryImpl(); obrRepository.setName(this.getName()); } /** * Generate the repository.xml with the artifact at the given URL. * * @throws Exception in case of repository.xml update failure. */ private void generateRepositoryXml() throws Exception { File repositoryXml = this.getRepositoryXmlFile(); OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(repositoryXml)); new DataModelHelperImpl().writeRepository(obrRepository, writer); writer.flush(); writer.close(); } /** * Add a resource in the OBR repository. * * @param resource the resource to add. * @throws Exception in case of failure. */ private void addResource(ResourceImpl resource) throws Exception { if (resource != null) { this.useResourceRelativeUri(resource); obrRepository.addResource(resource); obrRepository.setLastModified(System.currentTimeMillis()); } } /** * Upload an artifact from the given URL. * * @param url the URL of the artifact. * @throws Exception in case of upload failure. */ public void upload(URL url) throws Exception { LOGGER.debug("Upload new artifact from {}", url); String artifactName = "artifact-" + System.currentTimeMillis(); File temp = new File(new File(this.getLocation()), artifactName); FileUtils.copyURLToFile(url, temp); // update the repository.xml ResourceImpl resource = (ResourceImpl) new DataModelHelperImpl().createResource(temp.toURI().toURL()); if (resource == null) { temp.delete(); LOGGER.warn("The {} artifact source is not a valid OSGi bundle", url); throw new IllegalArgumentException( "The " + url.toString() + " artifact source is not a valid OSGi bundle"); } File destination = new File(new File(this.getLocation()), resource.getSymbolicName() + "-" + resource.getVersion() + ".jar"); if (destination.exists()) { temp.delete(); LOGGER.warn("The {} artifact is already present in the Cave repository", url); throw new IllegalArgumentException( "The " + url.toString() + " artifact is already present in the Cave repository"); } FileUtils.moveFile(temp, destination); resource = (ResourceImpl) new DataModelHelperImpl().createResource(destination.toURI().toURL()); this.addResource(resource); this.generateRepositoryXml(); } /** * Scan the content of the whole repository to update the repository.xml. * * @throws Exception in case of scan failure. */ public void scan() throws Exception { obrRepository = new RepositoryImpl(); obrRepository.setName(this.getName()); this.scan(new File(this.getLocation())); this.generateRepositoryXml(); } /** * Recursive method to traverse all files in the repository. * * @param entry the * @throws Exception */ private void scan(File entry) throws Exception { if (entry.isDirectory()) { File[] children = entry.listFiles(); for (int i = 0; i < children.length; i++) { scan(children[i]); } } else { // populate the repository try { URL bundleUrl = entry.toURI().toURL(); if (isPotentialBundle(bundleUrl.toString())) { ResourceImpl resource = (ResourceImpl) new DataModelHelperImpl().createResource(bundleUrl); this.addResource(resource); } } catch (IllegalArgumentException e) { LOGGER.warn(e.getMessage()); } } } /** * Convenience method to filter Maven files with common non-bundle extensions. * * @param bundleUrl the file URL to check. * @return true if the file is a potential bundle, false else. */ private boolean isPotentialBundle(String bundleUrl) { return !bundleUrl.matches(".*\\.sha1") && !bundleUrl.matches(".*\\.pom") && !bundleUrl.matches(".*\\.xml") && !bundleUrl.matches(".*\\.repositories") && !bundleUrl.matches(".*\\.properties") && !bundleUrl.matches(".*\\.lastUpdated"); } /** * Proxy an URL (by adding repository.xml OBR information) in the Cave repository. * * @param url the URL to proxyFilesystem. the URL to proxyFilesystem. * @param filter regex filter. Only artifacts URL matching the filter will be considered. * @throws Exception */ public void proxy(URL url, String filter) throws Exception { if (url.getProtocol().equals("file")) { // filesystem proxyFilesystem (to another folder) File proxyFolder = new File(url.toURI()); this.proxyFilesystem(proxyFolder, filter); } if (url.getProtocol().equals("http")) { // HTTP proxyFilesystem this.proxyHttp(url.toExternalForm(), filter); } this.generateRepositoryXml(); } /** * Proxy an URL (by adding repository.xml OBR information) in the Cave repository. * * @param url the URL to proxy. * @throws Exception */ public void proxy(URL url) throws Exception { this.proxy(url, null); } /** * Proxy a local filesystem (folder). * * @param entry the filesystem to proxyFilesystem. * @param filter regex filter. Only the artifacts URL matching the filter will be considered. * @throws Exception in case of proxyFilesystem failure */ private void proxyFilesystem(File entry, String filter) throws Exception { LOGGER.debug("Proxying filesystem {}", entry.getAbsolutePath()); if (entry.isDirectory()) { File[] children = entry.listFiles(); for (int i = 0; i < children.length; i++) { proxyFilesystem(children[i], filter); } } else { try { if ((filter == null) || (entry.toURI().toURL().toString().matches(filter))) { Resource resource = new DataModelHelperImpl().createResource(entry.toURI().toURL()); if (resource != null) { obrRepository.addResource(resource); obrRepository.setLastModified(System.currentTimeMillis()); } } } catch (IllegalArgumentException e) { LOGGER.warn(e.getMessage()); } } } /** * Proxy a HTTP URL locally. * * @param url the HTTP URL to proxy. * @param filter regex filter. Only artifacts URL matching the filter will be considered. * @throws Exception in case of proxy failure. */ private void proxyHttp(String url, String filter) throws Exception { LOGGER.debug("Proxying HTTP URL {}", url); HttpClient httpClient = new DefaultHttpClient(); HttpGet httpGet = new HttpGet(url); HttpResponse response = httpClient.execute(httpGet); HttpEntity entity = response.getEntity(); if (entity != null) { if (entity.getContentType().getValue().equals("application/java-archive") || entity.getContentType().getValue().equals("application/octet-stream")) { // I have a jar/binary, potentially a resource try { if ((filter == null) || (url.matches(filter))) { Resource resource = new DataModelHelperImpl().createResource(new URL(url)); if (resource != null) { obrRepository.addResource(resource); obrRepository.setLastModified(System.currentTimeMillis()); } } } catch (IllegalArgumentException e) { LOGGER.warn(e.getMessage()); } } else { // try to find link to "browse" try { Document document = Jsoup.connect(url).get(); Elements links = document.select("a"); if (links.size() > 1) { for (int i = 1; i < links.size(); i++) { Element link = links.get(i); String absoluteHref = link.attr("abs:href"); this.proxyHttp(absoluteHref, filter); } } } catch (UnsupportedMimeTypeException e) { // ignore } } } } /** * Populate an URL into the Cave repository, and eventually update the OBR information. * * @param url the URL to copy. * @param filter regex filter. Only artifacts URL matching the filter will be considered. * @param update if true the OBR information is updated, false else. * @throws Exception in case of populate failure. */ public void populate(URL url, String filter, boolean update) throws Exception { if (url.getProtocol().equals("file")) { // populate the Cave repository from a filesystem folder File populateFolder = new File(url.toURI()); this.populateFromFilesystem(populateFolder, filter, update); } if (url.getProtocol().equals("http")) { // populate the Cave repository from a HTTP URL this.populateFromHttp(url.toExternalForm(), filter, update); } if (update) { this.generateRepositoryXml(); } } /** * Populate an URL into the Cave repository, and eventually update the OBR information. * * @param url the URL to copy. * @param update if true the OBR information is updated, false else. * @throws Exception */ public void populate(URL url, boolean update) throws Exception { this.populate(url, null, update); } /** * Populate the Cave repository using a filesystem directory. * * @param filesystem the "source" directory. * @param filter regex filter. Only artifacts URL matching the filter will be considered. * @param update if true, the resources are added into the OBR metadata, false else. * @throws Exception in case of populate failure. */ private void populateFromFilesystem(File filesystem, String filter, boolean update) throws Exception { LOGGER.debug("Populating from filesystem {}", filesystem.getAbsolutePath()); if (filesystem.isDirectory()) { File[] children = filesystem.listFiles(); for (int i = 0; i < children.length; i++) { populateFromFilesystem(children[i], filter, update); } } else { try { if ((filter == null) || (filesystem.toURI().toURL().toString().matches(filter))) { ResourceImpl resource = (ResourceImpl) new DataModelHelperImpl() .createResource(filesystem.toURI().toURL()); if (resource != null) { // copy the resource File destination = new File(new File(this.getLocation()), filesystem.getName()); LOGGER.debug("Copy from {} to {}", filesystem.getAbsolutePath(), destination.getAbsolutePath()); FileUtils.copyFile(filesystem, destination); if (update) { resource = (ResourceImpl) new DataModelHelperImpl() .createResource(destination.toURI().toURL()); LOGGER.debug("Update the OBR metadata with {}", resource.getId()); this.addResource(resource); } } } } catch (IllegalArgumentException e) { LOGGER.warn(e.getMessage()); } } } /** * Populate the Cave repository using the given URL. * * @param url the "source" HTTP URL. * @param filter regex filter. Only artifacts URL matching the filter will be considered. * @param update true if the OBR metadata should be updated, false else. * @throws Exception in case of populate failure. */ private void populateFromHttp(String url, String filter, boolean update) throws Exception { LOGGER.debug("Populating from HTTP URL {}", url); HttpClient httpClient = new DefaultHttpClient(); HttpGet httpGet = new HttpGet(url); HttpResponse response = httpClient.execute(httpGet); HttpEntity entity = response.getEntity(); if (entity != null) { if (entity.getContentType().getValue().equals("application/java-archive") || entity.getContentType().getValue().equals("application/octet-stream")) { // I have a jar/binary, potentially a resource try { if ((filter == null) || (url.matches(filter))) { ResourceImpl resource = (ResourceImpl) new DataModelHelperImpl() .createResource(new URL(url)); if (resource != null) { LOGGER.debug("Copy {} into the Cave repository storage", url); int index = url.lastIndexOf("/"); if (index > 0) { url = url.substring(index); } File destination = new File(new File(this.getLocation()), url); FileOutputStream outputStream = new FileOutputStream(destination); entity.writeTo(outputStream); outputStream.flush(); outputStream.close(); if (update) { resource = (ResourceImpl) new DataModelHelperImpl() .createResource(destination.toURI().toURL()); LOGGER.debug("Update OBR metadata with {}", resource.getId()); this.addResource(resource); } } } } catch (IllegalArgumentException e) { LOGGER.warn(e.getMessage()); } } else { // try to find link to "browse" Document document = Jsoup.connect(url).get(); Elements links = document.select("a"); if (links.size() > 1) { for (int i = 1; i < links.size(); i++) { Element link = links.get(i); String absoluteHref = link.attr("abs:href"); this.populateFromHttp(absoluteHref, filter, update); } } } } } /** * Convert the Resource absolute URI to an URI relative to the repository one. * * @param resource the Resource to manipulate. * @throws Exception in cave of URI conversion failure. */ private void useResourceRelativeUri(ResourceImpl resource) throws Exception { String resourceURI = resource.getURI(); String locationURI = "file:" + this.getLocation(); LOGGER.debug("Converting resource URI {} relatively to repository URI {}", resourceURI, locationURI); if (resourceURI.startsWith(locationURI)) { resourceURI = resourceURI.substring(locationURI.length() + 1); LOGGER.debug("Resource URI converted to " + resourceURI); resource.put(Resource.URI, resourceURI); } } /** * Get the File object of the OBR repository.xml file. * * @return the File corresponding to the OBR repository.xml. * @throws Exception */ private File getRepositoryXmlFile() throws Exception { return new File(new File(this.getLocation()), "repository.xml"); } public void getResourceByUri(String uri) { // construct the file starting from the repository URI } /** * Return the OBR repository.xml corresponding to this Cave repository. * * @return the URL of the OBR repository.xml. * @throws Exception in case of lookup failure. */ public URL getRepositoryXml() throws Exception { File repositoryXml = this.getRepositoryXmlFile(); return repositoryXml.toURI().toURL(); } /** * Delete the repository storage folder. * * @throws Exception in case of destroy failure. */ public void cleanup() throws Exception { FileUtils.deleteDirectory(new File(this.getLocation())); } }