View Javadoc

1   /*
2    * Copyright 2005 Jukka Zitting <jz@yukatan.fi>
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package net.sf.exorcist.midgard;
17  
18  import java.io.File;
19  import java.io.FileFilter;
20  import java.io.FileInputStream;
21  import java.io.FileOutputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.OutputStream;
25  import java.sql.Connection;
26  import java.sql.DriverManager;
27  import java.sql.PreparedStatement;
28  import java.sql.ResultSet;
29  import java.sql.SQLException;
30  import java.util.ArrayList;
31  import java.util.HashSet;
32  import java.util.Iterator;
33  import java.util.Set;
34  
35  import javax.xml.parsers.ParserConfigurationException;
36  
37  import net.sf.exorcist.api.ContentException;
38  import net.sf.exorcist.api.ContentImporter;
39  import net.sf.exorcist.api.ContentState;
40  
41  import org.apache.commons.codec.digest.DigestUtils;
42  import org.xml.sax.SAXException;
43  
44  
45  public class MidgardImporter implements ContentImporter {
46  
47      private static class LinkInfo {
48          int id;
49          String table;
50          String property;
51          String guid;
52          public LinkInfo(int i, String t, String p, String g) {
53              id = i; table = t; property = p; guid = g;
54          }
55      }
56  
57      private ContentState state;
58  
59      private DOMWalker walker;
60  
61      private Connection connection;
62      
63      private GUIDResolver guidFinder;
64  
65      private MidgardSchema schema;
66  
67      private int sitegroup;
68  
69      private String database;
70  
71      private String username;
72  
73      private String password;
74  
75      private String filepath;
76  
77      private boolean delete = false;
78  
79      private Set links;
80      private Set deletes;
81  
82      public void setSchema(String file)
83              throws IOException, ParserConfigurationException, SAXException {
84          schema = new MidgardSchema();
85          schema.parse(new FileInputStream(file));
86      }
87  
88      public void setSitegroup(String sitegroup) {
89          this.sitegroup = Integer.parseInt(sitegroup);
90      }
91  
92      public void setDatabase(String database) {
93          this.database = database;
94      }
95  
96      public void setUsername(String username) {
97          this.username = username;
98      }
99  
100     public void setPassword(String password) {
101         this.password = password;
102     }
103 
104     public void setFilepath(String filepath) {
105         this.filepath = filepath;
106     }
107 
108     public void setDelete(boolean delete) {
109         this.delete = delete;
110     }
111 
112     private static int guidcount = 0;
113     
114     public void importObject(MidgardSchema schema, int parent)
115             throws SQLException, IOException {
116         MidgardType type = schema.getTypeByName(walker.getName());
117         ArrayList properties = new ArrayList();
118         Iterator iterator = type.getProperties();
119         while (iterator.hasNext()) {
120             MidgardProperty property = (MidgardProperty) iterator.next();
121             if (property != type.getPrimary() && property != type.getParent()) {
122                properties.add(property);
123             }
124         }
125 
126         System.out.println(parent);
127         String guid = walker.getContent("guid");
128         if (guid == null || guid.length() == 0) {
129             guid = DigestUtils.md5Hex("exorcist:" + System.currentTimeMillis() + ":" + guidcount++);
130         }
131         int id = guidFinder.resolveGUID(guid);
132         StringBuffer sql = new StringBuffer();
133         if (id == 0 && type.getTable().endsWith("_i")) {
134             sql.append("SELECT id FROM ");
135             sql.append(type.getTable());
136             sql.append(" WHERE ");
137             sql.append(type.getParent().getName());
138             sql.append(" = ? AND lang = ?");
139             PreparedStatement ps = connection.prepareStatement(sql.toString());
140             try {
141                 ps.setInt(1, parent);
142                 ps.setInt(2, Integer.parseInt(walker.getContent("lang")));
143                 ResultSet rs = ps.executeQuery();
144                 try {
145                     if (rs.next()) {
146                         id = rs.getInt(1);
147                     }
148                 } finally {
149                     rs.close();
150                 }
151             } catch (NumberFormatException e) {
152             } finally {
153                 ps.close();
154             }
155             sql = new StringBuffer();
156         }
157         if (id != 0) {
158             sql.append("UPDATE ");
159             sql.append(type.getTable());
160             sql.append(" SET sitegroup=?");
161             if (type.getParent() != null && parent != 0) {
162                 sql.append(",");
163                 sql.append(type.getParent().getName());
164                 sql.append("=?");
165             }
166             for (int i = 0; i < properties.size(); i++) {
167                 sql.append(",");
168                 sql.append(((MidgardProperty) properties.get(i)).getName());
169                 sql.append("=?");
170             }
171             sql.append(" WHERE ");
172             sql.append(type.getPrimary().getName());
173             sql.append("=?");
174         } else {
175             sql.append("INSERT INTO ");
176             sql.append(type.getTable());
177             sql.append("(sitegroup");
178             if (type.getParent() != null) {
179                 sql.append(",");
180                 sql.append(type.getParent().getName());
181             }
182             for (int i = 0; i < properties.size(); i++) {
183                 sql.append(",");
184                 sql.append(((MidgardProperty) properties.get(i)).getName());
185             }
186             sql.append(") VALUES (?");
187             if (type.getParent() != null) {
188                 if (parent != 0) {
189                     sql.append(",?");
190                 } else {
191                     sql.append(",0");
192                 }
193             }
194             for (int i = 0; i < properties.size(); i++) {
195                 sql.append(",?");
196             }
197             sql.append(")");
198         }
199 
200         PreparedStatement ps = connection.prepareStatement(
201                 sql.toString(), PreparedStatement.RETURN_GENERATED_KEYS);
202         try {
203             int col = 1;
204             ps.setInt(col++, sitegroup);
205             if (type.getParent() != null && parent != 0) {
206                 ps.setInt(col++, parent);
207             }
208             for (int i = 0; i < properties.size(); i++) {
209                 MidgardProperty property = (MidgardProperty) properties.get(i);
210                 String value = walker.getContent(property.getName());
211                 if (property.getLink() != null) {
212                     int link = guidFinder.resolveGUID(value);
213                     ps.setInt(col++, link);
214                 } else if ("integer".equals(property.getType())) {
215                     int val = 0;
216                     try {
217                         val = Integer.parseInt(value);
218                     } catch (NumberFormatException e) {
219                     }
220                     ps.setInt(col++, val);
221                 } else {
222                     ps.setString(col++, value);
223                 }
224             }
225             if (id != 0) {
226                 ps.setInt(col++, id);
227             }
228             ps.executeUpdate();
229             if (id == 0) {
230                 ResultSet rs = ps.getGeneratedKeys();
231                 try {
232                     if (rs.next()) {
233                         id = rs.getInt(1);
234                         if (guid == null) {
235                             guid = guidFinder.findGUID(type.getTable(), id);
236                         }
237                         PreparedStatement insert = connection.prepareStatement(
238                                 "INSERT INTO repligard (realm, id, guid,"
239                                 + " changed, updated, action, sitegroup)"
240                                 + " VALUES (?, ?, ?, NOW(), NOW(), 'created', ?)");
241                         try {
242                             insert.setString(1, type.getTable());
243                             insert.setInt(2, id);
244                             insert.setString(3, guid);
245                             insert.setInt(4, sitegroup);
246                             insert.executeUpdate();
247                         } finally {
248                             insert.close();
249                         }
250                     }
251                 } finally {
252                     rs.close();
253                 }
254             }
255         } finally {
256             ps.close();
257         }
258         deletes.remove(guid);
259 
260         // Mark unresolved links
261         for (int i = 0; i < properties.size(); i++) {
262             MidgardProperty property = (MidgardProperty) properties.get(i);
263             if (property.getLink() != null) {
264                 String value = walker.getContent(property.getName());
265                 int link = guidFinder.resolveGUID(value);
266                 if (link == 0) {
267                     links.add(new LinkInfo(id, type.getTable(), property.getName(), value)); 
268                 }
269             }
270         }
271         
272         if (id != 0) {
273             importParameters(type.getTable(), id);
274             importAttachments(type.getTable(), id);
275 
276             Iterator children = type.getChildren();
277             while (children.hasNext()) {
278                 MidgardType child = (MidgardType) children.next();
279                 sql = new StringBuffer();
280                 sql.append("SELECT ");
281                 sql.append(child.getPrimary().getName());
282                 sql.append(" FROM ");
283                 sql.append(child.getTable());
284                 sql.append(" WHERE sitegroup=? AND ");
285                 sql.append(child.getParent().getName());
286                 sql.append("=?");
287                 ps = connection.prepareStatement(sql.toString());
288                 try {
289                     ps.setInt(1, sitegroup);
290                     ps.setInt(2, id);
291                     ResultSet rs = ps.executeQuery();
292                     try {
293                         while (rs.next()) {
294                             guid = guidFinder.findGUID(child.getTable(), rs.getInt(1));
295                             deletes.add(guid);
296                         }
297                     } finally {
298                         rs.close();
299                     }
300                 } finally {
301                     ps.close();
302                 }
303 
304                 Object current = walker.getCurrent();
305                 while (walker.iterateElements(child.getName(), current)) {
306                     importObject(schema, id);
307                 }
308             }
309         }
310     }
311     
312     public void deleteObject(MidgardSchema schema, String guid) {
313         System.out.println("Removing " + guid);
314         // TODO
315     }
316 
317     public void importContent(ContentState state) throws ContentException {
318         try {
319             if (schema == null) {
320                 InputStream input = MidgardExporter.class.getClassLoader()
321                 .getResourceAsStream("net/sf/exorcist/midgard/midgard.xml");
322                 try {
323                     schema = new MidgardSchema();
324                     schema.parse(input);
325                 } finally {
326                     input.close();
327                 }
328             }
329 
330             this.state = state;
331             InputStream xml = state.getContent(); 
332             this.walker = new DOMWalker(xml, "http://ns.yukatan.fi/2005/midgard");
333             this.connection =
334                 DriverManager.getConnection(database, username, password);
335             this.guidFinder = new GUIDResolver(connection);
336             this.deletes = new HashSet();
337             this.links = new HashSet();
338 
339             Iterator types = schema.getTypes();
340             while (types.hasNext()) {
341                 MidgardType child = (MidgardType) types.next();
342                 Object current = walker.getCurrent();
343                 while (walker.iterateElements(child.getName(), current)) {
344                     importObject(schema, 0);
345                 }
346             }
347             if (delete) {
348                 Iterator guids = deletes.iterator();
349                 while (guids.hasNext()) {
350                     deleteObject(schema, (String) guids.next());
351                 }
352             }
353 
354             Iterator lis = links.iterator();
355             while (lis.hasNext()) {
356                 LinkInfo info = (LinkInfo) lis.next();
357                 int tid = guidFinder.resolveGUID(info.guid);
358                 if (tid != 0) {
359                     PreparedStatement ps = connection.prepareStatement(
360                             "UPDATE " + info.table + " SET " + info.property
361                             + " = " + tid + " WHERE id = " + info.id);
362                     try {
363                         ps.executeUpdate();
364                     } finally {
365                         ps.close();
366                     }
367                 } else {
368                     System.err.println("Broken link: " + info.guid);
369                 }
370             }
371 
372             xml.close();
373         } catch (SQLException e) {
374             throw new ContentException(e);
375         } catch (IOException e) {
376             throw new ContentException(e);
377         } catch (SAXException e) {
378             throw new ContentException(e);
379         } catch (ParserConfigurationException e) {
380             throw new ContentException(e);
381         }
382     }
383 
384     private void importParameters(String table, int id) throws SQLException {
385         PreparedStatement ps = connection.prepareStatement(
386             "SELECT id FROM record_extension WHERE tablename=? AND oid=? AND sitegroup=?");
387         try {
388             ps.setString(1, table);
389             ps.setInt(2, id);
390             ps.setInt(3, sitegroup);
391             ResultSet rs = ps.executeQuery();
392             try {
393                 while (rs.next()) {
394                     deletes.add(guidFinder.findGUID("record_extension", rs.getInt(1)));
395                 }
396             } finally {
397                 rs.close();
398             }
399         } finally {
400             ps.close();
401         }
402 
403         Object context = walker.getCurrent();
404         while (walker.iterateElements("parameter", context)) {
405             int pid = 0;
406             String guid = walker.getContent("guid");
407             PreparedStatement select = connection.prepareStatement(
408                     "SELECT id FROM repligard WHERE guid=?");
409             select.setString(1, guid);
410             ResultSet rs = select.executeQuery();
411             try {
412                 PreparedStatement update;
413                 if (!rs.next()) {
414                     update = connection.prepareStatement(
415                             "INSERT INTO record_extension (value,"
416                             + " tablename, oid, domain, name, sitegroup)"
417                             + " VALUES (?, ?, ?, ?, ?, ?)", PreparedStatement.RETURN_GENERATED_KEYS);
418                 } else {
419                     pid = rs.getInt(1);
420                     update = connection.prepareStatement(
421                             "UPDATE record_extension SET value = ?,"
422                             + " tablename = ?, oid = ?, domain = ?,"
423                             + " name = ?, sitegroup = ? WHERE id=?");
424                 }
425                 update.setString(1, walker.getContent("value"));
426                 update.setString(2, table);
427                 update.setInt(3, id);
428                 update.setString(4, walker.getContent("domain"));
429                 update.setString(5, walker.getContent("name"));
430                 update.setInt(6, sitegroup);
431                 if (pid != 0) {
432                     update.setInt(7, pid);
433                 }
434                 update.executeUpdate();
435                 if (pid == 0) {
436                     ResultSet keys = update.getGeneratedKeys();
437                     try {
438                         if (keys.next()) {
439                             pid = keys.getInt(1);
440                             if (guid == null || guid.length() == 0) {
441                                 guid = guidFinder.findGUID("record_extension", pid);
442                             }
443                             PreparedStatement insert = connection.prepareStatement(
444                                     "INSERT INTO repligard"
445                                     + " (realm, id, guid, sitegroup)"
446                                     + " VALUES (?, ?, ?, ?)");
447                             insert.setString(1, "record_extension");
448                             insert.setInt(2, pid);
449                             insert.setString(3, guid);
450                             insert.setInt(4, sitegroup);
451                             insert.executeUpdate();
452                             insert.close();
453                         }
454                     } finally {
455                         keys.close();
456                     }
457                 }
458                 update.close();
459             } finally {
460                 rs.close();
461             }
462             select.close();
463             deletes.remove(guid);
464         }
465     }
466 
467     private void importAttachments(String table, int id)
468             throws IOException, SQLException {
469         PreparedStatement ps = connection.prepareStatement(
470                 "SELECT id FROM blobs WHERE ptable=? AND pid=? AND sitegroup=?");
471         try {
472             ps.setString(1, table);
473             ps.setInt(2, id);
474             ps.setInt(3, sitegroup);
475             ResultSet rs = ps.executeQuery();
476             try {
477                 while (rs.next()) {
478                     deletes.add(guidFinder.findGUID("blobs", rs.getInt(1)));
479                 }
480             } finally {
481                 rs.close();
482             }
483         } finally {
484             ps.close();
485         }
486 
487         Object context = walker.getCurrent();
488         while (walker.iterateElements("attachment", context)) {
489             String guid = walker.getContent("guid");
490             String name = walker.getContent("name");
491             String hash = walker.getContent("location");
492             int blobid = 0;
493             String location = null;
494             if (guid != null && guid.length() > 0) {
495                 ps = connection.prepareStatement(
496                         "SELECT blobs.id, blobs.location FROM blobs, repligard"
497                         + " WHERE blobs.id=repligard.id AND repligard.realm='blobs'"
498                         + " AND guid=?");
499                 try {
500                     ps.setString(1, guid);
501                     ResultSet rs = ps.executeQuery();
502                     try {
503                         if (rs.next()) {
504                             blobid = rs.getInt(1);
505                             location = rs.getString(2);
506                         }
507                     } finally {
508                         rs.close();
509                     }
510                 } finally {
511                     ps.close();
512                 }
513             }
514             if (blobid != 0) {
515                 ps = connection.prepareStatement(
516                         "UPDATE blobs SET score = ?, title = ?,"
517                         + " location = ?, mimetype = ?, author = ?,"
518                         + " created = ?, ptable = ?, pid = ?, name = ?,"
519                         + " lang = 0, sitegroup = ? WHERE id = ?");
520             } else {
521                 ps = connection.prepareStatement(
522                         "INSERT INTO blobs (score, title, location,"
523                         + " mimetype, author, created, ptable, pid,"
524                         + " name, lang, sitegroup)"
525                         + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?)",
526                         PreparedStatement.RETURN_GENERATED_KEYS);
527                 location =
528                     hash.toUpperCase().charAt(0) + "/"
529                     + hash.toUpperCase().charAt(1) + "/"
530                     + hash.substring(0, 32);
531             }
532             try {
533                 ps.setInt(1, Integer.parseInt(walker.getContent("score")));
534             } catch (NumberFormatException e) {
535                 ps.setInt(1, 0);
536             }
537             ps.setString(2, walker.getContent("title"));
538             ps.setString(3, location);
539             ps.setString(4, walker.getContent("mimetype"));
540             ps.setInt(5, guidFinder.resolveGUID(walker.getContent("author")));
541             ps.setString(6, walker.getContent("created"));
542             ps.setString(7, table);
543             ps.setInt(8, id);
544             ps.setString(9, name);
545             ps.setInt(10, sitegroup);
546             if (blobid != 0) {
547                 ps.setInt(11, blobid);
548             }
549             ps.executeUpdate();
550             if (blobid == 0) {
551                 ResultSet keys = ps.getGeneratedKeys();
552                 try {
553                     if (keys.next()) {
554                         blobid = keys.getInt(1);
555                         if (guid == null || guid.length() == 0) {
556                             guid = guidFinder.findGUID("blobs", blobid);
557                         }
558                         PreparedStatement insert = connection.prepareStatement(
559                                 "INSERT INTO repligard"
560                                 + " (realm, id, guid, sitegroup)"
561                                 + " VALUES (?, ?, ?, ?)");
562                         insert.setString(1, "blobs");
563                         insert.setInt(2, blobid);
564                         insert.setString(3, guid);
565                         insert.setInt(4, sitegroup);
566                         insert.executeUpdate();
567                         insert.close();
568                     }
569                 } finally {
570                     keys.close();
571                 }
572             }
573             ps.close();
574             deletes.remove(guid);
575 
576             File file = new File(filepath, location);
577             file.getParentFile().mkdirs();
578             InputStream input = state.getAttachment(hash);
579             try {
580                 OutputStream output = new FileOutputStream(file); 
581                 try {
582                     byte[] buffer = new byte[4096];
583                     int n = input.read(buffer);
584                     while (n != -1) {
585                         output.write(buffer, 0, n);
586                         n = input.read(buffer);
587                     }
588                 } finally {
589                     output.close();
590                 }
591             } finally {
592                 input.close();
593             }
594 
595             importParameters("blobs", blobid);
596         }
597     }
598 
599 }