GenericURLContext.java revision 820:9205e980062a
1/*
2 * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.jndi.url.iiop;
27
28import com.sun.jndi.toolkit.corba.CorbaUtils;
29
30import javax.naming.*;
31import javax.naming.spi.ResolveResult;
32import javax.naming.spi.NamingManager;
33
34import java.util.Hashtable;
35import java.net.MalformedURLException;
36
37/**
38 * This abstract class is a generic URL context that accepts as the
39 * name argument either a string URL or a Name whose first component
40 * is a URL. It resolves the URL to a target context and then continues
41 * the operation using the remaining name in the target context as if
42 * the first component names a junction.
43 *
44 * A subclass must define getRootURLContext()
45 * to process the URL into head/tail pieces. If it wants to control how
46 * URL strings are parsed and compared for the rename() operation, then
47 * it should override getNonRootURLSuffixes() and urlEquals().
48 *
49 * @author Scott Seligman
50 * @author Rosanna Lee
51 */
52abstract public class GenericURLContext implements Context {
53    protected Hashtable<String, Object> myEnv = null;
54
55    @SuppressWarnings("unchecked") // Expect Hashtable<String, Object>
56    public GenericURLContext(Hashtable<?,?> env) {
57        // context that is not tied to any specific URL
58        myEnv =
59                (Hashtable<String, Object>)(env == null ? null : env.clone());
60    }
61
62    public void close() throws NamingException {
63        myEnv = null;
64    }
65
66    public String getNameInNamespace() throws NamingException {
67        return ""; // %%% check this out: A URL context's name is ""
68    }
69
70    /**
71     * Resolves 'name' into a target context with remaining name.
72     * For example, with a JNDI URL "jndi://dnsname/rest_name",
73     * this method resolves "jndi://dnsname/" to a target context,
74     * and returns the target context with "rest_name".
75     * The definition of "root URL" and how much of the URL to
76     * consume is implementation specific.
77     * If rename() is supported for a particular URL scheme,
78     * getRootURLContext(), getURLPrefix(), and getURLSuffix()
79     * must be in sync wrt how URLs are parsed and returned.
80     */
81    abstract protected ResolveResult getRootURLContext(String url,
82                                                       Hashtable<?,?> env) throws NamingException;
83
84    /**
85     * Returns the suffix of the url. The result should be identical to
86     * that of calling getRootURLContext().getRemainingName(), but
87     * without the overhead of doing anything with the prefix like
88     * creating a context.
89     *<p>
90     * This method returns a Name instead of a String because to give
91     * the provider an opportunity to return a Name (for example,
92     * for weakly separated naming systems like COS naming).
93     *<p>
94     * The default implementation uses skips 'prefix', calls
95     * CorbaUtils.decode() on it, and returns the result as a single component
96     * CompositeName.
97     * Subclass should override if this is not appropriate.
98     * This method is used only by rename().
99     * If rename() is supported for a particular URL scheme,
100     * getRootURLContext(), getURLPrefix(), and getURLSuffix()
101     * must be in sync wrt how URLs are parsed and returned.
102     *<p>
103     * For many URL schemes, this method is very similar to URL.getFile(),
104     * except getFile() will return a leading slash in the
105     * 2nd, 3rd, and 4th cases. For schemes like "ldap" and "iiop",
106     * the leading slash must be skipped before the name is an acceptable
107     * format for operation by the Context methods. For schemes that treat the
108     * leading slash as significant (such as "file"),
109     * the subclass must override getURLSuffix() to get the correct behavior.
110     * Remember, the behavior must match getRootURLContext().
111     *
112     * <pre>{@code
113     * URL                                     Suffix
114     * foo://host:port                         <empty string>
115     * foo://host:port/rest/of/name            rest/of/name
116     * foo:///rest/of/name                     rest/of/name
117     * foo:/rest/of/name                       rest/of/name
118     * foo:rest/of/name                        rest/of/name
119     * }</pre>
120     */
121    protected Name getURLSuffix(String prefix, String url) throws NamingException {
122        String suffix = url.substring(prefix.length());
123        if (suffix.length() == 0) {
124            return new CompositeName();
125        }
126
127        if (suffix.charAt(0) == '/') {
128            suffix = suffix.substring(1); // skip leading slash
129        }
130
131        try {
132            return new CompositeName().add(CorbaUtils.decode(suffix));
133        } catch (MalformedURLException e) {
134            throw new InvalidNameException(e.getMessage());
135        }
136    }
137
138    /**
139     * Finds the prefix of a URL.
140     * Default implementation looks for slashes and then extracts
141     * prefixes using String.substring().
142     * Subclass should override if this is not appropriate.
143     * This method is used only by rename().
144     * If rename() is supported for a particular URL scheme,
145     * getRootURLContext(), getURLPrefix(), and getURLSuffix()
146     * must be in sync wrt how URLs are parsed and returned.
147     *<p>
148     * URL                                     Prefix
149     * foo://host:port                         foo://host:port
150     * foo://host:port/rest/of/name            foo://host:port
151     * foo:///rest/of/name                     foo://
152     * foo:/rest/of/name                       foo:
153     * foo:rest/of/name                        foo:
154     */
155    protected String getURLPrefix(String url) throws NamingException {
156        int start = url.indexOf(':');
157
158        if (start < 0) {
159            throw new OperationNotSupportedException("Invalid URL: " + url);
160        }
161        ++start; // skip ':'
162
163        if (url.startsWith("//", start)) {
164            start += 2;  // skip double slash
165
166            // find last slash
167            int posn = url.indexOf('/', start);
168            if (posn >= 0) {
169                start = posn;
170            } else {
171                start = url.length();  // rest of URL
172            }
173        }
174
175        // else 0 or 1 initial slashes; start is unchanged
176        return url.substring(0, start);
177    }
178
179    /**
180     * Determines whether two URLs are the same.
181     * Default implementation uses String.equals().
182     * Subclass should override if this is not appropriate.
183     * This method is used by rename().
184     */
185    protected boolean urlEquals(String url1, String url2) {
186        return url1.equals(url2);
187    }
188
189    /**
190     * Gets the context in which to continue the operation. This method
191     * is called when this context is asked to process a multicomponent
192     * Name in which the first component is a URL.
193     * Treat the first component like a junction: resolve it and then use
194     * NamingManager.getContinuationContext() to get the target context in
195     * which to operate on the remainder of the name (n.getSuffix(1)).
196     */
197    protected Context getContinuationContext(Name n) throws NamingException {
198        Object obj = lookup(n.get(0));
199        CannotProceedException cpe = new CannotProceedException();
200        cpe.setResolvedObj(obj);
201        cpe.setEnvironment(myEnv);
202        return NamingManager.getContinuationContext(cpe);
203    }
204
205    public Object lookup(String name) throws NamingException {
206        ResolveResult res = getRootURLContext(name, myEnv);
207        Context ctx = (Context)res.getResolvedObj();
208        try {
209            return ctx.lookup(res.getRemainingName());
210        } finally {
211            ctx.close();
212        }
213    }
214
215    public Object lookup(Name name) throws NamingException {
216        if (name.size() == 1) {
217            return lookup(name.get(0));
218        } else {
219            Context ctx = getContinuationContext(name);
220            try {
221                return ctx.lookup(name.getSuffix(1));
222            } finally {
223                ctx.close();
224            }
225        }
226    }
227
228    public void bind(String name, Object obj) throws NamingException {
229        ResolveResult res = getRootURLContext(name, myEnv);
230        Context ctx = (Context)res.getResolvedObj();
231        try {
232            ctx.bind(res.getRemainingName(), obj);
233        } finally {
234            ctx.close();
235        }
236    }
237
238    public void bind(Name name, Object obj) throws NamingException {
239        if (name.size() == 1) {
240            bind(name.get(0), obj);
241        } else {
242            Context ctx = getContinuationContext(name);
243            try {
244                ctx.bind(name.getSuffix(1), obj);
245            } finally {
246                ctx.close();
247            }
248        }
249    }
250
251    public void rebind(String name, Object obj) throws NamingException {
252        ResolveResult res = getRootURLContext(name, myEnv);
253        Context ctx = (Context)res.getResolvedObj();
254        try {
255            ctx.rebind(res.getRemainingName(), obj);
256        } finally {
257            ctx.close();
258        }
259    }
260
261    public void rebind(Name name, Object obj) throws NamingException {
262        if (name.size() == 1) {
263            rebind(name.get(0), obj);
264        } else {
265            Context ctx = getContinuationContext(name);
266            try {
267                ctx.rebind(name.getSuffix(1), obj);
268            } finally {
269                ctx.close();
270            }
271        }
272    }
273
274    public void unbind(String name) throws NamingException {
275        ResolveResult res = getRootURLContext(name, myEnv);
276        Context ctx = (Context)res.getResolvedObj();
277        try {
278            ctx.unbind(res.getRemainingName());
279        } finally {
280            ctx.close();
281        }
282    }
283
284    public void unbind(Name name) throws NamingException {
285        if (name.size() == 1) {
286            unbind(name.get(0));
287        } else {
288            Context ctx = getContinuationContext(name);
289            try {
290                ctx.unbind(name.getSuffix(1));
291            } finally {
292                ctx.close();
293            }
294        }
295    }
296
297    public void rename(String oldName, String newName) throws NamingException {
298        String oldPrefix = getURLPrefix(oldName);
299        String newPrefix = getURLPrefix(newName);
300        if (!urlEquals(oldPrefix, newPrefix)) {
301            throw new OperationNotSupportedException(
302                    "Renaming using different URL prefixes not supported : " +
303                            oldName + " " + newName);
304        }
305
306        ResolveResult res = getRootURLContext(oldName, myEnv);
307        Context ctx = (Context)res.getResolvedObj();
308        try {
309            ctx.rename(res.getRemainingName(), getURLSuffix(newPrefix, newName));
310        } finally {
311            ctx.close();
312        }
313    }
314
315    public void rename(Name name, Name newName) throws NamingException {
316        if (name.size() == 1) {
317            if (newName.size() != 1) {
318                throw new OperationNotSupportedException(
319                        "Renaming to a Name with more components not supported: " + newName);
320            }
321            rename(name.get(0), newName.get(0));
322        } else {
323            // > 1 component with 1st one being URL
324            // URLs must be identical; cannot deal with diff URLs
325            if (!urlEquals(name.get(0), newName.get(0))) {
326                throw new OperationNotSupportedException(
327                        "Renaming using different URLs as first components not supported: " +
328                                name + " " + newName);
329            }
330
331            Context ctx = getContinuationContext(name);
332            try {
333                ctx.rename(name.getSuffix(1), newName.getSuffix(1));
334            } finally {
335                ctx.close();
336            }
337        }
338    }
339
340    public NamingEnumeration<NameClassPair> list(String name)   throws NamingException {
341        ResolveResult res = getRootURLContext(name, myEnv);
342        Context ctx = (Context)res.getResolvedObj();
343        try {
344            return ctx.list(res.getRemainingName());
345        } finally {
346            ctx.close();
347        }
348    }
349
350    public NamingEnumeration<NameClassPair> list(Name name) throws NamingException {
351        if (name.size() == 1) {
352            return list(name.get(0));
353        } else {
354            Context ctx = getContinuationContext(name);
355            try {
356                return ctx.list(name.getSuffix(1));
357            } finally {
358                ctx.close();
359            }
360        }
361    }
362
363    public NamingEnumeration<Binding> listBindings(String name)
364            throws NamingException {
365        ResolveResult res = getRootURLContext(name, myEnv);
366        Context ctx = (Context)res.getResolvedObj();
367        try {
368            return ctx.listBindings(res.getRemainingName());
369        } finally {
370            ctx.close();
371        }
372    }
373
374    public NamingEnumeration<Binding> listBindings(Name name) throws NamingException {
375        if (name.size() == 1) {
376            return listBindings(name.get(0));
377        } else {
378            Context ctx = getContinuationContext(name);
379            try {
380                return ctx.listBindings(name.getSuffix(1));
381            } finally {
382                ctx.close();
383            }
384        }
385    }
386
387    public void destroySubcontext(String name) throws NamingException {
388        ResolveResult res = getRootURLContext(name, myEnv);
389        Context ctx = (Context)res.getResolvedObj();
390        try {
391            ctx.destroySubcontext(res.getRemainingName());
392        } finally {
393            ctx.close();
394        }
395    }
396
397    public void destroySubcontext(Name name) throws NamingException {
398        if (name.size() == 1) {
399            destroySubcontext(name.get(0));
400        } else {
401            Context ctx = getContinuationContext(name);
402            try {
403                ctx.destroySubcontext(name.getSuffix(1));
404            } finally {
405                ctx.close();
406            }
407        }
408    }
409
410    public Context createSubcontext(String name) throws NamingException {
411        ResolveResult res = getRootURLContext(name, myEnv);
412        Context ctx = (Context)res.getResolvedObj();
413        try {
414            return ctx.createSubcontext(res.getRemainingName());
415        } finally {
416            ctx.close();
417        }
418    }
419
420    public Context createSubcontext(Name name) throws NamingException {
421        if (name.size() == 1) {
422            return createSubcontext(name.get(0));
423        } else {
424            Context ctx = getContinuationContext(name);
425            try {
426                return ctx.createSubcontext(name.getSuffix(1));
427            } finally {
428                ctx.close();
429            }
430        }
431    }
432
433    public Object lookupLink(String name) throws NamingException {
434        ResolveResult res = getRootURLContext(name, myEnv);
435        Context ctx = (Context)res.getResolvedObj();
436        try {
437            return ctx.lookupLink(res.getRemainingName());
438        } finally {
439            ctx.close();
440        }
441    }
442
443    public Object lookupLink(Name name) throws NamingException {
444        if (name.size() == 1) {
445            return lookupLink(name.get(0));
446        } else {
447            Context ctx = getContinuationContext(name);
448            try {
449                return ctx.lookupLink(name.getSuffix(1));
450            } finally {
451                ctx.close();
452            }
453        }
454    }
455
456    public NameParser getNameParser(String name) throws NamingException {
457        ResolveResult res = getRootURLContext(name, myEnv);
458        Context ctx = (Context)res.getResolvedObj();
459        try {
460            return ctx.getNameParser(res.getRemainingName());
461        } finally {
462            ctx.close();
463        }
464    }
465
466    public NameParser getNameParser(Name name) throws NamingException {
467        if (name.size() == 1) {
468            return getNameParser(name.get(0));
469        } else {
470            Context ctx = getContinuationContext(name);
471            try {
472                return ctx.getNameParser(name.getSuffix(1));
473            } finally {
474                ctx.close();
475            }
476        }
477    }
478
479    public String composeName(String name, String prefix)
480            throws NamingException {
481        if (prefix.equals("")) {
482            return name;
483        } else if (name.equals("")) {
484            return prefix;
485        } else {
486            return (prefix + "/" + name);
487        }
488    }
489
490    public Name composeName(Name name, Name prefix) throws NamingException {
491        Name result = (Name)prefix.clone();
492        result.addAll(name);
493        return result;
494    }
495
496    public Object removeFromEnvironment(String propName)
497            throws NamingException {
498        if (myEnv == null) {
499            return null;
500        }
501        return myEnv.remove(propName);
502    }
503
504    public Object addToEnvironment(String propName, Object propVal)
505            throws NamingException {
506        if (myEnv == null) {
507            myEnv = new Hashtable<String, Object>(11, 0.75f);
508        }
509        return myEnv.put(propName, propVal);
510    }
511
512    @SuppressWarnings("unchecked") // clone()
513    public Hashtable<String, Object> getEnvironment() throws NamingException {
514        if (myEnv == null) {
515            return new Hashtable<>(5, 0.75f);
516        } else {
517            return (Hashtable<String, Object>)myEnv.clone();
518        }
519    }
520
521/*
522// To test, declare getURLPrefix and getURLSuffix static.
523
524    public static void main(String[] args) throws Exception {
525        String[] tests = {"file://host:port",
526                          "file:///rest/of/name",
527                          "file://host:port/rest/of/name",
528                          "file:/rest/of/name",
529                          "file:rest/of/name"};
530        for (int i = 0; i < tests.length; i++) {
531            String pre = getURLPrefix(tests[i]);
532            System.out.println(pre);
533            System.out.println(getURLSuffix(pre, tests[i]));
534        }
535    }
536*/
537}
538