/******************************************************************************* * Copyright (c) 2006-2007, Cloudsmith Inc. * The code, documentation and other materials contained herein have been * licensed under the Eclipse Public License - v 1.0 by the copyright holder * listed above, as the Initial Contributor under such license. The text of * such license is available at www.eclipse.org. ******************************************************************************/ package org.eclipse.buckminster.jnlp.p2; import static org.eclipse.buckminster.jnlp.p2.MaterializationConstants.DEFAULT_MATERIALIZATION_FOLDER; import static org.eclipse.buckminster.jnlp.p2.MaterializationConstants.ERROR_CODE_403_EXCEPTION; import static org.eclipse.buckminster.jnlp.p2.MaterializationConstants.ERROR_CODE_404_EXCEPTION; import static org.eclipse.buckminster.jnlp.p2.MaterializationConstants.ERROR_CODE_500_EXCEPTION; import static org.eclipse.buckminster.jnlp.p2.MaterializationConstants.ERROR_CODE_ARTIFACT_EXCEPTION; import static org.eclipse.buckminster.jnlp.p2.MaterializationConstants.ERROR_CODE_FILE_IO_EXCEPTION; import static org.eclipse.buckminster.jnlp.p2.MaterializationConstants.ERROR_CODE_REMOTE_IO_EXCEPTION; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLEncoder; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.httpclient.HttpStatus; import org.eclipse.buckminster.core.RMContext; import org.eclipse.buckminster.core.common.model.ExpandingProperties; import org.eclipse.buckminster.core.cspec.ICSpecData; import org.eclipse.buckminster.core.metadata.IResolution; import org.eclipse.buckminster.core.metadata.model.BOMNode; import org.eclipse.buckminster.core.metadata.model.BillOfMaterials; import org.eclipse.buckminster.core.mspec.builder.MaterializationNodeBuilder; import org.eclipse.buckminster.core.mspec.builder.MaterializationSpecBuilder; import org.eclipse.buckminster.jnlp.distroprovider.IRemoteDistroProvider; import org.eclipse.buckminster.runtime.BuckminsterException; import org.eclipse.buckminster.runtime.IOUtils; import org.eclipse.buckminster.sax.Utils; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.swt.graphics.Image; import org.xml.sax.SAXException; /** * @author Karel Brezina * */ public class MaterializationUtils { static class PropertyEntryByLength implements Comparable<PropertyEntryByLength> { private String m_key; private String m_value; public PropertyEntryByLength(String key, String value) { m_key = key; m_value = value; } public int compareTo(PropertyEntryByLength o) { int result = o.getKey().length() - m_key.length(); if(result != 0) return result; return m_key.compareTo(o.getKey()); } public String getKey() { return m_key; } public String getValue() { return m_value; } } private static Map<MaterializationSpecBuilder, Map<String, ? extends Object>> m_expandProperties = new HashMap<MaterializationSpecBuilder, Map<String, ? extends Object>>(); private static Map<MaterializationSpecBuilder, Set<PropertyEntryByLength>> m_generalizeProperties = new HashMap<MaterializationSpecBuilder, Set<PropertyEntryByLength>>(); private static final Map<String, String> s_humanReadableComponentTypes; // needs to be synchronized with the server side, new component types need to be added static { s_humanReadableComponentTypes = new HashMap<String, String>(); s_humanReadableComponentTypes.put(null, "Any"); s_humanReadableComponentTypes.put("unknown", "None"); s_humanReadableComponentTypes.put("cssite", "Cloudsmith"); s_humanReadableComponentTypes.put("osgi.bundle", "OSGi Bundle"); s_humanReadableComponentTypes.put("eclipse.feature", "Eclipse Feature"); s_humanReadableComponentTypes.put("maven", "Maven"); s_humanReadableComponentTypes.put("site.feature", "Eclipse Update Manager"); s_humanReadableComponentTypes.put("buckminster", "Buckminster"); s_humanReadableComponentTypes.put("jar", "Java Archive"); s_humanReadableComponentTypes.put("bom", "Bill of Materials"); } /** * Checks HTTP response code and throws JNLPException if there is a problem * * @param connection * @throws JNLPException * @throws IOException */ public static void checkConnection(int status, String originalURL) throws JNLPException { if(status != HttpStatus.SC_OK) { String errorCode; switch(status) { case HttpStatus.SC_FORBIDDEN: errorCode = ERROR_CODE_403_EXCEPTION; break; case HttpStatus.SC_NOT_FOUND: errorCode = ERROR_CODE_404_EXCEPTION; break; case HttpStatus.SC_INTERNAL_SERVER_ERROR: errorCode = ERROR_CODE_500_EXCEPTION; break; default: errorCode = ERROR_CODE_REMOTE_IO_EXCEPTION; break; } throw new JNLPException("Cannot read materialization specification", errorCode, BuckminsterException.fromMessage("%s - %s", originalURL, HttpStatus.getStatusText(status))); } } /** * Checks response of IAuthenticator.register method * * @param result * result of IAuthenticator.register method * @throws JNLPException */ public static void checkRegistrationResponse(int result) throws JNLPException { switch(result) { case IRemoteDistroProvider.REGISTER_FAIL: throw new JNLPException("Registration was not successful", null); case IRemoteDistroProvider.REGISTER_LOGIN_EXISTS: throw new JNLPException("Login name already exists - choose a different one", null); case IRemoteDistroProvider.REGISTER_LOGIN_TOO_SHORT: throw new JNLPException("Login is too short - length must be between 3 and 25", null); case IRemoteDistroProvider.REGISTER_LOGIN_CONTAINS_AT: throw new JNLPException("Login name contains '@'", null); case IRemoteDistroProvider.REGISTER_LOGIN_INVALID: throw new JNLPException("Login name is invalid", null); case IRemoteDistroProvider.REGISTER_PASSWORD_TOO_SHORT: throw new JNLPException("Password is too short - length must be between 4 and 25", null); case IRemoteDistroProvider.REGISTER_EMAIL_FORMAT_ERROR: throw new JNLPException("Email does not have standard format", null); case IRemoteDistroProvider.REGISTER_EMAIL_ALREADY_VALIDATED: throw new JNLPException("Email is already verified for another user", null); } } public static String createMailtoURL(String to, String subject, String body) { StringBuilder sb = new StringBuilder(); sb.append("mailto:"); sb.append(mailtoEncode(to)); sb.append("?subject="); sb.append(mailtoEncode(subject)); sb.append("&body="); sb.append(mailtoEncode(body)); return sb.toString(); } public static String createStatusMessage(IStatus status) { final String NEW_LINE_SEPARATOR = "\n"; String[] systemProps = { "os.name", "os.arch", "user.country", "java.vendor", "java.version" }; StringBuilder messageBuilder = new StringBuilder(); messageBuilder.append("Environment:"); messageBuilder.append(NEW_LINE_SEPARATOR); for(String prop : systemProps) { messageBuilder.append(prop); messageBuilder.append("="); messageBuilder.append(System.getProperty(prop)); messageBuilder.append(NEW_LINE_SEPARATOR); } messageBuilder.append(NEW_LINE_SEPARATOR); messageBuilder.append("Status Message:"); messageBuilder.append(NEW_LINE_SEPARATOR); messageBuilder.append(status.getMessage()); messageBuilder.append(NEW_LINE_SEPARATOR); messageBuilder.append(NEW_LINE_SEPARATOR); Throwable throwable = status.getException(); if(throwable != null) { messageBuilder.append("Stack Trace:"); messageBuilder.append(NEW_LINE_SEPARATOR); StringWriter stackTrace = new StringWriter(); PrintWriter stackTraceWriter = null; try { stackTraceWriter = new PrintWriter(stackTrace); throwable.printStackTrace(stackTraceWriter); } finally { if(stackTraceWriter != null) { stackTraceWriter.flush(); stackTraceWriter.close(); } } messageBuilder.append(stackTrace); } return messageBuilder.toString(); } /** * Excludes CSSite components * * @param mspec * @param depNode * @throws CoreException */ public static void excludeCSsiteComponents(MaterializationSpecBuilder mspec, BOMNode depNode) throws CoreException { if(hasCSsiteReader(depNode)) excludeComponent(mspec, depNode); for(BOMNode childDepNode : depNode.getChildren()) excludeCSsiteComponents(mspec, childDepNode); } /** * Expands <code>IPath</code> that contains properties * * @param mspec * @param installLocation * @return */ public static IPath expandPath(MaterializationSpecBuilder mspec, IPath installLocation) { Map<String, ? extends Object> properties = m_expandProperties.get(mspec); if(properties == null) { properties = new RMContext(mspec.getProperties()); m_expandProperties.put(mspec, properties); } return Path.fromOSString(ExpandingProperties.expand(properties, installLocation.toOSString(), 0)); } /** * Generalize <code>IPath</code> * * @param mspec * @param installLocation * @return */ public static IPath generalizePath(MaterializationSpecBuilder mspec, IPath installLocation) { if(installLocation == null) return null; Set<PropertyEntryByLength> properties = m_generalizeProperties.get(mspec); if(properties == null) { properties = new TreeSet<PropertyEntryByLength>(); RMContext context = new RMContext(mspec.getProperties()); for(String key : context.keySet()) { String value = (String)context.get(key); if(value == null || value.length() == 0) continue; // unifying file separators String unifiedValue = new Path(value).removeTrailingSeparator().toString(); // changing meaning - key is value, value is key properties.add(new PropertyEntryByLength(unifiedValue, key)); } m_generalizeProperties.put(mspec, properties); } String pathToGeneralize = installLocation.addTrailingSeparator().toString(); int len = pathToGeneralize.length(); for(PropertyEntryByLength entry : properties) { String key = entry.getKey(); if(key.length() > len) continue; Pattern pattern = Pattern.compile("(^|/)(" + Pattern.quote(key) + ")/"); Matcher matcher = pattern.matcher(pathToGeneralize); pathToGeneralize = matcher.replaceAll("$1\\${" + entry.getValue() + "}/"); } return new Path(pathToGeneralize); } public static File getBackupFolder(File eclipseFolder) { String backupString = eclipseFolder.getPath() + ".backup"; File backupFile = new File(backupString); int i = 0; while(backupFile.exists()) { backupFile = new File(backupString + String.format(".%d", Integer.valueOf(i++))); } return backupFile; } /** * Gets default install location * * @param artifactName * @return * @throws JNLPException */ public static String getDefaultDestination(String artifactName) throws JNLPException { String destination = null; String userHome = System.getProperty("user.home"); if(userHome != null) { destination = userHome + File.separator + DEFAULT_MATERIALIZATION_FOLDER; if(artifactName != null) destination += File.separator + artifactName; } return destination; } /** * Gets human readable component type * * @param componentType * componentType ID * @return human readable component type */ public static String getHumanReadableComponentType(String componentType) { String hrType = s_humanReadableComponentTypes.get(componentType); if(hrType == null) hrType = componentType; return hrType; } public static Image getImage(String imageName) { Class<?> myClass = MaterializationUtils.class; String imageResource = "/icons/" + imageName; URL imageUrl = myClass.getResource(imageResource); return ImageDescriptor.createFromURL(imageUrl).createImage(); } /** * From a given path computes a path that exists in the file system. * * @param enteredPath * entered path * @return path that exists in the file system */ public static String getKnownPath(String enteredPath) { IPath path = new Path(enteredPath); File file = null; String pathString = null; do { // second and other runs - remove last segment if(file != null) path = path.removeLastSegments(1); pathString = path.removeTrailingSeparator().toOSString(); file = new File(pathString); } while(!file.exists() && pathString.length() > 0); if(!file.isDirectory()) return null; return pathString; } public static String mailtoEncode(String input) { String output; try { output = URLEncoder.encode(input, "UTF-8"); } catch(UnsupportedEncodingException e) { throw new RuntimeException(e); } return output.replace("+", "%20"); } public static void saveBOM(BillOfMaterials bom, File file) { OutputStream os = null; try { os = new FileOutputStream(file); Utils.serialize(bom, os); } catch(FileNotFoundException e1) { throw new JNLPException("File cannot be opened or created", ERROR_CODE_FILE_IO_EXCEPTION, e1); } catch(SAXException e1) { throw new JNLPException("Unable to read BOM specification", ERROR_CODE_ARTIFACT_EXCEPTION, e1); } finally { IOUtils.close(os); } } private static void excludeComponent(MaterializationSpecBuilder mspec, BOMNode depNode) throws CoreException { IResolution resolution = depNode.getResolution(); if(resolution != null) { ICSpecData cspec = resolution.getCSpec(); if(cspec != null) { String componentName = cspec.getName(); String componentType = cspec.getComponentTypeID(); for(MaterializationNodeBuilder builder : mspec.getNodeBuilders()) if((componentType == null || componentType.equals(builder.getComponentTypeID())) && builder.getNamePattern().matcher(componentName).matches()) builder.setExclude(true); MaterializationNodeBuilder nodeBuilder = mspec.addNodeBuilder(); nodeBuilder.setNamePattern(Pattern.compile("^\\Q" + componentName + "\\E$")); nodeBuilder.setComponentTypeID(componentType); nodeBuilder.setExclude(true); } } } private static boolean hasCSsiteReader(BOMNode depNode) throws CoreException { IResolution resolution = depNode.getResolution(); if(resolution != null) if(MaterializationConstants.READER_TYPE_CSSITE.equals(resolution.getProvider().getReaderTypeId())) return true; return false; } }