2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.openecomp.mso.properties;
24 import java.io.FileNotFoundException;
25 import java.io.IOException;
26 import java.io.Serializable;
27 import java.util.LinkedList;
28 import java.util.List;
29 import java.util.Map.Entry;
30 import java.util.concurrent.ConcurrentHashMap;
31 import java.util.concurrent.TimeUnit;
32 import java.util.concurrent.locks.ReentrantReadWriteLock;
34 import javax.ejb.ConcurrencyManagement;
35 import javax.ejb.ConcurrencyManagementType;
36 import javax.ejb.LocalBean;
37 import javax.ejb.Schedule;
38 import javax.ejb.Singleton;
39 import javax.ws.rs.GET;
40 import javax.ws.rs.Path;
41 import javax.ws.rs.PathParam;
42 import javax.ws.rs.Produces;
43 import javax.ws.rs.core.Response;
45 import org.openecomp.mso.logger.MessageEnum;
46 import org.openecomp.mso.logger.MsoLogger;
47 import org.openecomp.mso.utils.CryptoUtils;
50 * This EJB Singleton class returns an instance of the mso properties for a specified file.
51 * This class can handle many config at the same time and is thread safe.
52 * This instance is a copy of the one cached so it can be modified or reloaded without impacting the others class using
54 * The mso properties files loaded and cached here will be reloaded every X second (it's configurable with the init
56 * This class can be used as an EJB or can be instantiated directly as long as the EJB has been initialized for the current
57 * module. Locks are made manually and not using EJB locks to allow this.
61 @Singleton(name = "MsoPropertiesFactory")
62 @ConcurrencyManagement(ConcurrencyManagementType.BEAN)
65 public class MsoPropertiesFactory implements Serializable {
67 private static final long serialVersionUID = 4365495305496742113L;
69 protected static String prefixMsoPropertiesPath = System.getProperty ("mso.config.path");
71 private static MsoLogger LOGGER = MsoLogger.getMsoLogger (MsoLogger.Catalog.GENERAL);
73 // Keep a static copy of properties for global usage
74 private static final ConcurrentHashMap <String, MsoPropertiesParameters> msoPropertiesCache;
77 if (prefixMsoPropertiesPath == null) {
78 // Hardcode if nothing is received
79 prefixMsoPropertiesPath = "";
81 msoPropertiesCache = new ConcurrentHashMap <String, MsoPropertiesParameters> ();
84 private static final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock ();
86 public MsoPropertiesFactory () {
90 private boolean isJsonFile(String propertiesFilePath) {
91 return propertiesFilePath.endsWith(".json");
95 private boolean isJavaPropertiesFile (String propertiesFilePath) {
96 return propertiesFilePath.endsWith(".properties");
99 private MsoPropertiesParameters createObjectType (MsoPropertiesParameters msoPropParams, String propertiesFilePath) throws MsoPropertiesException, IOException {
102 if (this.isJavaPropertiesFile(propertiesFilePath)) {
104 msoPropParams.msoProperties = new MsoJavaProperties();
105 msoPropParams.msoPropertiesType = MsoPropertiesParameters.MsoPropertiesType.JAVA_PROP;
106 } else if (this.isJsonFile(propertiesFilePath)) {
108 msoPropParams.msoProperties = new MsoJsonProperties();
109 msoPropParams.msoPropertiesType = MsoPropertiesParameters.MsoPropertiesType.JSON_PROP;
111 throw new MsoPropertiesException("Unable to load the MSO properties file because format is not recognized (only .json or .properties): " + propertiesFilePath);
114 msoPropParams.msoProperties.loadPropertiesFile (propertiesFilePath);
116 return msoPropParams;
118 if (msoPropParams.msoProperties!=null) {
119 msoPropParams.refreshCounter = msoPropParams.msoProperties.getAutomaticRefreshInMinutes();
126 * This method is used to create a MsoProperties file cache and factory.
127 * The ID is kept in cache even if the config fails to be loaded.
128 * This is used to maintain the config ID until someone fixes the config file.
130 * @param msoPropertiesID A string representing the key of the config
131 * @param propertiesFilePath The mso properties file to load
133 * @throws MsoPropertiesException In case of issues with the mso properties loading
135 * @see MsoPropertiesFactory#getMsoJavaProperties()
136 * @see MsoPropertiesFactory#getMsoJsonProperties()
138 public void initializeMsoProperties (String msoPropertiesID,
139 String propertiesFilePath) throws MsoPropertiesException {
141 rwl.writeLock ().lock ();
143 String msoPropPath="none";
144 MsoPropertiesParameters msoPropertiesParams=new MsoPropertiesParameters();
146 msoPropPath = prefixMsoPropertiesPath + propertiesFilePath;
147 if (msoPropertiesCache.get (msoPropertiesID) != null) {
148 throw new MsoPropertiesException ("The factory contains already an instance of this mso properties: "
151 // Create the global MsoProperties object
152 msoPropertiesParams = createObjectType(msoPropertiesParams, msoPropPath);
154 } catch (FileNotFoundException e) {
155 throw new MsoPropertiesException ("Unable to load the MSO properties file because it has not been found:"
158 } catch (IOException e) {
159 throw new MsoPropertiesException ("Unable to load the MSO properties file because IOException occurs: "
162 // put it in all cases, just to not forget about him and attempt a default reload
163 msoPropertiesCache.put (msoPropertiesID, msoPropertiesParams);
164 rwl.writeLock ().unlock ();
168 public void removeMsoProperties (String msoPropertiesID) throws MsoPropertiesException {
170 rwl.writeLock ().lock ();
172 if (MsoPropertiesFactory.msoPropertiesCache.remove (msoPropertiesID) == null) {
173 throw new MsoPropertiesException ("Mso properties not found in cache:" + msoPropertiesID);
176 rwl.writeLock ().unlock ();
181 * This method clears all the configs in cache, the factory will then be free of any config.
183 * @see MsoPropertiesFactory#initializeMsoProperties(String, String)
185 public void removeAllMsoProperties () {
187 rwl.writeLock ().lock ();
189 MsoPropertiesFactory.msoPropertiesCache.clear ();
191 rwl.writeLock ().unlock ();
196 * THis method can be used to change the file and timer fields of an existing MSO properties file.
198 * @param msoPropertiesID The MSO properties ID
199 * @param newMsoPropPath The new file Path
200 * @throws MsoPropertiesException In case of the MSO Properties is not found in cache
202 public void changeMsoPropertiesFilePath (String msoPropertiesID,
203 String newMsoPropPath) throws MsoPropertiesException {
205 rwl.writeLock ().lock ();
207 MsoPropertiesParameters msoPropInCache = MsoPropertiesFactory.msoPropertiesCache.get (msoPropertiesID);
209 if (msoPropInCache != null) {
210 msoPropInCache.msoProperties.propertiesFileName = prefixMsoPropertiesPath + newMsoPropPath;
213 throw new MsoPropertiesException ("Mso properties not found in cache:" + msoPropertiesID);
216 rwl.writeLock ().unlock ();
220 private AbstractMsoProperties getAndCloneProperties(String msoPropertiesID, MsoPropertiesParameters.MsoPropertiesType type) throws MsoPropertiesException {
221 rwl.readLock ().lock ();
223 MsoPropertiesParameters msoPropInCache = MsoPropertiesFactory.msoPropertiesCache.get (msoPropertiesID);
224 if (msoPropInCache == null) {
225 throw new MsoPropertiesException ("Mso properties not found in cache:" + msoPropertiesID);
227 if (type.equals(msoPropInCache.msoPropertiesType)) {
228 return msoPropInCache.msoProperties.clone ();
230 throw new MsoPropertiesException ("Mso properties is not "+type.name()+" properties type:" + msoPropertiesID);
235 rwl.readLock ().unlock ();
240 * Get the MSO Properties (As Java Properties) as a copy of the mso properties cache.
241 * The object returned can therefore be modified.
243 * @return A copy of the mso properties, properties class can be empty if the file has not been read properly
244 * @throws MsoPropertiesException If the mso properties does not exist in the cache
246 public MsoJavaProperties getMsoJavaProperties (String msoPropertiesID) throws MsoPropertiesException {
248 return (MsoJavaProperties)getAndCloneProperties(msoPropertiesID,MsoPropertiesParameters.MsoPropertiesType.JAVA_PROP);
252 * Get the MSO Properties (As JSON Properties) as a copy of the mso properties cache.
253 * The object returned can therefore be modified.
255 * @return A copy of the mso properties, properties class can be empty if the file has not been read properly
256 * @throws MsoPropertiesException If the mso properties does not exist in the cache
258 public MsoJsonProperties getMsoJsonProperties (String msoPropertiesID) throws MsoPropertiesException {
260 return (MsoJsonProperties)getAndCloneProperties(msoPropertiesID,MsoPropertiesParameters.MsoPropertiesType.JSON_PROP);
264 * Get all MSO Properties as a copy of the mso properties cache.
265 * The objects returned can therefore be modified.
267 * @return A List of copies of the mso properties, can be empty
269 public List <AbstractMsoProperties> getAllMsoProperties () {
271 List <AbstractMsoProperties> resultList = new LinkedList <AbstractMsoProperties> ();
272 rwl.readLock ().lock ();
275 for (MsoPropertiesParameters msoProp:MsoPropertiesFactory.msoPropertiesCache.values ()) {
276 resultList.add(msoProp.msoProperties.clone());
281 rwl.readLock ().unlock ();
287 * This method is not intended to be called, it's used to refresh the config automatically
289 * @return true if Properties have been reloaded, false otherwise
291 @Schedule(minute = "*/1", hour = "*", persistent = false)
292 public boolean reloadMsoProperties () {
293 AbstractMsoProperties msoPropInCache = null;
295 if (!rwl.writeLock ().tryLock () && !rwl.writeLock ().tryLock (30L, TimeUnit.SECONDS)) {
296 LOGGER.debug ("Busy write lock on mso properties factory, skipping the reloading");
299 } catch (InterruptedException e1) {
300 LOGGER.debug ("Interrupted while trying to acquire write lock on mso properties factory, skipping the reloading");
301 Thread.currentThread ().interrupt ();
305 for (Entry <String, MsoPropertiesParameters> entryMsoPropTimer : MsoPropertiesFactory.msoPropertiesCache.entrySet ()) {
307 if (entryMsoPropTimer.getValue ().refreshCounter <= 1) {
308 // It's time to reload the config
309 msoPropInCache = MsoPropertiesFactory.msoPropertiesCache.get (entryMsoPropTimer.getKey ()).msoProperties;
311 AbstractMsoProperties oldProps = msoPropInCache.clone ();
312 msoPropInCache.reloadPropertiesFile ();
313 entryMsoPropTimer.getValue().refreshCounter=entryMsoPropTimer.getValue().msoProperties.getAutomaticRefreshInMinutes();
315 if (!msoPropInCache.equals (oldProps)) {
316 LOGGER.info (MessageEnum.LOAD_PROPERTIES_SUC, msoPropInCache.getPropertiesFileName (), "", "");
318 } catch (FileNotFoundException ef) {
319 LOGGER.error (MessageEnum.NO_PROPERTIES, msoPropInCache.propertiesFileName, "", "", MsoLogger.ErrorCode.PermissionError, "", ef);
320 } catch (Exception e) {
321 LOGGER.error (MessageEnum.LOAD_PROPERTIES_FAIL, msoPropInCache.propertiesFileName, "", "", MsoLogger.ErrorCode.BusinessProcesssError, "", e);
325 --entryMsoPropTimer.getValue().refreshCounter;
329 } catch (Exception e) {
330 LOGGER.error (MessageEnum.LOAD_PROPERTIES_FAIL, "Unknown. Global issue while reloading", "", "", MsoLogger.ErrorCode.BusinessProcesssError, "", e);
333 rwl.writeLock ().unlock ();
338 * This method can be used to known if the MSO properties instance hold is different from the one in cache
340 * @param msoPropertiesID The MSO properties ID
341 * @param oldMsoProperties The MSO Properties instance that must be compared to
342 * @return True if they are the same, false otherwise
343 * @throws MsoPropertiesException
345 public boolean propertiesHaveChanged (String msoPropertiesID, AbstractMsoProperties oldMsoProperties) throws MsoPropertiesException {
346 rwl.readLock ().lock ();
348 if (MsoPropertiesFactory.msoPropertiesCache.get (msoPropertiesID) == null) {
349 throw new MsoPropertiesException ("Mso properties not found in cache:" + msoPropertiesID);
352 AbstractMsoProperties msoPropInCache = MsoPropertiesFactory.msoPropertiesCache.get (msoPropertiesID).msoProperties;
353 if (oldMsoProperties != null) {
354 return !oldMsoProperties.equals (msoPropInCache);
356 return msoPropInCache != null;
359 rwl.readLock ().unlock ();
365 @Produces("text/plain")
366 public Response showProperties () {
368 List <AbstractMsoProperties> listMsoProp = this.getAllMsoProperties ();
369 StringBuffer response = new StringBuffer ();
371 if (listMsoProp.isEmpty ()) {
372 response.append ("No file defined");
375 for (AbstractMsoProperties properties : listMsoProp) {
377 response.append(properties.toString());
380 return Response.status (200).entity (response).build ();
384 @Path("/encrypt/{value}/{cryptKey}")
385 @Produces("text/plain")
386 public Response encryptProperty (@PathParam("value") String value, @PathParam("cryptKey") String cryptKey) {
388 String encryptedValue = CryptoUtils.encrypt (value, cryptKey);
389 return Response.status (200).entity (encryptedValue).build ();
390 } catch (Exception e) {
391 LOGGER.error (MessageEnum.GENERAL_EXCEPTION_ARG, "Encryption error", "", "", MsoLogger.ErrorCode.BusinessProcesssError, "Error in encrypting property", e);
392 return Response.status (500).entity (e.getMessage ()).build ();