/* * Copyright 2012 Red Hat, Inc. and/or its affiliates. * * Licensed 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.guvnor.m2repo.backend.server; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.List; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import org.drools.compiler.kproject.xml.MinimalPomParser; import org.drools.compiler.kproject.xml.PomModel; import org.eclipse.aether.artifact.Artifact; import org.guvnor.common.services.project.model.GAV; import org.guvnor.m2repo.model.JarListPageRequest; import org.guvnor.m2repo.model.JarListPageRow; import org.guvnor.m2repo.service.M2RepoService; import org.jboss.errai.bus.server.annotations.Service; import org.uberfire.paging.PageResponse; @Service @ApplicationScoped // Implementation needs to implement both interfaces even though one extends the other // otherwise the implementation discovery mechanism for the @Service annotation fails. public class M2RepoServiceImpl implements M2RepoService, ExtendedM2RepoService { @Inject private GuvnorM2Repository repository; @Override public void deployJar(final InputStream is, final GAV gav) { repository.deployArtifact(is, gav, true); } @Override public void deployJarInternal(final InputStream is, final GAV gav) { repository.deployArtifact(is, gav, false); } @Override public void deployPom(final InputStream is, final GAV gav) { repository.deployPom(is, gav); } @Override public String getPomText(final String path) { checkPathTraversal(path); return GuvnorM2Repository.getPomText(path); } @Override public GAV loadGAVFromJar(final String path) { checkPathTraversal(path); final GAV gav = repository.loadGAVFromJar(path); return gav; } @Override public PageResponse<JarListPageRow> listArtifacts(final JarListPageRequest pageRequest) { //Get unsorted files matching filter final String filters = pageRequest.getFilters(); final List<String> fileFormats = pageRequest.getFileFormats(); final String dataSourceName = pageRequest.getDataSourceName(); final boolean isAscending = pageRequest.isAscending(); final Collection<Artifact> files = repository.listArtifacts(filters, fileFormats); //Convert files to JarListPageRow final List<JarListPageRow> jarPageRowList = new ArrayList<JarListPageRow>(); for (Artifact artifact : files) { final File file = artifact.getFile(); JarListPageRow jarListPageRow = new JarListPageRow(); jarListPageRow.setName(file.getName()); jarListPageRow.setPath(getJarPath(file.getPath(), File.separator)); jarListPageRow.setGav(getGAV(jarListPageRow.getPath())); jarListPageRow.setLastModified(new Date(file.lastModified())); jarListPageRow.setRepositoryName(artifact.getProperty("repository", "undefined")); jarPageRowList.add(jarListPageRow); } //Sort JarListPageRow entries, if required if (dataSourceName != null) { final int order = (isAscending ? 1 : -1); if (dataSourceName.equals(JarListPageRequest.COLUMN_NAME)) { Collections.sort(jarPageRowList, new Comparator<JarListPageRow>() { @Override public int compare(final JarListPageRow o1, final JarListPageRow o2) { return o1.getName().compareTo(o2.getName()) * order; } }); } else if (dataSourceName.equals(JarListPageRequest.COLUMN_PATH)) { Collections.sort(jarPageRowList, new Comparator<JarListPageRow>() { @Override public int compare(final JarListPageRow o1, final JarListPageRow o2) { return o1.getPath().compareTo(o2.getPath()) * order; } }); } else if (dataSourceName.equals(JarListPageRequest.COLUMN_GAV)) { Collections.sort(jarPageRowList, new Comparator<JarListPageRow>() { @Override public int compare(final JarListPageRow o1, final JarListPageRow o2) { final GAV gav1 = o1.getGav(); final GAV gav2 = o2.getGav(); return gav1.toString().compareToIgnoreCase(gav2.toString()) * order; } }); } else if (dataSourceName.equals(JarListPageRequest.COLUMN_LAST_MODIFIED)) { Collections.sort(jarPageRowList, new Comparator<JarListPageRow>() { @Override public int compare(final JarListPageRow o1, final JarListPageRow o2) { final Long ft1 = o1.getLastModified().getTime(); final Long ft2 = o2.getLastModified().getTime(); return ft1.compareTo(ft2) * order; } }); } } //Copy request "page" of entries to response final Integer pageSize = pageRequest.getPageSize(); final int startRowIndex = pageRequest.getStartRowIndex(); final int endRowIndex = Math.min(jarPageRowList.size(), (pageSize == null ? jarPageRowList.size() : startRowIndex + pageSize)); final List<JarListPageRow> responsePageRowList = new ArrayList<JarListPageRow>(); if (startRowIndex < jarPageRowList.size()) { int i = startRowIndex; while (i < endRowIndex && i < jarPageRowList.size()) { responsePageRowList.add(jarPageRowList.get(i)); i++; } } final PageResponse<JarListPageRow> response = new PageResponse<JarListPageRow>(); response.setPageRowList(responsePageRowList); response.setStartRowIndex(pageRequest.getStartRowIndex()); response.setTotalRowSize(files.size()); response.setTotalRowSizeExact(true); return response; } // The file separator is provided as a parameter so that we can test for correct JAR path creation on both // Windows and Linux based Operating Systems in Unit tests running on either platform. See JarPathTest. String getJarPath(final String path, final String separator) { //Strip "Repository" prefix String jarPath = path.substring(GuvnorM2Repository.M2_REPO_DIR.length() + 1); //Replace OS-dependent file separators with HTTP path separators jarPath = jarPath.replaceAll("\\" + separator, "/"); return jarPath; } GAV getGAV(final String path) { GAV gav = null; InputStream is = null; try { final String pom = getPomText(path); is = new ByteArrayInputStream(pom.getBytes(Charset.forName("UTF-8"))); final PomModel model = MinimalPomParser.parse(path, is); gav = new GAV(model.getReleaseId().getGroupId(), model.getReleaseId().getArtifactId(), model.getReleaseId().getVersion()); } catch (RuntimeException rte) { //RuntimeException is thrown by MinimalPomParser for any Exception.. gav = new GAV("<undetermined>", "<undetermined>", "<undetermined>"); } finally { if (is != null) { try { is.close(); } catch (IOException ioe) { //Swallow } } } return gav; } /** * @param baseURL the base URL where Guvnor M2 repo is hosted in web container. return a Guvnor M2 repo * URL point to local file system if baseURL is not available. * @return String */ @Override public String getRepositoryURL(final String baseURL) { if (baseURL == null || baseURL.isEmpty()) { return repository.getRepositoryURL(); } else { if (baseURL.endsWith("/")) { return baseURL + "maven2/"; } else { return baseURL + "/maven2/"; } } } /** * Asserts that the path does not cause traversal. * @param path the path to check, must not be null. */ private void checkPathTraversal(String path) { // There's no more decoding / unescaping happening on the String beyond // this point so we don't have to check for %u002e, %252e, etc., as these // paths would result in a FileNotFoundException anyway. if (path.contains("..")) { throw new RuntimeException("Invalid path provided!"); } } }