001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    
018    package org.apache.geronimo.system.resolver;
019    
020    import java.io.File;
021    import java.io.FileInputStream;
022    import java.io.IOException;
023    import java.io.FileOutputStream;
024    import java.util.Collection;
025    import java.util.HashMap;
026    import java.util.Map;
027    import java.util.Properties;
028    
029    import org.apache.geronimo.gbean.GBeanInfo;
030    import org.apache.geronimo.gbean.GBeanInfoBuilder;
031    import org.apache.geronimo.kernel.repository.Artifact;
032    import org.apache.geronimo.kernel.repository.ArtifactManager;
033    import org.apache.geronimo.kernel.repository.ArtifactResolver;
034    import org.apache.geronimo.kernel.repository.DefaultArtifactResolver;
035    import org.apache.geronimo.kernel.repository.ListableRepository;
036    import org.apache.geronimo.system.serverinfo.ServerInfo;
037    
038    /**
039     * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
040     */
041    public class ExplicitDefaultArtifactResolver extends DefaultArtifactResolver implements LocalAliasedArtifactResolver {
042        private static final String COMMENT = "#You can use this file to indicate that you want to substitute one module for another.\n" +
043                "#format is oldartifactid=newartifactId e.g.\n" +
044                "#org.apache.geronimo.configs/transaction//car=org.apache.geronimo.configs/transaction-jta11/1.2-SNAPSHOT/car\n" +
045                "#versions can be ommitted on the left side but not the right.\n" +
046                "#This can also specify explicit versions in the same format.";
047    
048        private final String artifactAliasesFile;
049        private final ServerInfo serverInfo;
050    
051        public ExplicitDefaultArtifactResolver(String versionMapLocation,
052                                               ArtifactManager artifactManager,
053                                               Collection<? extends ListableRepository> repositories,
054                                               ServerInfo serverInfo ) throws IOException {
055            this(versionMapLocation, artifactManager, repositories, null, serverInfo);
056        }
057    
058        public ExplicitDefaultArtifactResolver(String versionMapLocation,
059                ArtifactManager artifactManager,
060                Collection<? extends ListableRepository> repositories,
061                Map<String, String> additionalAliases,
062                ServerInfo serverInfo ) throws IOException {
063            super(artifactManager, repositories, buildExplicitResolution(versionMapLocation, additionalAliases, serverInfo));
064            this.artifactAliasesFile = versionMapLocation;
065            this.serverInfo = serverInfo;
066        }
067    
068    
069        public String getArtifactAliasesFile() {
070            return artifactAliasesFile;
071        }
072    
073        private static Map<Artifact, Artifact> buildExplicitResolution(String versionMapLocation, Map<String, String> additionalAliases, ServerInfo serverInfo) throws IOException {
074            if (versionMapLocation == null) {
075                return null;
076            }
077            Properties properties = new Properties();
078            File location = serverInfo == null? new File(versionMapLocation): serverInfo.resolveServer(versionMapLocation);
079            if (location.exists()) {
080                FileInputStream in = new FileInputStream(location);
081                try {
082                    properties.load(in);
083                } finally {
084                    in.close();
085                }
086            }
087            if (additionalAliases != null) {
088                properties.putAll(additionalAliases);
089            }
090            return propertiesToArtifactMap(properties);
091        }
092    
093        private static Map<Artifact, Artifact> propertiesToArtifactMap(Properties properties) {
094            Map<Artifact, Artifact> explicitResolution = new HashMap<Artifact, Artifact>();
095            for (Map.Entry<Object, Object> entry : properties.entrySet()) {
096                String key = (String) entry.getKey();
097                String resolvedString = (String) entry.getValue();
098                Artifact source = Artifact.createPartial(key);
099                Artifact resolved = Artifact.create(resolvedString);
100                explicitResolution.put(source, resolved);
101            }
102            return explicitResolution;
103        }
104    
105        private static void saveExplicitResolution(Map<Artifact, Artifact> artifactMap, String versionMapLocation, ServerInfo serverInfo) throws IOException {
106            if (versionMapLocation == null) {
107                return;
108            }
109            File location = serverInfo == null? new File(versionMapLocation): serverInfo.resolveServer(versionMapLocation);
110            if (!location.exists()) {
111                File parent = location.getParentFile();
112                if (!parent.exists() && !parent.mkdirs()) {
113                    throw new IOException("Could not create directory for artifact aliases at " + parent);
114                }
115            }
116            FileOutputStream in = new FileOutputStream(location);
117            Properties properties = artifactMapToProperties(artifactMap);
118            try {
119                properties.store(in, COMMENT);
120            } finally {
121                in.close();
122            }
123        }
124    
125        private static Properties artifactMapToProperties(Map<Artifact, Artifact> artifactMap) {
126            Properties properties = new Properties();
127            for (Map.Entry<Artifact, Artifact> entry: artifactMap.entrySet()) {
128                properties.setProperty(entry.getKey().toString(), entry.getValue().toString());
129            }
130            return properties;
131        }
132    
133        /**
134         * Add some more artifact aliases.  The plugin installer calls this
135         * TODO when a plugin is uninstalled, remove the aliases?
136         * @param properties Properties object containing the new aliases
137         * @throws IOException if the modified aliases map cannot be saved.                                            
138         */
139        public synchronized void addAliases(Properties properties) throws IOException {
140            Map<Artifact, Artifact> explicitResolutions = propertiesToArtifactMap(properties);
141            getExplicitResolution().putAll(explicitResolutions);
142            saveExplicitResolution(getExplicitResolution(), artifactAliasesFile, serverInfo);
143        }
144    
145        public static final GBeanInfo GBEAN_INFO;
146    
147        static {
148            GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic(ExplicitDefaultArtifactResolver.class, "ArtifactResolver");
149            infoFactory.addAttribute("versionMapLocation", String.class, true, true);
150            infoFactory.addAttribute("additionalAliases", Map.class, true, true);
151            infoFactory.addReference("ArtifactManager", ArtifactManager.class, "ArtifactManager");
152            infoFactory.addReference("Repositories", ListableRepository.class, "Repository");
153            infoFactory.addReference("ServerInfo", ServerInfo.class, "GBean");
154            infoFactory.addInterface(ArtifactResolver.class);
155    
156            infoFactory.setConstructor(new String[]{
157                    "versionMapLocation",
158                    "ArtifactManager",
159                    "Repositories",
160                    "additionalAliases",
161                    "ServerInfo"
162            });
163    
164    
165            GBEAN_INFO = infoFactory.getBeanInfo();
166    
167        }
168    
169        public static GBeanInfo getGBeanInfo() {
170            return GBEAN_INFO;
171        }
172    }