001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.directory.server.core.journal; 021 022 023import java.io.File; 024import java.io.IOException; 025import java.io.PrintWriter; 026import java.io.Writer; 027import java.nio.charset.StandardCharsets; 028import java.nio.file.Files; 029import java.nio.file.StandardOpenOption; 030 031import org.apache.directory.api.ldap.model.exception.LdapException; 032import org.apache.directory.api.ldap.model.ldif.LdifEntry; 033import org.apache.directory.api.ldap.model.ldif.LdifUtils; 034import org.apache.directory.server.core.api.DirectoryService; 035import org.apache.directory.server.core.api.LdapPrincipal; 036import org.apache.directory.server.core.api.journal.JournalStore; 037 038 039/** 040 * The default Journal Store implementation. It creates a file on disk in which 041 * the logs will be appended. 042 * 043 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 044*/ 045public class DefaultJournalStore implements JournalStore 046{ 047 /** The directory where the journal is stored */ 048 private File workingDirectory; 049 050 /** The journal file name */ 051 private String fileName; 052 053 /** The file containing the journal */ 054 private File journal; 055 056 /** The stream used to write data into the journal */ 057 private Writer writer; 058 059 060 /** 061 * {@inheritDoc} 062 */ 063 @Override 064 public void destroy() throws IOException 065 { 066 if ( writer != null ) 067 { 068 writer.close(); 069 } 070 } 071 072 073 /** 074 * Initialize the interceptor 075 */ 076 @Override 077 public void init( DirectoryService service ) throws IOException 078 { 079 if ( workingDirectory == null ) 080 { 081 workingDirectory = service.getInstanceLayout().getLogDirectory(); 082 } 083 084 /** Load or create the journal file */ 085 if ( fileName == null ) 086 { 087 fileName = "journal.ldif"; 088 } 089 090 journal = new File( workingDirectory, fileName ); 091 092 // The new requests are added at the end of the existing journal 093 writer = new PrintWriter( 094 Files.newBufferedWriter( journal.toPath(), StandardCharsets.UTF_8, StandardOpenOption.APPEND ) ); 095 } 096 097 098 /** 099 * Stores an event into the journal. 100 * 101 * @param principal The principal who is logging the change 102 * @param revision The operation revision 103 * @param forward The change to log 104 */ 105 @Override 106 public boolean log( LdapPrincipal principal, long revision, LdifEntry forward ) 107 { 108 synchronized ( writer ) 109 { 110 try 111 { 112 // Write the LdapPrincipal 113 writer.write( "# principal: " ); 114 writer.write( principal.getName() ); 115 writer.write( '\n' ); 116 117 // Write the timestamp 118 writer.write( "# timestamp: " ); 119 writer.write( Long.toString( System.currentTimeMillis() ) ); 120 writer.write( '\n' ); 121 122 // Write the revision 123 writer.write( "# revision: " ); 124 writer.write( Long.toString( revision ) ); 125 writer.write( "\n" ); 126 127 // Write the entry 128 writer.write( LdifUtils.convertToLdif( forward, 80 ) ); 129 writer.flush(); 130 } 131 catch ( LdapException | IOException e ) 132 { 133 return false; 134 } 135 } 136 137 return true; 138 } 139 140 141 /** 142 * Records a ack for a change 143 * 144 * @param revision The change revision which is acked 145 * @return <code>true</code> if the ack has been written 146 */ 147 @Override 148 public boolean ack( long revision ) 149 { 150 synchronized ( writer ) 151 { 152 try 153 { 154 // Write the revision 155 writer.write( "# ack-revision: " ); 156 writer.write( Long.toString( revision ) ); 157 writer.write( "\n\n" ); 158 159 writer.flush(); 160 } 161 catch ( IOException ioe ) 162 { 163 return false; 164 } 165 } 166 167 return true; 168 } 169 170 171 /** 172 * Records a nack for a change 173 * 174 * @param revision The change revision which is nacked 175 * @return <code>true</code> if the nack has been written 176 */ 177 @Override 178 public boolean nack( long revision ) 179 { 180 synchronized ( writer ) 181 { 182 try 183 { 184 // Write the revision 185 writer.write( "# nack-revision: " ); 186 writer.write( Long.toString( revision ) ); 187 writer.write( "\n\n" ); 188 189 writer.flush(); 190 } 191 catch ( IOException ioe ) 192 { 193 return false; 194 } 195 } 196 197 return true; 198 } 199 200 201 @Override 202 public void sync() throws IOException 203 { 204 // TODO Auto-generated method stub 205 206 } 207 208 209 @Override 210 public long getCurrentRevision() 211 { 212 // TODO Auto-generated method stub 213 return 0; 214 } 215 216 217 /** 218 * @return the fileName 219 */ 220 public String getFileName() 221 { 222 return fileName; 223 } 224 225 226 /** 227 * @param fileName the fileName to set 228 */ 229 @Override 230 public void setFileName( String fileName ) 231 { 232 this.fileName = fileName; 233 } 234 235 236 /** 237 * {@inheritDoc} 238 */ 239 @Override 240 public void setWorkingDirectory( String workingDirectoryName ) 241 { 242 this.workingDirectory = new File( workingDirectoryName ); 243 } 244}