001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.spring.osgi;
016    
017    import com.liferay.portal.kernel.util.GetterUtil;
018    import com.liferay.portal.kernel.util.PropertiesUtil;
019    import com.liferay.portal.kernel.util.PropsUtil;
020    import com.liferay.portal.kernel.util.StringPool;
021    import com.liferay.portal.kernel.util.StringUtil;
022    import com.liferay.portal.kernel.util.Validator;
023    
024    import java.lang.annotation.ElementType;
025    import java.lang.annotation.Retention;
026    import java.lang.annotation.RetentionPolicy;
027    import java.lang.annotation.Target;
028    import java.lang.reflect.Array;
029    
030    import java.util.HashMap;
031    import java.util.Map;
032    import java.util.Properties;
033    
034    /**
035     * Provides the OSGi service properties used when publishing Spring beans as
036     * services.
037     *
038     * @author Raymond Aug??
039     */
040    @Retention(RetentionPolicy.RUNTIME)
041    @Target(ElementType.TYPE)
042    public @interface OSGiBeanProperties {
043    
044            /**
045             * Returns <code>true</code> if the property prefix should be removed from
046             * <code>portal.properties</code>.
047             *
048             * @return <code>true</code> if the property prefix should be removed from
049             *         <code>portal.properties</code>; <code>false</code> otherwise
050             */
051            public boolean portalPropertiesRemovePrefix() default true;
052    
053            /**
054             * Returns the value of the property prefix used for retrieving properties
055             * from <code>portal.properties</code>.
056             *
057             * @return the value of the property prefix
058             */
059            public String portalPropertyPrefix() default "";
060    
061            /**
062             * Returns the service properties.
063             *
064             * <p>
065             * Each property string is specified as <code>"key=value"</code>. The type
066             * of the property value can be specified in the key as
067             * <code>"key:type=value"</code>. The type must be from {@link Type}. To
068             * specify a property with multiple values, use multiple key-value pairs.
069             * For example, <code>"foo=bar", "foo=baz"</code>.
070             * </p>
071             *
072             * @return the service properties
073             */
074            public String[] property() default {};
075    
076            /**
077             * Converts OSGi bean properties from the {@link OSGiBeanProperties}
078             * annotation into a properties map. This is a helper class.
079             */
080            public static class Convert {
081    
082                    /**
083                     * Returns a properties map representing the object's OSGi bean properties.
084                     *
085                     * @param  object the object that is possibly annotated with {@link
086                     *         OSGiBeanProperties}
087                     * @return a properties map representing the object's OSGi bean
088                     *         properties. The map will be <code>null</code> if the object
089                     *         is not annotated with {@link OSGiBeanProperties} or will be
090                     *         empty if the object is annotated with {@link
091                     *         OSGiBeanProperties} but has no properties.
092                     */
093                    public static Map<String, Object> fromObject(Object object) {
094                            Class<? extends Object> clazz = object.getClass();
095    
096                            OSGiBeanProperties osgiBeanProperties = clazz.getAnnotation(
097                                    OSGiBeanProperties.class);
098    
099                            if (osgiBeanProperties == null) {
100                                    return null;
101                            }
102    
103                            return toMap(osgiBeanProperties);
104                    }
105    
106                    /**
107                     * Returns a properties map representing the {@link OSGiBeanProperties}
108                     * instance.
109                     *
110                     * @param  osgiBeanProperties the instance of {@link OSGiBeanProperties}
111                     *         read for properties
112                     * @return a properties map representing the {@link OSGiBeanProperties}
113                     *         instance. The map will be empty if the {@link
114                     *         OSGiBeanProperties} instance has no properties.
115                     */
116                    public static Map<String, Object> toMap(
117                            OSGiBeanProperties osgiBeanProperties) {
118    
119                            Map<String, Object> properties = new HashMap<String, Object>();
120    
121                            for (String property : osgiBeanProperties.property()) {
122                                    String[] parts = property.split(StringPool.EQUAL, 2);
123    
124                                    if (parts.length <= 1) {
125                                            continue;
126                                    }
127    
128                                    String key = parts[0];
129                                    String className = String.class.getSimpleName();
130    
131                                    if (key.indexOf(StringPool.COLON) != -1) {
132                                            String[] keyParts = StringUtil.split(key, StringPool.COLON);
133    
134                                            key = keyParts[0];
135                                            className = keyParts[1];
136                                    }
137    
138                                    String value = parts[1];
139    
140                                    _put(key, value, className, properties);
141                            }
142    
143                            String portalPropertyPrefix =
144                                    osgiBeanProperties.portalPropertyPrefix();
145    
146                            if (Validator.isNotNull(portalPropertyPrefix)) {
147                                    Properties portalProperties = PropsUtil.getProperties(
148                                            portalPropertyPrefix,
149                                            osgiBeanProperties.portalPropertiesRemovePrefix());
150    
151                                    properties.putAll(PropertiesUtil.toMap(portalProperties));
152                            }
153    
154                            return properties;
155                    }
156    
157                    private static void _put(
158                            String key, String value, String className,
159                            Map<String, Object> properties) {
160    
161                            Type type = Type._getType(className);
162    
163                            Object previousValue = properties.get(key);
164    
165                            properties.put(key, type._convert(value, previousValue));
166                    }
167    
168            }
169    
170            public enum Type {
171    
172                    BOOLEAN, BYTE, CHARACTER, DOUBLE, FLOAT, INTEGER, LONG, SHORT, STRING;
173    
174                    private static Type _getType(String name) {
175                            name = StringUtil.toUpperCase(name);
176    
177                            for (Type type : values()) {
178                                    if (name.equals(type.name())) {
179                                            return type;
180                                    }
181                            }
182    
183                            return Type.STRING;
184                    }
185    
186                    private Object _convert(String value, Object previousValue) {
187                            if (previousValue == null) {
188                                    return _getTypedValue(value);
189                            }
190    
191                            Class<?> clazz = previousValue.getClass();
192    
193                            if (!clazz.isArray()) {
194                                    Object array = Array.newInstance(_getTypeClass(), 2);
195    
196                                    Array.set(array, 0, previousValue);
197                                    Array.set(array, 1, _getTypedValue(value));
198    
199                                    return array;
200                            }
201    
202                            Object array = Array.newInstance(
203                                    _getTypeClass(), Array.getLength(previousValue) + 1);
204    
205                            for (int i = 0; i < Array.getLength(previousValue); i++) {
206                                    Array.set(array, i, Array.get(previousValue, i));
207                            }
208    
209                            Array.set(
210                                    array, Array.getLength(previousValue), _getTypedValue(value));
211    
212                            return array;
213                    }
214    
215                    private Class<?> _getTypeClass() {
216                            if (this == Type.BOOLEAN) {
217                                    return java.lang.Boolean.class;
218                            }
219                            else if (this == Type.BYTE) {
220                                    return java.lang.Byte.class;
221                            }
222                            else if (this == Type.CHARACTER) {
223                                    return java.lang.Character.class;
224                            }
225                            else if (this == Type.DOUBLE) {
226                                    return java.lang.Double.class;
227                            }
228                            else if (this == Type.FLOAT) {
229                                    return java.lang.Float.class;
230                            }
231                            else if (this == Type.INTEGER) {
232                                    return java.lang.Integer.class;
233                            }
234                            else if (this == Type.LONG) {
235                                    return java.lang.Long.class;
236                            }
237                            else if (this == Type.SHORT) {
238                                    return java.lang.Short.class;
239                            }
240                            else if (this == Type.STRING) {
241                                    return java.lang.String.class;
242                            }
243    
244                            return null;
245                    }
246    
247                    private Object _getTypedValue(String value) {
248                            if (this == Type.BOOLEAN) {
249                                    return GetterUtil.getBoolean(value);
250                            }
251                            else if (this == Type.BYTE) {
252                                    return new java.lang.Byte(value).byteValue();
253                            }
254                            else if (this == Type.CHARACTER) {
255                                    return value.charAt(0);
256                            }
257                            else if (this == Type.DOUBLE) {
258                                    return GetterUtil.getDouble(value);
259                            }
260                            else if (this == Type.FLOAT) {
261                                    return GetterUtil.getFloat(value);
262                            }
263                            else if (this == Type.INTEGER) {
264                                    return GetterUtil.getInteger(value);
265                            }
266                            else if (this == Type.LONG) {
267                                    return GetterUtil.getLong(value);
268                            }
269                            else if (this == Type.SHORT) {
270                                    return GetterUtil.getShort(value);
271                            }
272    
273                            return value;
274                    }
275    
276            }
277    
278    }