001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *   http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    package org.apache.felix.framework.util;
020    
021    import java.io.*;
022    
023    import java.util.ArrayList;
024    import java.util.HashMap;
025    import java.util.List;
026    import java.util.Map;
027    import java.util.Properties;
028    
029    import org.apache.felix.framework.util.manifestparser.Capability;
030    import org.apache.felix.moduleloader.*;
031    import org.osgi.framework.Bundle;
032    import org.osgi.framework.Constants;
033    import org.osgi.framework.ServiceReference;
034    
035    public class Util
036    {
037        /**
038         * Converts a module identifier to a bundle identifier. Module IDs
039         * are typically <tt>&lt;bundle-id&gt;.&lt;revision&gt;</tt>; this
040         * method returns only the portion corresponding to the bundle ID.
041        **/
042        public static long getBundleIdFromModuleId(String id)
043        {
044            try
045            {
046                String bundleId = (id.indexOf('.') >= 0)
047                    ? id.substring(0, id.indexOf('.')) : id;
048                return Long.parseLong(bundleId);
049            }
050            catch (NumberFormatException ex)
051            {
052                return -1;
053            }
054        }
055    
056        /**
057         * Converts a module identifier to a bundle identifier. Module IDs
058         * are typically <tt>&lt;bundle-id&gt;.&lt;revision&gt;</tt>; this
059         * method returns only the portion corresponding to the revision.
060        **/
061        public static int getModuleRevisionFromModuleId(String id)
062        {
063            try
064            {
065                int index = id.indexOf('.');
066                if (index >= 0)
067                {
068                    return Integer.parseInt(id.substring(index + 1));
069                }
070            }
071            catch (NumberFormatException ex)
072            {
073            }
074            return -1;
075        }
076    
077        public static String getClassName(String className)
078        {
079            if (className == null)
080            {
081                className = "";
082            }
083            return (className.lastIndexOf('.') < 0)
084                ? "" : className.substring(className.lastIndexOf('.') + 1);
085        }
086    
087        public static String getClassPackage(String className)
088        {
089            if (className == null)
090            {
091                className = "";
092            }
093            return (className.lastIndexOf('.') < 0)
094                ? "" : className.substring(0, className.lastIndexOf('.'));
095        }
096    
097        public static String getResourcePackage(String resource)
098        {
099            if (resource == null)
100            {
101                resource = "";
102            }
103            // NOTE: The package of a resource is tricky to determine since
104            // resources do not follow the same naming conventions as classes.
105            // This code is pessimistic and assumes that the package of a
106            // resource is everything up to the last '/' character. By making
107            // this choice, it will not be possible to load resources from
108            // imports using relative resource names. For example, if a
109            // bundle exports "foo" and an importer of "foo" tries to load
110            // "/foo/bar/myresource.txt", this will not be found in the exporter
111            // because the following algorithm assumes the package name is
112            // "foo.bar", not just "foo". This only affects imported resources,
113            // local resources will work as expected.
114            String pkgName = (resource.startsWith("/")) ? resource.substring(1) : resource;
115            pkgName = (pkgName.lastIndexOf('/') < 0)
116                ? "" : pkgName.substring(0, pkgName.lastIndexOf('/'));
117            pkgName = pkgName.replace('/', '.');
118            return pkgName;
119        }
120    
121        /**
122         * <p>
123         * This is a simple utility class that attempts to load the named
124         * class using the class loader of the supplied class or
125         * the class loader of one of its super classes or their implemented
126         * interfaces. This is necessary during service registration to test
127         * whether a given service object implements its declared service
128         * interfaces.
129         * </p>
130         * <p>
131         * To perform this test, the framework must try to load
132         * the classes associated with the declared service interfaces, so
133         * it must choose a class loader. The class loader of the registering
134         * bundle cannot be used, since this disallows third parties to
135         * register service on behalf of another bundle. Consequently, the
136         * class loader of the service object must be used. However, this is
137         * also not sufficient since the class loader of the service object
138         * may not have direct access to the class in question.
139         * </p>
140         * <p>
141         * The service object's class loader may not have direct access to
142         * its service interface if it extends a super class from another
143         * bundle which implements the service interface from an imported
144         * bundle or if it implements an extension of the service interface
145         * from another bundle which imports the base interface from another
146         * bundle. In these cases, the service object's class loader only has
147         * access to the super class's class or the extended service interface,
148         * respectively, but not to the actual service interface.
149         * </p>
150         * <p>
151         * Thus, it is necessary to not only try to load the service interface
152         * class from the service object's class loader, but from the class
153         * loaders of any interfaces it implements and the class loaders of
154         * all super classes.
155         * </p>
156         * @param svcObj the class that is the root of the search.
157         * @param name the name of the class to load.
158         * @return the loaded class or <tt>null</tt> if it could not be
159         *         loaded.
160        **/
161        public static Class loadClassUsingClass(Class clazz, String name, SecureAction action)
162        {
163            Class loadedClass = null;
164    
165            while (clazz != null)
166            {
167                // Get the class loader of the current class object.
168                ClassLoader loader = action.getClassLoader(clazz);
169                // A null class loader represents the system class loader.
170                loader = (loader == null) ? action.getSystemClassLoader() : loader;
171                try
172                {
173                    return loader.loadClass(name);
174                }
175                catch (ClassNotFoundException ex)
176                {
177                    // Ignore and try interface class loaders.
178                }
179    
180                // Try to see if we can load the class from
181                // one of the class's implemented interface
182                // class loaders.
183                Class[] ifcs = clazz.getInterfaces();
184                for (int i = 0; i < ifcs.length; i++)
185                {
186                    loadedClass = loadClassUsingClass(ifcs[i], name, action);
187                    if (loadedClass != null)
188                    {
189                        return loadedClass;
190                    }
191                }
192    
193                // Try to see if we can load the class from
194                // the super class class loader.
195                clazz = clazz.getSuperclass();
196            }
197    
198            return null;
199        }
200    
201        /**
202         * This method determines if the requesting bundle is able to cast
203         * the specified service reference based on class visibility rules
204         * of the underlying modules.
205         * @param requester The bundle requesting the service.
206         * @param ref The service in question.
207         * @return <tt>true</tt> if the requesting bundle is able to case
208         *         the service object to a known type.
209        **/
210        public static boolean isServiceAssignable(Bundle requester, ServiceReference ref)
211        {
212            // Boolean flag.
213            boolean allow = true;
214            // Get the service's objectClass property.
215            String[] objectClass = (String[]) ref.getProperty(FelixConstants.OBJECTCLASS);
216    
217            // The the service reference is not assignable when the requesting
218            // bundle is wired to a different version of the service object.
219            // NOTE: We are pessimistic here, if any class in the service's
220            // objectClass is not usable by the requesting bundle, then we
221            // disallow the service reference.
222            for (int classIdx = 0; (allow) && (classIdx < objectClass.length); classIdx++)
223            {
224                if (!ref.isAssignableTo(requester, objectClass[classIdx]))
225                {
226                    allow = false;
227                }
228            }
229            return allow;
230        }
231    
232        public static ICapability getSatisfyingCapability(IModule m, IRequirement req)
233        {
234            ICapability[] caps = m.getCapabilities();
235            for (int i = 0; (caps != null) && (i < caps.length); i++)
236            {
237                if (caps[i].getNamespace().equals(req.getNamespace()) &&
238                    req.isSatisfied(caps[i]))
239                {
240                    return caps[i];
241                }
242            }
243            return null;
244        }
245    
246        /**
247         * Returns all the capabilities from a module that has a specified namespace.
248         *
249         * @param module    module providing capabilities
250         * @param namespace capability namespace
251         * @return array of matching capabilities or empty if none found
252         */
253        public static ICapability[] getCapabilityByNamespace(IModule module, String namespace)
254        {
255            final List matching = new ArrayList();
256            final ICapability[] caps = module.getCapabilities();
257            for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
258            {
259                if (caps[capIdx].getNamespace().equals(namespace))
260                {
261                    matching.add(caps[capIdx]);
262                }
263            }
264            return (ICapability[]) matching.toArray(new ICapability[matching.size()]);
265        }
266    
267        public static IWire getWire(IModule m, String name)
268        {
269            IWire[] wires = m.getWires();
270            for (int i = 0; (wires != null) && (i < wires.length); i++)
271            {
272                if (wires[i].getCapability().getNamespace().equals(ICapability.PACKAGE_NAMESPACE) &&
273                    ((Capability) wires[i].getCapability()).getPackageName().equals(name))
274                {
275                    return wires[i];
276                }
277            }
278            return null;
279        }
280    
281        private static final byte encTab[] = { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
282            0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52,
283            0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, 0x63, 0x64,
284            0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
285            0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x30, 0x31,
286            0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2b, 0x2f };
287    
288        private static final byte decTab[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1,
289            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
290            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1,
291            -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1,
292            -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
293            18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29,
294            30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
295            48, 49, 50, 51, -1, -1, -1, -1, -1 };
296    
297        public static String base64Encode(String s) throws IOException
298        {
299            return encode(s.getBytes(), 0);
300        }
301    
302        /**
303         * Encode a raw byte array to a Base64 String.
304         *
305         * @param in Byte array to encode.
306         * @param len Length of Base64 lines. 0 means no line breaks.
307        **/
308        public static String encode(byte[] in, int len) throws IOException
309        {
310            ByteArrayOutputStream baos = null;
311            ByteArrayInputStream bais = null;
312            try
313            {
314                baos = new ByteArrayOutputStream();
315                bais = new ByteArrayInputStream(in);
316                encode(bais, baos, len);
317                // ASCII byte array to String
318                return (new String(baos.toByteArray()));
319            }
320            finally
321            {
322                if (baos != null)
323                {
324                    baos.close();
325                }
326                if (bais != null)
327                {
328                    bais.close();
329                }
330            }
331        }
332    
333        public static void encode(InputStream in, OutputStream out, int len)
334            throws IOException
335        {
336    
337            // Check that length is a multiple of 4 bytes
338            if (len % 4 != 0)
339            {
340                throw new IllegalArgumentException("Length must be a multiple of 4");
341            }
342    
343            // Read input stream until end of file
344            int bits = 0;
345            int nbits = 0;
346            int nbytes = 0;
347            int b;
348    
349            while ((b = in.read()) != -1)
350            {
351                bits = (bits << 8) | b;
352                nbits += 8;
353                while (nbits >= 6)
354                {
355                    nbits -= 6;
356                    out.write(encTab[0x3f & (bits >> nbits)]);
357                    nbytes++;
358                    // New line
359                    if (len != 0 && nbytes >= len)
360                    {
361                        out.write(0x0d);
362                        out.write(0x0a);
363                        nbytes -= len;
364                    }
365                }
366            }
367    
368            switch (nbits)
369            {
370                case 2:
371                    out.write(encTab[0x3f & (bits << 4)]);
372                    out.write(0x3d); // 0x3d = '='
373                    out.write(0x3d);
374                    break;
375                case 4:
376                    out.write(encTab[0x3f & (bits << 2)]);
377                    out.write(0x3d);
378                    break;
379            }
380    
381            if (len != 0)
382            {
383                if (nbytes != 0)
384                {
385                    out.write(0x0d);
386                    out.write(0x0a);
387                }
388                out.write(0x0d);
389                out.write(0x0a);
390            }
391        }
392    
393    
394        private static final String DELIM_START = "${";
395        private static final String DELIM_STOP  = "}";
396    
397        /**
398         * <p>
399         * This method performs property variable substitution on the
400         * specified value. If the specified value contains the syntax
401         * <tt>${&lt;prop-name&gt;}</tt>, where <tt>&lt;prop-name&gt;</tt>
402         * refers to either a configuration property or a system property,
403         * then the corresponding property value is substituted for the variable
404         * placeholder. Multiple variable placeholders may exist in the
405         * specified value as well as nested variable placeholders, which
406         * are substituted from inner most to outer most. Configuration
407         * properties override system properties.
408         * </p>
409         * @param val The string on which to perform property substitution.
410         * @param currentKey The key of the property being evaluated used to
411         *        detect cycles.
412         * @param cycleMap Map of variable references used to detect nested cycles.
413         * @param configProps Set of configuration properties.
414         * @return The value of the specified string after system property substitution.
415         * @throws IllegalArgumentException If there was a syntax error in the
416         *         property placeholder syntax or a recursive variable reference.
417        **/
418        public static String substVars(String val, String currentKey,
419            Map cycleMap, Properties configProps)
420            throws IllegalArgumentException
421        {
422            // If there is currently no cycle map, then create
423            // one for detecting cycles for this invocation.
424            if (cycleMap == null)
425            {
426                cycleMap = new HashMap();
427            }
428    
429            // Put the current key in the cycle map.
430            cycleMap.put(currentKey, currentKey);
431    
432            // Assume we have a value that is something like:
433            // "leading ${foo.${bar}} middle ${baz} trailing"
434    
435            // Find the first ending '}' variable delimiter, which
436            // will correspond to the first deepest nested variable
437            // placeholder.
438            int stopDelim = val.indexOf(DELIM_STOP);
439    
440            // Find the matching starting "${" variable delimiter
441            // by looping until we find a start delimiter that is
442            // greater than the stop delimiter we have found.
443            int startDelim = val.indexOf(DELIM_START);
444            while (stopDelim >= 0)
445            {
446                int idx = val.indexOf(DELIM_START, startDelim + DELIM_START.length());
447                if ((idx < 0) || (idx > stopDelim))
448                {
449                    break;
450                }
451                else if (idx < stopDelim)
452                {
453                    startDelim = idx;
454                }
455            }
456    
457            // If we do not have a start or stop delimiter, then just
458            // return the existing value.
459            if ((startDelim < 0) && (stopDelim < 0))
460            {
461                return val;
462            }
463            // At this point, we found a stop delimiter without a start,
464            // so throw an exception.
465            else if (((startDelim < 0) || (startDelim > stopDelim))
466                && (stopDelim >= 0))
467            {
468                throw new IllegalArgumentException(
469                    "stop delimiter with no start delimiter: "
470                    + val);
471            }
472    
473            // At this point, we have found a variable placeholder so
474            // we must perform a variable substitution on it.
475            // Using the start and stop delimiter indices, extract
476            // the first, deepest nested variable placeholder.
477            String variable =
478                val.substring(startDelim + DELIM_START.length(), stopDelim);
479    
480            // Verify that this is not a recursive variable reference.
481            if (cycleMap.get(variable) != null)
482            {
483                throw new IllegalArgumentException(
484                    "recursive variable reference: " + variable);
485            }
486    
487            // Get the value of the deepest nested variable placeholder.
488            // Try to configuration properties first.
489            String substValue = (configProps != null)
490                ? configProps.getProperty(variable, null)
491                : null;
492            if (substValue == null)
493            {
494                // Ignore unknown property values.
495                substValue = System.getProperty(variable, "");
496            }
497    
498            // Remove the found variable from the cycle map, since
499            // it may appear more than once in the value and we don't
500            // want such situations to appear as a recursive reference.
501            cycleMap.remove(variable);
502    
503            // Append the leading characters, the substituted value of
504            // the variable, and the trailing characters to get the new
505            // value.
506            val = val.substring(0, startDelim)
507                + substValue
508                + val.substring(stopDelim + DELIM_STOP.length(), val.length());
509    
510            // Now perform substitution again, since there could still
511            // be substitutions to make.
512            val = substVars(val, currentKey, cycleMap, configProps);
513    
514            // Return the value.
515            return val;
516        }
517    
518        /**
519         * Checks if the provided module definition declares a fragment host.
520         *
521         * @param module the module to check
522         * @return <code>true</code> if the module declares a fragment host, <code>false</code>
523         *      otherwise.
524         */
525        public static boolean isFragment(IModule module)
526        {
527            Map headerMap = module.getHeaders();
528            return headerMap.containsKey(Constants.FRAGMENT_HOST);
529        }
530    
531    
532        //
533        // The following substring-related code was lifted and modified
534        // from the LDAP parser code.
535        //
536    
537        public static String[] parseSubstring(String target)
538        {
539            List pieces = new ArrayList();
540            StringBuffer ss = new StringBuffer();
541            // int kind = SIMPLE; // assume until proven otherwise
542            boolean wasStar = false; // indicates last piece was a star
543            boolean leftstar = false; // track if the initial piece is a star
544            boolean rightstar = false; // track if the final piece is a star
545    
546            int idx = 0;
547    
548            // We assume (sub)strings can contain leading and trailing blanks
549    loop:   for (;;)
550            {
551                if (idx >= target.length())
552                {
553                    if (wasStar)
554                    {
555                        // insert last piece as "" to handle trailing star
556                        rightstar = true;
557                    }
558                    else
559                    {
560                        pieces.add(ss.toString());
561                        // accumulate the last piece
562                        // note that in the case of
563                        // (cn=); this might be
564                        // the string "" (!=null)
565                    }
566                    ss.setLength(0);
567                    break loop;
568                }
569    
570                char c = target.charAt(idx++);
571                if (c == '*')
572                {
573                    if (wasStar)
574                    {
575                        // encountered two successive stars;
576                        // I assume this is illegal
577                        throw new IllegalArgumentException("Invalid filter string: " + target);
578                    }
579                    if (ss.length() > 0)
580                    {
581                        pieces.add(ss.toString()); // accumulate the pieces
582                        // between '*' occurrences
583                    }
584                    ss.setLength(0);
585                    // if this is a leading star, then track it
586                    if (pieces.size() == 0)
587                    {
588                        leftstar = true;
589                    }
590                    wasStar = true;
591                }
592                else
593                {
594                    wasStar = false;
595                    ss.append(c);
596                }
597            }
598            if (leftstar || rightstar || pieces.size() > 1)
599            {
600                // insert leading and/or trailing "" to anchor ends
601                if (rightstar)
602                {
603                    pieces.add("");
604                }
605                if (leftstar)
606                {
607                    pieces.add(0, "");
608                }
609            }
610            return (String[]) pieces.toArray(new String[pieces.size()]);
611        }
612    
613        public static boolean checkSubstring(String[] pieces, String s)
614        {
615            // Walk the pieces to match the string
616            // There are implicit stars between each piece,
617            // and the first and last pieces might be "" to anchor the match.
618            // assert (pieces.length > 1)
619            // minimal case is <string>*<string>
620    
621            boolean result = true;
622            int len = pieces.length;
623    
624            int index = 0;
625    
626    loop:   for (int i = 0; i < len; i++)
627            {
628                String piece = pieces[i];
629    
630                // If this is the first piece, then make sure the
631                // string starts with it.
632                if (i == 0)
633                {
634                    if (!s.startsWith(piece))
635                    {
636                        result = false;
637                        break loop;
638                    }
639                }
640    
641                // If this is the last piece, then make sure the
642                // string ends with it.
643                if (i == len - 1)
644                {
645                    if (s.endsWith(piece))
646                    {
647                        result = true;
648                    }
649                    else
650                    {
651                        result = false;
652                    }
653                    break loop;
654                }
655    
656                // If this is neither the first or last piece, then
657                // make sure the string contains it.
658                if ((i > 0) && (i < (len - 1)))
659                {
660                    index = s.indexOf(piece, index);
661                    if (index < 0)
662                    {
663                        result = false;
664                        break loop;
665                    }
666                }
667    
668                // Move string index beyond the matching piece.
669                index += piece.length();
670            }
671    
672            return result;
673        }
674    }