HistoryDAO.java-sonar fixes
[aaf/authz.git] / auth / auth-cass / src / main / java / org / onap / aaf / auth / dao / cass / HistoryDAO.java
1 /**
2  * ============LICENSE_START====================================================
3  * org.onap.aaf
4  * ===========================================================================
5  * Copyright (c) 2018 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
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
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====================================================
19  *
20  */
21
22 package org.onap.aaf.auth.dao.cass;
23
24 import java.nio.ByteBuffer;
25 import java.text.SimpleDateFormat;
26 import java.util.Date;
27 import java.util.List;
28 import java.util.UUID;
29
30 import org.onap.aaf.auth.dao.AbsCassDAO;
31 import org.onap.aaf.auth.dao.CassDAOImpl;
32 import org.onap.aaf.auth.dao.Loader;
33 import org.onap.aaf.auth.env.AuthzTrans;
34 import org.onap.aaf.auth.layer.Result;
35
36 import com.datastax.driver.core.Cluster;
37 import com.datastax.driver.core.ConsistencyLevel;
38 import com.datastax.driver.core.ResultSet;
39 import com.datastax.driver.core.Row;
40
41 /**
42  * History
43  *
44  * Originally written PE3617
45  * @author Jonathan
46  *
47  * History is a special case, because we don't want Updates or Deletes...  Too likely to mess up history.
48  *
49  * Jonathan 9-9-2013 - Found a problem with using "Prepare".  You cannot prepare anything with a "now()" in it, as
50  * it is evaluated once during the prepare, and kept.  That renders any use of "now()" pointless.  Therefore
51  * the Create function needs to be run fresh everytime.
52  *
53  * Fixed in Cassandra 1.2.6 https://issues.apache.org/jira/browse/CASSANDRA-5616
54  *
55  */
56 public class HistoryDAO extends CassDAOImpl<AuthzTrans, HistoryDAO.Data> {
57     private static final String TABLE = "history";
58
59     private String[] helpers;
60
61     private HistLoader defLoader;
62
63     private AbsCassDAO<AuthzTrans, Data>.PSInfo readByUser, readBySubject, readByYRMN;
64
65     public HistoryDAO(AuthzTrans trans, Cluster cluster, String keyspace) {
66         super(trans, HistoryDAO.class.getSimpleName(),cluster,keyspace,Data.class,TABLE,ConsistencyLevel.LOCAL_ONE,ConsistencyLevel.ANY);
67         init(trans);
68     }
69
70     public HistoryDAO(AuthzTrans trans, AbsCassDAO<AuthzTrans,?> aDao) {
71         super(trans, HistoryDAO.class.getSimpleName(),aDao,Data.class,TABLE,ConsistencyLevel.LOCAL_ONE,ConsistencyLevel.ANY);
72         init(trans);
73     }
74
75
76     private static final int KEYLIMIT = 1;
77     public static class Data {
78         public UUID id;
79         public int    yr_mon;
80         public String user;
81         public String action;
82         public String target;
83         public String subject;
84         public String  memo;
85         public ByteBuffer reconstruct;
86     }
87
88     public static class HistLoader extends Loader<Data> {
89         public HistLoader(int keylimit) {
90             super(keylimit);
91         }
92
93         @Override
94         public Data load(Data data, Row row) {
95             data.id = row.getUUID(0);
96             data.yr_mon = row.getInt(1);
97             data.user = row.getString(2);
98             data.action = row.getString(3);
99             data.target = row.getString(4);
100             data.subject = row.getString(5);
101             data.memo = row.getString(6);
102             data.reconstruct = row.getBytes(7);
103             return data;
104         }
105
106         @Override
107         protected void key(Data data, int idx, Object[] obj) {
108             obj[idx]=data.id;
109         }
110
111         @Override
112         protected void body(Data data, int _idx, Object[] obj) {
113                 int idx = _idx;
114             obj[idx]=data.yr_mon;
115             obj[++idx]=data.user;
116             obj[++idx]=data.action;
117             obj[++idx]=data.target;
118             obj[++idx]=data.subject;
119             obj[++idx]=data.memo;
120             obj[++idx]=data.reconstruct;
121         }
122     };
123
124     private void init(AuthzTrans trans) {
125         // Loader must match fields order
126         defLoader = new HistLoader(KEYLIMIT);
127         helpers = setCRUD(trans, TABLE, Data.class, defLoader);
128
129         // Need a specialty Creator to handle the "now()"
130         // 9/9/2013 - Jonathan - Just great... now() is evaluated once on Client side, invalidating usage (what point is a now() from a long time in the past?
131         // Unless this is fixed, we're putting in non-prepared statement
132         // Solved in Cassandra.  Make sure you are running 1.2.6 Cassandra or later. https://issues.apache.org/jira/browse/CASSANDRA-5616
133         replace(CRUD.create, new PSInfo(trans, "INSERT INTO history (" +  helpers[FIELD_COMMAS] +
134                     ") VALUES(now(),?,?,?,?,?,?,?)",
135                     new HistLoader(0) {
136                         @Override
137                         protected void key(Data data, int idx, Object[] obj) {
138                         }
139                     },writeConsistency)
140                 );
141
142        replace(CRUD.read, new PSInfo(trans, SELECT_SP +  helpers[FIELD_COMMAS] +
143                 " FROM history WHERE id = ?", defLoader,readConsistency)
144 //                new HistLoader(2) {
145 //                    @Override
146 //                    protected void key(Data data, int idx, Object[] obj) {
147 //                        obj[idx]=data.yr_mon;
148 //                        obj[++idx]=data.id;
149 //                    }
150 //                })
151             );
152         disable(CRUD.update);
153         disable(CRUD.delete);
154
155         readByUser = new PSInfo(trans, SELECT_SP + helpers[FIELD_COMMAS] +
156                 " FROM history WHERE user = ?", defLoader,readConsistency);
157         readBySubject = new PSInfo(trans, SELECT_SP + helpers[FIELD_COMMAS] +
158                 " FROM history WHERE subject = ? and target = ? ALLOW FILTERING", defLoader,readConsistency);
159         readByYRMN = new PSInfo(trans, SELECT_SP + helpers[FIELD_COMMAS] +
160                 " FROM history WHERE yr_mon = ?", defLoader,readConsistency);
161         async(true); //TODO dropping messages with Async
162     }
163
164     public static Data newInitedData() {
165         Data data = new Data();
166         Date now = new Date();
167         // Sonar claims that SimpleDateFormat is not thread safe, so we can't be static
168         data.yr_mon = Integer.parseInt(new SimpleDateFormat("yyyyMM").format(now));
169         // data.day_time = Integer.parseInt(dayTimeFormat.format(now));
170         return data;
171     }
172
173     public void createBatch(StringBuilder sb, Data data) {
174         sb.append("INSERT INTO history (");
175         sb.append(helpers[FIELD_COMMAS]);
176         sb.append(") VALUES(now(),");
177         sb.append(data.yr_mon);
178         sb.append(",'");
179         sb.append(data.user);
180         sb.append("','");
181         sb.append(data.action);
182         sb.append("','");
183         sb.append(data.target);
184         sb.append("','");
185         sb.append(data.subject);
186         sb.append("','");
187         sb.append(data.memo);
188         sb.append("',null);\n");
189     }
190
191     public Result<List<Data>> readByYYYYMM(AuthzTrans trans, int yyyymm) {
192         Result<ResultSet> rs = readByYRMN.exec(trans, "yr_mon", yyyymm);
193         if (rs.notOK()) {
194             return Result.err(rs);
195         }
196         return extract(defLoader,rs.value,null,dflt);
197     }
198
199     /**
200      * Gets the history for a user in the specified year and month
201      * year - the year in yyyy format
202      * month -  the month in a year ...values 1 - 12
203      **/
204     public Result<List<Data>> readByUser(AuthzTrans trans, String user, int ... yyyymm) {
205         if (yyyymm.length==0) {
206             return Result.err(Status.ERR_BadData, "No or invalid yyyymm specified");
207         }
208         Result<ResultSet> rs = readByUser.exec(trans, "user", user);
209         if (rs.notOK()) {
210             return Result.err(rs);
211         }
212         return extract(defLoader,rs.value,null,yyyymm.length>0?new YYYYMM(yyyymm):dflt);
213     }
214
215     public Result<List<Data>> readBySubject(AuthzTrans trans, String subject, String target, int ... yyyymm) {
216         if (yyyymm.length==0) {
217             return Result.err(Status.ERR_BadData, "No or invalid yyyymm specified");
218         }
219         Result<ResultSet> rs = readBySubject.exec(trans, "subject", subject, target);
220         if (rs.notOK()) {
221             return Result.err(rs);
222         }
223         return extract(defLoader,rs.value,null,yyyymm.length>0?new YYYYMM(yyyymm):dflt);
224     }
225
226     private class YYYYMM implements Accept<Data> {
227         private int[] yyyymm;
228         public YYYYMM(int[] yyyymm) {
229             this.yyyymm = yyyymm;
230         }
231         @Override
232         public boolean ok(Data data) {
233             int dym = data.yr_mon;
234             for (int ym:yyyymm) {
235                 if (dym==ym) {
236                     return true;
237                 }
238             }
239             return false;
240         }
241
242     };
243
244 }