Java tutorial
/* * Copyright (c) 2010-2013 Evolveum * * 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 com.evolveum.midpoint.model.common.expression.functions; import java.io.File; import java.io.IOException; import java.text.Normalizer; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; import javax.xml.datatype.XMLGregorianCalendar; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.time.DateUtils; import org.w3c.dom.Element; import org.w3c.dom.Node; import com.evolveum.midpoint.model.common.expression.script.ScriptExpression; import com.evolveum.midpoint.model.common.expression.script.ScriptExpressionEvaluationContext; import com.evolveum.midpoint.prism.PrismContainer; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismProperty; import com.evolveum.midpoint.prism.crypto.EncryptionException; import com.evolveum.midpoint.prism.crypto.Protector; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.polystring.PolyString; import com.evolveum.midpoint.prism.xml.XmlTypeConverter; import com.evolveum.midpoint.schema.constants.MidPointConstants; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.processor.ResourceAttribute; import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.schema.util.ShadowUtil; import com.evolveum.midpoint.util.DOMUtil; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.exception.SystemException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType; /** * Library of standard midPoint functions. These functions are made available to all * midPoint expressions. * * The functions should be written to support scripting-like comfort. It means that they all needs * to be null-safe, automatically convert data types as necessary and so on. * * @author Radovan Semancik * */ public class BasicExpressionFunctions { public static final String NAME_SEPARATOR = " "; public static final Trace LOGGER = TraceManager.getTrace(BasicExpressionFunctions.class); private PrismContext prismContext; private Protector protector; public BasicExpressionFunctions(PrismContext prismContext, Protector protector) { super(); this.prismContext = prismContext; this.protector = protector; } /** * Convert string to lower case. */ public static String lc(String orig) { return StringUtils.lowerCase(orig); } /** * Convert string to upper case. */ public static String uc(String orig) { return StringUtils.upperCase(orig); } /** * Remove whitespaces at the beginning and at the end of the string. */ public static String trim(String orig) { return StringUtils.trim(orig); } /** * Concatenates the arguments to create a name. * Each argument is stringified, trimmed and the result is concatenated by spaces. */ public String concatName(Object... components) { if (components == null || components.length == 0) { return ""; } boolean endsWithSeparator = false; StringBuilder sb = new StringBuilder(); for (int i = 0; i < components.length; i++) { Object component = components[i]; if (component == null) { continue; } String stringComponent = stringify(component); if (stringComponent == null) { continue; } String trimmedStringComponent = trim(stringComponent); if (trimmedStringComponent.isEmpty()) { continue; } sb.append(trimmedStringComponent); if (i < (components.length - 1)) { sb.append(NAME_SEPARATOR); endsWithSeparator = true; } else { endsWithSeparator = false; } } if (endsWithSeparator) { sb.delete(sb.length() - NAME_SEPARATOR.length(), sb.length()); } return sb.toString(); } /** * Normalize a string value. It follows the default normalization algorithm * used for PolyString values. * * @param orig original value to normalize * @return normalized value */ public String norm(String orig) { if (orig == null) { return null; } PolyString polyString = new PolyString(orig); polyString.recompute(prismContext.getDefaultPolyStringNormalizer()); return polyString.getNorm(); } /** * Normalize a PolyString value. * * @param orig original value to normalize * @return normalized value */ public String norm(PolyString orig) { if (orig == null) { return null; } if (orig.getNorm() != null) { return orig.getNorm(); } orig.recompute(prismContext.getDefaultPolyStringNormalizer()); return orig.getNorm(); } /** * Normalize a PolyStringType value. * * @param orig original value to normalize * @return normalized value */ public String norm(PolyStringType orig) { if (orig == null) { return null; } PolyString polyString = orig.toPolyString(); return norm(polyString); } public String toAscii(Object input) { if (input == null) { return null; } String inputString = stringify(input); String decomposed = Normalizer.normalize(inputString, Normalizer.Form.NFKD); return decomposed.replaceAll("\\p{M}", ""); } /** * Converts whatever it gets to a string. But it does it in a sensitive way. * E.g. it tries to detect collections and returns the first element (if there is only one). * Never returns null. Returns empty string instead. */ public String stringify(Object whatever) { if (whatever == null) { return ""; } if (whatever instanceof String) { return (String) whatever; } if (whatever instanceof PolyString) { return ((PolyString) whatever).getOrig(); } if (whatever instanceof PolyStringType) { return ((PolyStringType) whatever).getOrig(); } if (whatever instanceof Collection) { Collection collection = (Collection) whatever; if (collection.isEmpty()) { return ""; } if (collection.size() > 1) { throw new IllegalArgumentException( "Cannot stringify collection because it has " + collection.size() + " values"); } whatever = collection.iterator().next(); } Class<? extends Object> whateverClass = whatever.getClass(); if (whateverClass.isArray()) { Object[] array = (Object[]) whatever; if (array.length == 0) { return ""; } if (array.length > 1) { throw new IllegalArgumentException( "Cannot stringify array because it has " + array.length + " values"); } whatever = array[0]; } if (whatever == null) { return ""; } if (whatever instanceof String) { return (String) whatever; } if (whatever instanceof PolyString) { return ((PolyString) whatever).getOrig(); } if (whatever instanceof PolyStringType) { return ((PolyStringType) whatever).getOrig(); } if (whatever instanceof Element) { Element element = (Element) whatever; Element origElement = DOMUtil.getChildElement(element, PolyString.F_ORIG); if (origElement != null) { // This is most likely a PolyStringType return origElement.getTextContent(); } else { return element.getTextContent(); } } if (whatever instanceof Node) { return ((Node) whatever).getTextContent(); } return whatever.toString(); } public Collection<String> getOids(Collection<ObjectReferenceType> refs) { if (refs == null) { return null; } Collection<String> oids = new ArrayList<String>(); for (ObjectReferenceType ort : refs) { if (StringUtils.isNotBlank(ort.getOid())) { oids.add(ort.getOid()); } else if (ort.asReferenceValue().getObject() != null && StringUtils.isNotBlank(ort.asReferenceValue().getObject().getOid())) { oids.add(ort.asReferenceValue().getObject().getOid()); } } return oids; } public Collection<String> getOids(ObjectReferenceType refs) { List<ObjectReferenceType> refList = new ArrayList<>(); refList.add(refs); return getOids(refList); } public Collection<String> getOids(ObjectType refs) { List<String> oid = new ArrayList<>(); oid.add(refs.getOid()); return oid; } public boolean isEmpty(Object whatever) { if (whatever == null) { return true; } if (whatever instanceof String) { return ((String) whatever).isEmpty(); } if (whatever instanceof Collection) { return ((Collection) whatever).isEmpty(); } String whateverString = stringify(whatever); if (whateverString == null) { return true; } return whateverString.isEmpty(); } public <T> Collection<T> getExtensionPropertyValues(ObjectType object, String namespace, String localPart) { return getExtensionPropertyValues(object, new javax.xml.namespace.QName(namespace, localPart)); } public <T> Collection<T> getExtensionPropertyValues(ObjectType object, groovy.xml.QName propertyQname) { return getExtensionPropertyValues(object, propertyQname.getNamespaceURI(), propertyQname.getLocalPart()); } public <T> Collection<T> getExtensionPropertyValues(ObjectType object, javax.xml.namespace.QName propertyQname) { return ObjectTypeUtil.getExtensionPropertyValuesNotNull(object, propertyQname); } public <T> T getExtensionPropertyValue(ObjectType object, String namespace, String localPart) throws SchemaException { return getExtensionPropertyValue(object, new javax.xml.namespace.QName(namespace, localPart)); } public <T> T getExtensionPropertyValue(ObjectType object, groovy.xml.QName propertyQname) throws SchemaException { return getExtensionPropertyValue(object, propertyQname.getNamespaceURI(), propertyQname.getLocalPart()); } public <T> T getExtensionPropertyValue(ObjectType object, javax.xml.namespace.QName propertyQname) throws SchemaException { if (object == null) { return null; } Collection<T> values = ObjectTypeUtil.getExtensionPropertyValues(object, propertyQname); return toSingle(values, "a multi-valued extension property " + propertyQname); } public <T> T getPropertyValue(ObjectType object, String path) throws SchemaException { Collection<T> values = getPropertyValues(object, path); return toSingle(values, "a multi-valued property " + path); } public <T> Collection<T> getPropertyValues(ObjectType object, String path) { if (object == null) { return null; } ScriptExpressionEvaluationContext scriptContext = ScriptExpressionEvaluationContext.getThreadLocal(); ScriptExpression scriptExpression = scriptContext.getScriptExpression(); ItemPath itemPath = scriptExpression.parsePath(path); PrismProperty property = object.asPrismObject().findProperty(itemPath); if (property == null) { return new ArrayList<T>(0); } return property.getRealValues(); } public <T> Collection<T> getAttributeValues(ShadowType shadow, String attributeNamespace, String attributeLocalPart) { return getAttributeValues(shadow, new javax.xml.namespace.QName(attributeNamespace, attributeLocalPart)); } public <T> Collection<T> getAttributeValues(ShadowType shadow, String attributeLocalPart) { return getAttributeValues(shadow, new javax.xml.namespace.QName(MidPointConstants.NS_RI, attributeLocalPart)); } public <T> Collection<T> getAttributeValues(ShadowType shadow, groovy.xml.QName attributeQname) { return getAttributeValues(shadow, attributeQname.getNamespaceURI(), attributeQname.getLocalPart()); } public <T> Collection<T> getAttributeValues(ShadowType shadow, javax.xml.namespace.QName attributeQname) { return ShadowUtil.getAttributeValues(shadow, attributeQname); } public <T> T getAttributeValue(ShadowType shadow, String attributeNamespace, String attributeLocalPart) throws SchemaException { return getAttributeValue(shadow, new javax.xml.namespace.QName(attributeNamespace, attributeLocalPart)); } public <T> T getAttributeValue(ShadowType shadow, String attributeLocalPart) throws SchemaException { return getAttributeValue(shadow, new javax.xml.namespace.QName(MidPointConstants.NS_RI, attributeLocalPart)); } public <T> T getAttributeValue(ShadowType shadow, groovy.xml.QName attributeQname) throws SchemaException { return getAttributeValue(shadow, attributeQname.getNamespaceURI(), attributeQname.getLocalPart()); } public <T> T getAttributeValue(ShadowType shadow, javax.xml.namespace.QName attributeQname) throws SchemaException { return ShadowUtil.getAttributeValue(shadow, attributeQname); } public Collection<String> getAttributeStringValues(ShadowType shadow, String attributeNamespace, String attributeLocalPart) { return getAttributeStringValues(shadow, new javax.xml.namespace.QName(attributeNamespace, attributeLocalPart)); } public Collection<String> getAttributeStringValues(ShadowType shadow, groovy.xml.QName attributeQname) { return getAttributeStringValues(shadow, attributeQname.getNamespaceURI(), attributeQname.getLocalPart()); } public Collection<String> getAttributeStringValues(ShadowType shadow, javax.xml.namespace.QName attributeQname) { return ShadowUtil.getAttributeValues(shadow, attributeQname, String.class); } public <T> T getIdentifierValue(ShadowType shadow) throws SchemaException { if (shadow == null) { return null; } Collection<ResourceAttribute<?>> identifiers = ShadowUtil.getIdentifiers(shadow); if (identifiers.size() == 0) { return null; } if (identifiers.size() > 1) { throw new SchemaException("More than one idenfier in " + shadow); } Collection<T> realValues = (Collection<T>) identifiers.iterator().next().getRealValues(); if (realValues.size() == 0) { return null; } if (realValues.size() > 1) { throw new SchemaException("More than one idenfier value in " + shadow); } return realValues.iterator().next(); } public <T> T getSecondaryIdentifierValue(ShadowType shadow) throws SchemaException { if (shadow == null) { return null; } Collection<ResourceAttribute<?>> identifiers = ShadowUtil.getSecondaryIdentifiers(shadow); if (identifiers.size() == 0) { return null; } if (identifiers.size() > 1) { throw new SchemaException("More than one secondary idenfier in " + shadow); } Collection<T> realValues = (Collection<T>) identifiers.iterator().next().getRealValues(); if (realValues.size() == 0) { return null; } if (realValues.size() > 1) { throw new SchemaException("More than one secondary idenfier value in " + shadow); } return realValues.iterator().next(); } public String determineLdapSingleAttributeValue(Collection<String> dns, String attributeName, PrismProperty attribute) throws NamingException { return determineLdapSingleAttributeValue(dns, attributeName, attribute.getRealValues()); } public <T> T getResourceIcfConfigurationPropertyValue(ResourceType resource, javax.xml.namespace.QName propertyQname) throws SchemaException { if (propertyQname == null) { return null; } PrismContainer<?> configurationProperties = getIcfConfigurationProperties(resource); if (configurationProperties == null) { return null; } PrismProperty<T> property = configurationProperties.findProperty(propertyQname); if (property == null) { return null; } return property.getRealValue(); } public <T> T getResourceIcfConfigurationPropertyValue(ResourceType resource, String propertyLocalPart) throws SchemaException { if (propertyLocalPart == null) { return null; } PrismContainer<?> configurationProperties = getIcfConfigurationProperties(resource); if (configurationProperties == null) { return null; } for (PrismProperty<?> property : configurationProperties.getValue().getProperties()) { if (propertyLocalPart.equals(property.getElementName().getLocalPart())) { return (T) property.getRealValue(); } } return null; } private PrismContainer<?> getIcfConfigurationProperties(ResourceType resource) { if (resource == null) { return null; } PrismContainer<?> connectorConfiguration = resource.asPrismObject() .findContainer(ResourceType.F_CONNECTOR_CONFIGURATION); if (connectorConfiguration == null) { return null; } return connectorConfiguration.findContainer(SchemaConstants.ICF_CONFIGURATION_PROPERTIES); } public String determineLdapSingleAttributeValue(Collection<String> dns, String attributeName, Collection<String> values) throws NamingException { if (values == null || values.isEmpty()) { // Shortcut. This is maybe the most common case. We want to return quickly and we also need to avoid more checks later. return null; } if (dns == null || dns.isEmpty()) { return determineLdapSingleAttributeValue((String) null, attributeName, values); } if (dns.size() > 1) { throw new IllegalArgumentException("Nore than one value (" + dns.size() + " for dn argument specified"); } return determineLdapSingleAttributeValue(dns.iterator().next(), attributeName, values); } // We cannot have Collection<String> here. The generic type information will disappear at runtime and the scripts can pass // anything that they find suitable. E.g. XPath is passing elements public String determineLdapSingleAttributeValue(String dn, String attributeName, Collection<?> values) throws NamingException { if (values == null || values.isEmpty()) { return null; } Collection<String> stringValues = null; // Determine item type, try to convert to strings Object firstElement = values.iterator().next(); if (firstElement instanceof String) { stringValues = (Collection) values; } else if (firstElement instanceof Element) { stringValues = new ArrayList<String>(values.size()); for (Object value : values) { Element element = (Element) value; stringValues.add(element.getTextContent()); } } else { throw new IllegalArgumentException("Unexpected value type " + firstElement.getClass()); } if (stringValues.size() == 1) { return stringValues.iterator().next(); } if (StringUtils.isBlank(dn)) { throw new IllegalArgumentException( "No dn argument specified, cannot determine which of " + values.size() + " values to use"); } LdapName parsedDn = new LdapName(dn); for (int i = 0; i < parsedDn.size(); i++) { Rdn rdn = parsedDn.getRdn(i); Attributes rdnAttributes = rdn.toAttributes(); NamingEnumeration<String> rdnIDs = rdnAttributes.getIDs(); while (rdnIDs.hasMore()) { String rdnID = rdnIDs.next(); Attribute attribute = rdnAttributes.get(rdnID); if (attributeName.equals(attribute.getID())) { for (int j = 0; j < attribute.size(); j++) { Object value = attribute.get(j); if (stringValues.contains(value)) { return (String) value; } } } } } // Fallback. No values in DN. Just return the first alphabetically-wise value. return Collections.min(stringValues); } public <T> T toSingle(Collection<T> values) throws SchemaException { if (values == null || values.isEmpty()) { return null; } else if (values.size() > 1) { throw new SchemaException("Attempt to get single value from a multi-valued property"); } else { return values.iterator().next(); } } private <T> T toSingle(Collection<T> values, String contextDesc) throws SchemaException { if (values == null || values.isEmpty()) { return null; } else if (values.size() > 1) { throw new SchemaException("Attempt to get single value from " + contextDesc); } else { return values.iterator().next(); } } public static String readFile(String filename) throws IOException { return FileUtils.readFileToString(new File(filename)); } public String formatDateTime(String format, XMLGregorianCalendar xmlCal) { if (xmlCal == null || format == null) { return null; } SimpleDateFormat sdf = new SimpleDateFormat(format); Date date = XmlTypeConverter.toDate(xmlCal); return sdf.format(date); } public String formatDateTime(String format, Long millis) { if (millis == null || format == null) { return null; } SimpleDateFormat sdf = new SimpleDateFormat(format); return sdf.format(millis); } public XMLGregorianCalendar parseDateTime(String format, String stringDate) throws ParseException { if (format == null || stringDate == null) { return null; } String[] formats = new String[] { format }; Date date = DateUtils.parseDate(stringDate, formats); if (date == null) { return null; } return XmlTypeConverter.createXMLGregorianCalendar(date); } public XMLGregorianCalendar currentDateTime() { return XmlTypeConverter.createXMLGregorianCalendar(System.currentTimeMillis()); } public String decrypt(ProtectedStringType protectedString) { try { return protector.decryptString(protectedString); } catch (EncryptionException e) { throw new SystemException(e.getMessage(), e); } } public ProtectedStringType encrypt(String string) { try { return protector.encryptString(string); } catch (EncryptionException e) { throw new SystemException(e.getMessage(), e); } } }