001/*
002// This software is subject to the terms of the Eclipse Public License v1.0
003// Agreement, available at the following URL:
004// http://www.eclipse.org/legal/epl-v10.html.
005// You must accept the terms of that agreement to use this software.
006//
007// Copyright (C) 2010-2013 Pentaho
008// All Rights Reserved.
009*/
010package mondrian.server;
011
012import mondrian.olap.MondrianServer;
013import mondrian.olap.Util;
014import mondrian.spi.CatalogLocator;
015import mondrian.spi.impl.IdentityCatalogLocator;
016import mondrian.util.LockBox;
017
018import java.io.*;
019import java.net.URL;
020import java.util.*;
021
022/**
023 * Registry of all servers within this JVM, and also serves as a factory for
024 * servers.
025 *
026 * <p>This class is not a public API. User applications should use the
027 * methods in {@link mondrian.olap.MondrianServer}.
028 *
029 * @author jhyde
030 */
031public class MondrianServerRegistry {
032    public static final MondrianServerRegistry INSTANCE =
033        new MondrianServerRegistry();
034
035    public MondrianServerRegistry() {
036        super();
037    }
038
039    /**
040     * Registry of all servers.
041     */
042    final LockBox lockBox = new LockBox();
043
044    /**
045     * The one and only one server that does not have a repository.
046     */
047    final MondrianServer staticServer =
048        createWithRepository(null, null);
049
050    private MondrianServer.MondrianVersion version = null;
051
052    /**
053     * Looks up a server with a given id. If the id is null, returns the
054     * static server.
055     *
056     * @param instanceId Unique identifier of server instance
057     * @return Server
058     * @throws RuntimeException if no server instance exists
059     */
060    public MondrianServer serverForId(String instanceId) {
061        if (instanceId != null) {
062            final LockBox.Entry entry = lockBox.get(instanceId);
063            if (entry == null) {
064                throw Util.newError(
065                    "No server instance has id '" + instanceId + "'");
066            }
067            return (MondrianServer) entry.getValue();
068        } else {
069            return staticServer;
070        }
071    }
072
073    public synchronized MondrianServer.MondrianVersion getOrLoadVersion() {
074        if (version == null) {
075            final String[] vendorTitleVersion = loadVersionFile();
076            String vendor = vendorTitleVersion[0];
077            final String title = vendorTitleVersion[1];
078            final String versionString = vendorTitleVersion[2];
079            if (false) {
080                System.out.println(
081                    "vendor=" + vendor
082                    + ", title=" + title
083                    + ", versionString=" + versionString);
084            }
085            int dot1 = versionString.indexOf('.');
086            final int majorVersion =
087                dot1 < 0
088                ? 1
089                : Integer.valueOf(versionString.substring(0, dot1));
090            int dot2 = versionString.indexOf('.', dot1 + 1);
091            final int minorVersion =
092                dot2 < 0
093                ? 0
094                : Integer.valueOf(versionString.substring(dot1 + 1, dot2));
095            version = new MondrianServer.MondrianVersion() {
096                public String getVersionString() {
097                    return versionString;
098                }
099
100                public int getMajorVersion() {
101                    return majorVersion;
102                }
103
104                public int getMinorVersion() {
105                    return minorVersion;
106                }
107
108                public String getProductName() {
109                    return title;
110                }
111            };
112        }
113        return version;
114    }
115
116    private static String[] loadVersionFile() {
117        // First, try to read the version info from the package. If the classes
118        // came from a jar, this info will be set from manifest.mf.
119        Package pakkage = MondrianServerImpl.class.getPackage();
120        String implementationVersion = pakkage.getImplementationVersion();
121
122        // Second, try to read VERSION.txt.
123        String version = "Unknown Version";
124        String title = "Unknown Database";
125        String vendor = "Unknown Vendor";
126        URL resource =
127            Util.getClosestResource(
128                MondrianServerImpl.class.getClassLoader(),
129                "DefaultRules.xml");
130        if (resource != null) {
131            try {
132                String path = resource.getPath();
133                String path2 =
134                    Util.replace(
135                        path, "DefaultRules.xml", "VERSION.txt");
136                URL resource2 =
137                    new URL(
138                        resource.getProtocol(),
139                        resource.getHost(),
140                        path2);
141
142                // Parse VERSION.txt. E.g.
143                //   Title: mondrian
144                //   Version: 3.4.9
145                // becomes {("Title", "mondrian"), ("Version", "3.4.9")}
146                final Map<String, String> map = new HashMap<String, String>();
147                final LineNumberReader r =
148                    new LineNumberReader(
149                        new InputStreamReader(resource2.openStream()));
150                try {
151                    String line;
152                    while ((line = r.readLine()) != null) {
153                        int i = line.indexOf(": ");
154                        if (i >= 0) {
155                            String key = line.substring(0, i);
156                            String value = line.substring(i + ": ".length());
157                            map.put(key, value);
158                        }
159                    }
160                } finally {
161                    r.close();
162                }
163
164                title = map.get("Title");
165                version = map.get("Version");
166                try {
167                    Integer.parseInt(version);
168                } catch (NumberFormatException e) {
169                    // Version is not a number (e.g. "TRUNK-SNAPSHOT").
170                    // Fall back on VersionMajor, VersionMinor, if present.
171                    String versionMajor = map.get("VersionMajor");
172                    String versionMinor = map.get("VersionMinor");
173                    if (versionMajor != null) {
174                        version = versionMajor;
175                    }
176                    if (versionMinor != null) {
177                        version += "." + versionMinor;
178                    }
179                }
180                vendor = map.get("Vendor");
181            } catch (IOException e) {
182                // ignore exception - it's OK if file is not found
183                Util.discard(e);
184            }
185        }
186
187        // Version from jar manifest overrides that from VERSION.txt.
188        // But ignore versions like 'TRUNK-SNAPSHOT'.
189        if (implementationVersion != null
190            && !implementationVersion.endsWith("-SNAPSHOT"))
191        {
192            version = implementationVersion;
193        }
194        return new String[] {vendor, title, version};
195    }
196
197    public MondrianServer createWithRepository(
198        RepositoryContentFinder contentFinder,
199        CatalogLocator catalogLocator)
200    {
201        if (catalogLocator == null) {
202            catalogLocator = new IdentityCatalogLocator();
203        }
204        final Repository repository;
205        if (contentFinder == null) {
206            // NOTE: registry.staticServer is initialized by calling this
207            // method; this is the only time that it is null.
208            if (staticServer != null) {
209                return staticServer;
210            }
211            repository = new ImplicitRepository();
212        } else {
213            repository = new FileRepository(contentFinder, catalogLocator);
214        }
215        return new MondrianServerImpl(this, repository, catalogLocator);
216    }
217}
218
219// End MondrianServerRegistry.java